Camunda 7 滚动更新(Rolling Update)测试套件深度解析:跨版本数据库兼容性验证的完整方案
【免费下载链接】camunda-bpm-platformCamunda 7 CE is End of Life (EoL). Please check out Camunda 8 instead (https://github.com/camunda/camunda) or read about Camunda 7 Enterprise End of Life (https://camunda.com/blog/2025/02/camunda-7-enterprise-end-of-life-extension/) – Camunda 7 CE was a flexible framework for workflow and decision automation using BPMN and DMN.项目地址: https://gitcode.com/GitHub_Trending/ca/camunda-bpm-platform
导读
本文以 qa/test-db-rolling-update/README.md 为主线,深入剖析 Camunda BPM Platform 中用于验证跨小版本(minor version)数据库兼容性的滚动更新测试套件。该套件模拟真实生产环境中最常见的升级路径——旧版本引擎进程实例存活期间,数据库 schema 被新版本升级——并验证旧引擎操作升级后数据库时的正确性。读完本文,你将掌握该套件的五步工作流、四个 Maven 模块的职责划分、全部测试场景的覆盖范围,以及如何使用mvn/mvnw在任意支持的数据库上运行这套测试。
一、为什么需要滚动更新测试:场景与意义
Camunda 7 引擎的版本演进遵循"滚动升级"(rolling upgrade)原则:在生产环境中,旧版本($PREVIOUS)引擎和新版本($CURRENT)引擎会在同一套数据库上共存一段时间。旧引擎进程实例尚未结束,而数据库 schema 可能已经被新引擎的升级脚本修改。
这意味着,数据库需要同时满足两个方向的要求:
- 前向兼容(forward compatible):新版本引擎可以读取旧版本引擎写入的数据(通过
$PREVIOUS→$CURRENT的 schema 升级脚本实现); - 后向兼容(backward compatible):旧版本引擎在升级后的 schema 上仍然可以继续运行、完成任务、查询数据。
滚动更新测试套件正是为此设计:它刻意让旧引擎在升级后的数据库上执行测试,从而验证后向兼容性——这是普通单元测试和集成测试无法覆盖的盲区。
从源码结构看,该套件由四个 Maven 模块组成(定义于 qa/test-db-rolling-update/pom.xml 的<modules>中),按执行顺序依次是:
| 模块 | 职责 |
|---|---|
rolling-update-util | 共享的测试场景定义与工具类(TestFixture、场景类、常量) |
create-old-engine | 用旧版本引擎创建数据(schema + 流程实例) |
create-new-engine | 升级 schema,并用新版本引擎补充数据 |
test-old-engine | 用旧版本引擎执行全部验证测试 |
二、五步工作流:从旧引擎建库到旧引擎验证
README 用一个精炼的五步流程概括了整套机制($CURRENT指当前小版本,$PREVIOUS指$CURRENT - 1):
- Create DB schema for
$PREVIOUS:为旧版本创建完整的数据库 schema; - Execute test setup with
$PREVIOUSengine:使用旧版本引擎执行测试场景设置(部署流程、启动流程实例、写入历史数据等); - Patch DB schema to
$CURRENT:将数据库 schema 升级(patch)到当前版本; - Execute test setup with
$CURRENTengine:使用当前版本引擎执行补充的场景设置; - Run tests with
$PREVIOUSengine:最终用旧版本引擎运行全部测试,验证其对升级后数据库的兼容性。
其中步骤 3 与步骤 4 的顺序至关重要:schema 先升级、再由新引擎写入"新版本特性"数据,最终旧引擎必须能够正确读取和处理这些数据——这正是滚动升级场景下最易出问题的环节。
源码印证:新旧引擎入口类
步骤 2 与步骤 4 分别由两个main类驱动,它们都委托给共享的 TestFixture:
- CreateOldEngineMain.java:调用
TestFixture.main(new String[]{RollingUpdateConstants.OLD_ENGINE_TAG}); - TestNewEngineMain.java:调用
TestFixture.main(new String[]{RollingUpdateConstants.NEW_ENGINE_TAG})。
新旧引擎通过 tag 区分,常量定义在 RollingUpdateConstants.java:OLD_ENGINE_TAG = "OLD",NEW_ENGINE_TAG = "CURRENT"。tag 会被拼入场景名称(如OLD.ProcessWithUserTaskScenario.xxx),供测试阶段按 tag 检索对应场景创建的数据。
三、场景设置(TestFixture):两个引擎版本各写什么数据
TestFixture.main() 展示了数据准备的完整逻辑:
- 从
camunda.cfg.xml构建 ProcessEngine; - 创建
ScenarioRunner并注册通用场景(新旧引擎都会执行); - 仅当 tag 为
CURRENT(新引擎)时,额外注册三个"新版本专属"场景; - 关闭引擎后,再从
camunda.auth.cfg.xml构建第二个引擎,注册授权(authorization)场景。
通用场景(新旧引擎均执行)
ScenarioRunner.setupScenarios()依次注册了以下场景类(均在rolling-update-util的scenarios包下):
- ProcessWithUserTaskScenario:含用户任务的流程;
- ProcessWithAsyncServiceTaskScenario:异步服务任务;
- ProcessWithUserTaskAndTimerScenario:用户任务 + 定时器;
- DeploymentWhichShouldBeDeletedScenario:验证删除部署;
- ProcessWithParallelGatewayScenario 与 ProcessWithParallelGatewayAndServiceTaskScenario:并行网关相关;
- ProcessWithCallActivityScenario:调用活动(Call Activity);
- ProcessWithMultiInstanceCallActivityScenario:多实例调用活动(注意源码包名中
mulltiInstance为原样保留的拼写); - ProcessWithExternalTaskScenario:外部任务;
- ProcessWithEventSubProcessScenario:事件子流程;
- JobTimestampsUpdateScenario 与 IncidentTimestampUpdateScenario:Job 与 Incident 时间戳更新。
新引擎专属场景(仅CURRENTtag 执行)
if (RollingUpdateConstants.NEW_ENGINE_TAG.equals(currentFixtureTag)) { runner.setupScenarios(HistoryCleanupScenario.class); runner.setupScenarios(EmptyStringVariableScenario.class); runner.setupScenarios(SetRemovalTimeToProcessInstanceScenario.class); }- HistoryCleanupScenario:历史数据清理;
- EmptyStringVariableScenario:空字符串变量;
- SetRemovalTimeToProcessInstanceScenario:批量设置移除时间(Batch 操作)。
此外,第二个引擎(camunda.auth.cfg.xml)上注册了 AuthorizationScenario,覆盖授权(权限)数据的兼容性。
ScenarioRunner 的底层机制
场景的注册与执行依赖 qa/test-db-util 中的 ScenarioRunner:它通过反射扫描场景类中带@Deployment注解的方法完成流程部署(支持 classpath 资源与 BpmnModelInstance 两种方式),再扫描带@DescribesScenario注解的方法收集场景定义,最后按依赖关系(@ExtendsScenario)创建实例。场景名称统一为{tag}.{ClassName}.{scenarioName}格式,测试阶段依据该命名从数据库中精确检索。
四、Schema 升级:patch 与 upgrade 脚本的执行顺序
步骤 3 的实现位于 create-new-engine/pom.xml 的rolling-updateprofile 中,核心是sql-maven-plugin在test-compile阶段按固定顺序执行的三个 SQL 批次:
- patch-previous-schema:应用旧版本(
$PREVIOUS)的累积 patch 脚本:${database.type}_engine_${previous.minor.version}_patch.sql${database.type}_identity_${previous.minor.version}_patch.sql
- upgrade-db:执行从旧版本到当前版本的小版本升级脚本:
${database.type}_engine_${previous.minor.version}_to_${current.minor.version}.sql${database.type}_identity_${previous.minor.version}_to_${current.minor.version}.sql
- patch-current-schema:显式应用当前版本(
$CURRENT)的后续 patch(该执行默认留空,源码注释中给出了形如7.24.0_to_7.24.1.sql的扩展示例)。
SQL 脚本本体并非手工复制,而是通过maven-dependency-plugin从org.camunda.bpm.distro:camunda-sql-scripts的 test-jar 中解压到target/scripts-current目录。同时maven-antrun-plugin会"兜底"创建脚本占位文件——因为某个版本可能恰好没有数据库变更(对应_patch.sql或_to_.sql文件不存在于发行包中的情况),保证构建不因文件缺失而失败。
版本号解析由build-helper-maven-plugin完成:create-new-engine/pom.xml中定义camunda.version.current=${project.version}(当前仓库为 7.24.0-SNAPSHOT)、camunda.version.previous=7.23.0,解析出的camunda.current.majorVersion/minorVersion与camunda.previous.majorVersion/minorVersion用于动态拼接上述 SQL 文件名。
五、执行测试:Maven 命令与数据库选择
README 给出的执行方式如下。
标准 Maven 命令
在项目根目录执行:
mvn clean install -Prolling-update,${DATABASE}其中:
rolling-updateprofile 是套件的核心激活开关——只有激活它,四个模块中与滚动更新相关的插件执行和测试才会运行;${DATABASE}是目标数据库标识符,需要替换为实际值。
Maven Wrapper 命令
使用仓库自带的mvnw从项目根目录执行:
./mvnw clean install -f qa/test-db-rolling-update/pom.xml -Prolling-update,${database-id}-f qa/test-db-rolling-update/pom.xml:显式指定套件自身的聚合 POM;${database-id}示例值为h2(内置数据库,无需外部依赖即可运行)。
数据库标识符的作用
${database-id}/${database.type}贯穿整个构建:它既决定了camunda-sql-scripts中解压的 SQL 脚本文件(${database.type}_engine_*.sql、${database.type}_identity_*.sql)与drop脚本,也决定了测试连接的 JDBC 数据源(通过各模块资源目录中的配置文件)。Camunda 7 官方支持的数据库(如 h2、MySQL/MariaDB、PostgreSQL、Oracle、SQL Server 等)均可作为该参数取值,实际可用集合以 qa 目录 及camunda-sql-scripts发行包中的脚本清单为准。
构建阶段与插件的完整编排
各模块在rolling-updateprofile 下的执行时机汇总如下:
| Maven 阶段 | create-old-engine | create-new-engine | test-old-engine |
|---|---|---|---|
generate-test-sources | — | antrun 创建脚本占位、dependency 解压 SQL、build-helper 解析版本 | — |
test-compile | — | sql-maven-plugin 依次执行 patch-previous → upgrade-db → patch-current | — |
process-test-classes | exec 运行CreateOldEngineMain | exec 运行TestNewEngineMain | — |
test/ 集成测试 | — | — | surefire 运行全部测试用例 |
post-integration-test | — | — | sql-maven-plugin 执行 drop 脚本清理数据库 |
可以看到,create-old-engine与create-new-engine通过 Maven 生命周期天然形成先后顺序,而test-old-engine的默认distroprofile 通过<skipTests>true</skipTests>跳过测试,只有在rolling-updateprofile 激活时才真正执行测试——这保证日常构建不会误跑需要特殊数据库编排的滚动更新用例。
六、测试用例:参数化双引擎验证
测试代码位于 test-old-engine 的测试目录,核心基类是 AbstractRollingUpdateTestCase.java:
@RunWith(Parameterized.class) public abstract class AbstractRollingUpdateTestCase { @Parameters(name = "{0} engine") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { {RollingUpdateConstants.OLD_ENGINE_TAG}, {RollingUpdateConstants.NEW_ENGINE_TAG} }); } @Parameter public String tag; @Rule public UpgradeTestRule rule = new UpgradeTestRule(); @Before public void init() { rule.setTag(tag); } }关键设计:
- 参数化运行:每个测试类都会以
OLD和CURRENT两个 tag 分别执行一遍(报告显示为 "OLD engine" / "CURRENT engine"); - tag 驱动数据检索:
UpgradeTestRule依据当前 tag 定位对应引擎版本创建的场景数据,因此同一份测试代码既验证旧引擎在升级后数据库上的行为(OLDtag,本文核心目标),也验证新引擎行为(CURRENTtag,作为对照)。
测试用例全景
继承该基类的测试覆盖了与场景一一对应的验证点(目录与场景包结构对应):
| 类别 | 测试类 |
|---|---|
| 用户任务 / 任务流转 | CompleteProcessWithUserTaskTest、CompleteProcessWithAsyncServiceTaskTest、CompleteProcessWithUserTaskAndTimerTest |
| 网关 / 调用活动 | CompleteProcessWithParallelGatewayTest、CompleteProcessWithParallelGatewayAndServiceTaskTest、CompleteProcessWithCallActivityTest、CompleteProcessWithMultiInstanceCallActivityTest |
| 外部任务 / 事件子流程 | CompleteProcessWithExternalTaskTest、CompleteProcessWithEventSubProcessTest |
| 时间戳 / 历史清理 / 批量 | IncidentTimestampUpdateTest、JobTimestampsUpdateTest、HistoryCleanupTest、SetRemovalTimeToProcessInstanceTest |
| 变量 / 部署 / 授权 | EmptyStringVariableTest、DeleteDeploymentTest、AuthorizationTest |
其中时间戳类测试继承 AbstractTimestampUpdateTest,专门校验升级过程中 Job 与 Incident 表时间戳字段的迁移正确性——这是 schema 升级中最容易因类型转换或默认值变化而出错的细节。
环境清理
test-old-engine的rolling-updateprofile 在post-integration-test阶段通过sql-maven-plugin执行camunda-sql-scripts中的 drop 脚本(${database.type}_engine_${project.version}.sql与_identity_对应脚本)清理数据库,保证重复运行不会受残留数据影响;同时redirectTestOutputToFile将测试输出重定向到文件,便于在 CI 中归档排查。
七、套件的适用前提与限制
基于仓库现状,使用该套件时需注意以下几点:
- 版本配对是硬编码的:
create-old-engine与test-old-engine通过camunda-bom锁定7.23.0,create-new-engine使用camunda.version.previous=7.23.0、current 为构建版本7.24.0-SNAPSHOT。因此当前仓库中"旧引擎"固定为 7.23.0、"新引擎"固定为 7.24.x,验证的是这两个相邻小版本之间的滚动兼容性; - 依赖 camunda-sql-scripts 发行包:SQL 升级/清理脚本从
org.camunda.bpm.distro:camunda-sql-scripts的 test-jar 解压而来,执行前需确保该构件可被 Maven 解析; - 社区版生命周期说明:根据 qa/test-db-rolling-update/pom.xml 中的描述,7.24.0 是 Camunda 7 社区版在 Maven Central 发布的最后一个版本,该套件不再随社区版发布新版本(扩展维护需转向企业版,仓库中亦注明 Camunda 7 CE 已进入 End of Life 阶段,新项目建议评估 Camunda 8)。
结语
滚动更新测试套件是 Camunda 7 保障升级安全性的关键基础设施:通过"旧引擎建库 → 新引擎升级 schema → 旧引擎回读验证"的闭环,它以最小成本捕获了跨版本数据库兼容性问题。理解其模块编排、tag 机制与 SQL 脚本执行顺序,不仅有助于你在本地复现升级验证(./mvnw clean install -f qa/test-db-rolling-update/pom.xml -Prolling-update,h2),也能为自研工作流引擎的升级测试体系设计提供可借鉴的范式。
【免费下载链接】camunda-bpm-platformCamunda 7 CE is End of Life (EoL). Please check out Camunda 8 instead (https://github.com/camunda/camunda) or read about Camunda 7 Enterprise End of Life (https://camunda.com/blog/2025/02/camunda-7-enterprise-end-of-life-extension/) – Camunda 7 CE was a flexible framework for workflow and decision automation using BPMN and DMN.项目地址: https://gitcode.com/GitHub_Trending/ca/camunda-bpm-platform
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考