azure-service-fabricservice-fabric-statefulservice-fabric-actor

Service Fabric: Move an enum class to different project


Recently to resolve circular dependency we need to move an enum class to a different project under a different namespace. There are some actor and stateful services which keep the instance of this enum value in their reliable states.

Enum class is something like this:

namespace com.libA
{
    public enum Foo
    {
        None = 0,
        Foo1 = 1,
        Foo2 = 2,
    }
}

We want to move this to another project with the namespace as com.libB. These enum values are stored in reliable states inside actor and stateful services and are fetched like this:

Foo foo = await this.StateManager.GetStateAsync<Foo>("FooKey").ConfigureAwait(false);

One of the actor service which stores the value of Foo is a very long-lived actor. It can theoretically live till infinity in happy paths and if delete is never invoked from outside. We tried simple refactor > Move and tried on our non-prod environments. This started causing SerializationException in our non-prod environments. The error message says: Expecting element 'Foo' from namespace 'http://schemas.datacontract.org/2004/07/com.libB'.. Encountered 'Element' with name 'Foo', namespace 'http://schemas.datacontract.org/2004/07/com.libA'.

These exceptions are coming just before fetching the value of Foo in older actors.

My question is:

  1. How can we move Foo to namespace com.libB? Will two-phase upgrade help here?
  2. Is it even possible to do so without data loss/corruption?

Solution

  • You can add the DataContract attribute with a namespace to your type. Since you are already running in your production environment you could use the namespace from the error to work arround the problem.

    Example:

    [DataContract(Name = "Foo", Namespace = "http://schemas.datacontract.org/2004/07/com.libA")]
    public enum Foo
    {
       // ...
    }
    

    A better approach might be to have an upgrade plan.

    1. Let the 2 types coexist
    2. When retrieving the state, have a try-mechanism, retrieve with old type, if this fails with the serialization exception try with the new type.
    3. When persisting the state, convert it to the new type in the new namespace, when it's the old type. (Add some logging so you can verify the conversion happend)
    4. Deploy to test, see if it work, if ok deploy to production
    5. Remove the old type, remove the conversion code
    6. Deploy to test, see it it works, if ok deploy to production.