Service Fabric:将枚举 class 移动到不同的项目

Service Fabric: Move an enum class to different project

最近为了解决循环依赖,我们需要将枚举 class 移动到不同命名空间下的不同项目。有一些参与者和有状态服务将此枚举值的实例保持在其可靠状态。

枚举 class 是这样的:

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

我们想将其移动到命名空间为 com.libB 的另一个项目。这些枚举值存储在 actor 和有状态服务中的可靠状态中,并像这样获取:

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

其中一个存储 Foo 值的 actor 服务是一个非常长寿的 actor。如果 delete 从未从外部调用,理论上它可以在快乐路径中一直存在到无穷大。我们尝试了简单的重构 > 移动并尝试了我们的非产品环境。这开始在我们的非产品环境中导致 SerializationException。错误消息说: 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'.

这些异常发生在获取旧 actor 中 Foo 的值之前。

我的问题是:

  1. 我们如何将 Foo 移动到命名空间 com.libB?两阶段升级对此有帮助吗?
  2. 是否可以在没有数据的情况下这样做 loss/corruption?

您可以将带有命名空间的 DataContract 属性添加到您的类型中。 由于您已经 运行 在您的生产环境中,您可以使用错误中的命名空间来解决问题。

示例:

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

更好的方法可能是制定升级计划。

  1. 让2种共存
  2. 检索状态时,有一个 try-mechanism,使用旧类型检索,如果失败并出现序列化异常,请尝试使用新类型。
  3. 持久化状态时,将其转换为新命名空间中的新类型,当它是旧类型时。 (添加一些日志记录,以便您可以验证转换是否发生)
  4. 部署测试,看看它是否工作,如果可以部署到生产环境
  5. 移除旧类型,移除转换代码
  6. 部署测试,看看它是否有效,如果没问题就部署到生产环境。

一个选项是为所有使用 Foo 的类型创建一个 custom serializer wrapping DataContractSerializer,在反序列化期间修复/忽略命名空间。

IReliableStateManager.TryAddStateSerializer is used to register a custom serializer for the given type T. This registration should happen in the construction of the StatefulServiceBase to ensure that before recovery starts, all Reliable Collections have access to the relevant serializer to read their persisted data.

  • IStateSerializer<OrderKey>.Read(BinaryReader reader)中读取序列化数据为XML
  • 根据需要更改 XML 命名空间
  • 将 XML 提供给 DataContractSerializer 以创建一个对象
  • Return 对象