升级部署
服务通过滚动更新升级:新节点在旧节点旁边启动,分区移到它们上面,然后旧节点停止。有一段时间,两个版本同时运行,面对同一个数据库和同样的主题。本页说明是什么让这件事保持安全,无论是升级 Wow 还是升级你自己的代码。
两个版本并行
在同一主版本内,Wow 相邻的次版本可以在一个服务内、也可以跨服务并行运行。它们的内部协议(集群通道、等待信号、线上形式)以及存储格式保持兼容:
- 存储格式按结构而不是按行做版本:每张日志、快照和定时器表都在
wow_format中记录它的格式。在同一主版本内格式不变,读者忽略它们不认识的东西。同一格式内新增的列或字段,在今天会这样做的存储里就地加上:ClickHouse 的_state(状态 schema 变化时)和 Elasticsearch 的映射(后来映射的字段)。 - 线上形式(Kafka 信封、命令、回执、等待信号)也带着它们的格式。新的可选字段先由一个次版本开始读取,下一个次版本才开始写入。
- 枚举的新值(例如结果的状态、跳的阶段、Manifest 的
creation)遵循同样的顺序:先读者,后写者。比写者旧的读者会宽容地读取它不认识的值:Wow 的客户端和节点把未知的状态、阶段或函数种类读作UNKNOWN,Manifest 的读者略去未知值所属的部分(未知creation的命令、未知ownership的聚合、未知种类或 start 的订阅),把错误中未知的retry读作 none,并记录日志。服务端仍会拒绝请求中的未知值。 - 改变自身某部分含义的 Manifest 在
requires中列出一个标记。不理解该标记的读者会拒绝这份 Manifest,而不是误读它。 - 不兼容的改变要等下一个主版本,由它的迁移来处理。
所以一次只升一个次版本,并让每次发布完成之后再开始下一次。
你的代码:事件
事件的类在你服务的两个版本之间可能改变,而发布期间两个版本都会读取日志:
- 新增一个带默认值的可选字段,什么都不需要。
- 其他任何改变都要提高事件的修订号,并提供一个从旧修订号开始的升级函数(演进事件)。先部署读取的一方:每个节点都能读修订号
n + 1之后,再部署写入它的代码。否则旧节点可能读到一条它没有升级函数的记录。 - 另一个服务中的消费者,在它的 api 升级之前,会把新修订号读成
PublishedEvent,也不会为它调用接收这个事件类的处理函数(发布与消费事件流)。
你的代码:状态
状态结构的改变要提高 @AggregateRoot(stateRevision = n)。发布期间,新节点以新修订号声明快照表并原地重建它,而旧节点继续以旧修订号写入;存储会阻止旧节点覆盖已重建的快照。回滚时,只有在不再剩下任何更高修订号的节点之后,才会把声明降回去(重建与重放)。
数仓的展平视图遵循同样的规则:写入者只会用更新的定义替换它,所以重启的旧节点不会撤销新节点的视图(ClickHouse)。
你的代码:契约
服务暴露的命令、事件、状态和错误,是它与客户端和对端之间的契约。Gradle 插件把每次构建的 Manifest 与提交的基线比较,会让破坏客户端的改变失败:删除命令或事件、新增必填字段、删除字段或改变字段类型。用 updateWowManifestBaseline 记录有意的改变,并与代码一起评审(测试服务)。在基线的兼容线(服务的主版本号)之内,更新会拒绝破坏性改变:它需要服务的下一个主版本(同时记录这次破坏和新的兼容线),或者在旧命令旁边新增一条命令。
发布期间的订阅
分区连同它的检查点移到新节点上,新节点的订阅从那里继续。新版本中新增的处理函数按它的种类开始(订阅):投影从开头开始;读取自己上下文日志的反应器或 Saga 从末尾开始;反应器或 Saga 读取另一个上下文事件流的处理函数,从它的服务加入这条流的地方开始,服务里还没有别的订阅读过它时从开头开始,否则从末尾开始。@OnEvent(start = ...) 或 wow.subscriptions.[<context>.<processor.function>].start 可以覆盖它。
订阅的名字就是它的身份,所以上线前要检查改名:
- 改了名的处理器或处理函数是一个新订阅。 它按同样的规则开始,就像新的一样:Saga 或反应器从末尾开始,跳过旧名字还没处理到的内容;投影从开头开始,重建它的读模型。旧名字的检查点和停放条目留在原处,不再有人读取。
- 要保留位置,就钉住旧名字:在改了名的类或函数上用
@Name。 - 新节点在首次启动时警告已没有订阅的名字下的检查点;
GET /wow/ops/subscriptions会列出它们(ops 服务与控制台)。 - 换一个名字是唯一的回卷:检查点从不往回设。用新名字和更早的
start部署处理函数,就是再读一次流的办法。
不能改变的设置
wow.log.partitions 在日志有了条目之后就固定了,一个服务的节点共用它们的存储布局(配置服务)。
主版本
新的主版本可能改变格式和协议。它的节点不会与上一个主版本的节点并行运行:它附带一次迁移,在切换之前或切换期间运行,就像 V10 从 9.x 迁移那样(从 9.x 迁移)。
从 10.0.0 预发布版升级
10.0.0 的预发布版(alpha、beta、rc)对它存储的数据与它的 API 都不做兼容承诺。它的存储不会带过来:请在空的存储上启动本版本。从 9.x 迁移的服务请按 从 9.x 迁移 进行。