数据库与消息后端适配
# 数据库与消息后端适配
版本 v1.3,2026-10-23。返回总览。全部列出的后端均为目标支持范围,M0 已实现 MySQL/PostgreSQL 连接与迁移,并完成专用测试库 PoC;尚未完成平台全模块数据库认证。其他目标状态见 M0 实施。
# 1. 数据库支持合同
“支持一种数据库”应包含:锁定版本与驱动、建库/升级脚本、事务和锁行为、核心模块回归、租户隔离、连接池、备份恢复说明。仅能建立连接或 ORM 声明支持,不算平台支持。
默认一套部署选择一种业务数据库,模块共享其事务能力;不同租户独立库、跨库 JOIN、多数据源分布式事务不作为默认能力。架构保留 TenantDatabaseResolver 端口,今后采用独立库时另行验证连接池和迁移调度。
| 业务数据库 | 拟接入路径 | 认证重点 | 建议批次 |
|---|---|---|---|
| MySQL | GORM MySQL dialect | 事务、字符排序、分页、时间精度、死锁重试 | M0 起主线 |
| PostgreSQL | GORM PostgreSQL dialect | identity/sequence、引号、RETURNING、布尔/JSON、隔离级别 | M0 PoC,R1 核心回归 |
| MariaDB | MySQL 协议驱动,独立 dialect 配置 | DDL、版本探测、索引和锁差异;不能只当 MySQL 别名 | R4 |
| TiDB | MySQL 协议驱动,独立数据库 profile | 分布式事务、锁/重试、ID 分配、DDL、执行计划 | R4 |
| SQL Server | GORM SQL Server dialect | IDENTITY、分页、锁提示、死锁、NVARCHAR、时间和标识符 | M0 PoC,R4 完整认证 |
| Oracle | 评估 GORM 文档指向的 Oracle driver 或经认证替代实现 | Instant Client/CGO、序列、空串视为 NULL、分页、DDL 提交、CLOB | M0 PoC,R4 完整认证 |
| 达梦 DM | 安装包 Go driver + 官方 GORM V2 方言;fago_dm 构建目标装配 fago-dm,与 fago-cli migrate 嵌入 dm v1–v13 方言 SQL | 开发实例已核对 M0 行为 PoC、v13 迁移升级/脏状态/会话锁、GORM 唯一键 -6625 映射;专用非管理员 schema 上 31 项完整业务 race 回归、逐版本迁移、真实 CLI/API 验证通过,生产认证仍属 R4 | 开发可运行业务库,R4 完整认证 |
GORM 当前文档列出多种数据库及 Oracle 接入;达梦提供独立的 Go/GORM 方言说明。DM8 开发实例与 Go 驱动 8.1.3.62、安装包方言已有行为 PoC,其他版本组合以及 GORM 的全部扩展仍要单独验证,不能从这一次结果推断都可移植。GORM 接入说明 (opens new window)、DM GORM 说明 (opens new window)
# 达梦业务接入验收(2026-10-23)
达梦已接入当前业务运行链路。fago/fago-migrations/dm/ 显式维护 v1–v13 SQL,fago_dm 构建只嵌入这一套脚本与驱动;构建目标不符会在取连接前拒绝。命令见 数据库迁移。不需要发布另一个数据库服务或复制其他方言依赖。
本轮使用 DM8 单实例、安装包 Go driver 8.1.3.62(2023.09.20)、Darwin arm64 客户端;参数为 COMPATIBLE_MODE=0、GLOBAL_CHARSET=1、GLOBAL_STR_CASE_SENSITIVE=0、LENGTH_IN_CHAR=1。应用使用随机非管理员所有者,仅授予 RESOURCE,未为业务账号提升系统目录权限。所有测试只操作独立测试 schema,完成后删除。
- 31 个顶层完整业务集成测试、44 个子测试以
-race通过,覆盖身份、Cookie、权限、组织、审计、租户/套餐/配额/到期隔离、加密 HTTP 及示例 widgets。 - 迁移和代码生成共 4 个顶层测试、18 个子测试通过:空库重复运行、v1–v12 带数据升级到 v13、凭证/会话/部门/权限保留、dirty/校验和/缺号/未来版本拒绝,DDL 后迁移锁仍有效。
- 实际生成的 CLI 完成 migrate、两种 realm 的 bootstrap;API 进程完成 ready、登录、改密后重登、me、平台租户查询、widgets 增改删及旧版本冲突拒绝。
- 默认 workspace 的 18 个模块 race/vet 和新缓存
GOWORK=off独立测试通过。MySQL/PostgreSQL 的迁移与共享认证回归通过;三种构建目标均验证了依赖和 SQL 裁剪,其他目标产物只在临时目录验证。
适配层将 RR 请求加强为 SERIALIZABLE,避免默认兼容模式拒绝 RR 或弱化快照;修复原生只读标志复用,恢复失败丢弃连接。每条物理连接独占连接器,原生握手及全局初始化串行,业务查询保持并发;供应商自由文本日志关闭,统一使用项目脱敏出口。组织表采用固定序列以保留自引用 CHECK,普通表保留 IDENTITY。迁移持有同一连接的会话锁,隐式提交后失败保留 dirty,不自动重放部分 DDL。
本轮脱敏验收记录 记录命令、版本组合、脚本与产物 SHA-256。此结论不扩展到其他 DM 版本、OS、集群/MPP、任意 upsert 或完整 GORM AutoMigrate;生产备份恢复、容量及发布认证仍属 R4。现有工作流用例保持 M0 有界 PoC 定位。
# 2. 持久化抽象
业务 service 依赖 repository 与 UnitOfWork;repository 使用统一的 TenantScope/DataScope 和参数化 SQL。方言模块封装:
- Open/health/pool 配置、错误归一、事务隔离和可重试错误分类。
- ID 生成/取回、批量插入、分页、大小写和标识符引用。
- 锁定记录、任务租约抢占、乐观版本更新和 upsert 等差异。
- 字符串/布尔/时间/JSON/大对象类型映射,迁移与种子写入。
- 租户和数据范围的谓词构造,包含 JOIN、子查询、批量和 count。
主键逻辑类型选有符号正 int64,API 仍用字符串;各库映射 BIGINT/NUMBER(19)/对应整型,通过 identity 或 sequence 生成。禁止在跨库逻辑模型使用 MySQL 专有 UNSIGNED、bit(1)、datetime on update、反引号或 JSON 查询操作符。
结构化扩展数据使用版本化 JSON 字符串,由 adapter 映射原生 JSON/CLOB/TEXT;R1 不把数据库专有 JSON 路径查询作为必需条件。时间逻辑统一 UTC,物理类型由方言映射。Oracle 空字符串与 NULL 的差异需在 DTO/repository 层归一;必填 code 不允许空字符串。
逻辑软删除标记和唯一性保留,外键、索引命名采用跨库安全长度和白名单命名规则。厂商兼容模式是测试环境配置的一部分,不依赖运维临时改模式解决错误。仅对明确幂等的完整事务做有界重试,不重试夹杂外部副作用的事务。
“透明租户”封装将受信上下文转换为 repository 默认范围,底层缺上下文即拒绝访问。受控全局 repository 显式声明全局表;raw SQL 需通过相同查询构造器或手工审核并跑隔离套件,不能宣称 ORM 自动改写任意 SQL。
# 3. 迁移与测试矩阵
迁移目录按数据库分别维护,共享逻辑版本号和变更说明。构建时只嵌入选定方言 SQL,Go migrate 命令校验运行 driver 与构建目标,再使用对应 runner/锁;Oracle、DM 的 runner、DDL 事务边界和失败恢复在 M0 PoC 明确。不会把 MySQL DDL 自动字符串替换后直接当生产脚本。
每日主线验证 MySQL/PostgreSQL;SQL Server 和可获取的商业数据库进入夜间或发布认证流水线。没有 Oracle/DM 合法测试实例时,状态记为“等待环境,未认证”,不能在支持清单标记通过。
每个数据库 profile 都执行:空库初始化、升级、主键/唯一/软删、并发更新、最后管理员锁定、token 轮换、租户关联、分页/count、一致时间、Unicode、任务抢占/outbox、故障重连、死锁及必要基准。关键测试与数据库无关,差异行为另有 adapter 测试。
支持清单记录 databaseVersion、driverVersion、GoVersion、OS/arch、兼容模式、认证模块与日期。自研 workflow-core 的持久化也走同一方言适配,七库测试增加并行分支、任务抢占、定时器 fencing、审批事务和重启恢复。不得让主业务表跨库兼容而引擎仍只绑定一种数据库。
# 4. 消息后端与能力
把用户所说 Event 解释为“进程内事件适配器”,这是本方案假设;它不是持久化 broker。事件分两类:本地通知(允许随进程退出丢失)与可靠集成事件(必须 outbox + 可恢复投递)。
| 后端 | 角色 | 基础能力与限制 |
|---|---|---|
| Event | 同进程事件分发 | 简单/低依赖;裸 Event 无跨进程、持久化或自动恢复;可靠本地消费由 outbox worker 驱动 |
| Redis | Redis Streams + consumer group | 确认、pending 恢复、保留策略;不以 Pub/Sub 承担可靠投递 |
| RabbitMQ | AMQP adapter | publisher confirm、消费者手动 ack、预取、retry/DLQ;发送确认不等于业务处理成功 |
| Kafka | log/partition adapter | key 定位 partition、consumer group、offset;不把跨 partition 顺序当全局顺序 |
| RocketMQ | producer/consumer adapter | 消费确认、重试、死信;客户端/协议/服务端版本联测 |
Redis Streams 提供分组消费与确认;RabbitMQ 明确区分生产确认和消费确认,RocketMQ 对消费重试有单独策略。应用仍负责幂等与重试边界。Redis (opens new window)、RabbitMQ (opens new window)、RocketMQ (opens new window)
当前 M5 已验证 Event/Redis 正式 worker、同库 outbox/inbox、租约恢复与 SQL 积压指标;Redis 按保留窗口和所有现有消费组的读取/确认状态有界回收引用,SQL 去重与执行历史持续保留。双库实际发布程序完成停写后的 SQL、AOF/PEL 和独立 S3 字节组合恢复,恢复后重投已完成事件不会新增业务回执。此结论限本地工程基线;其他消息后端、在线迁移及生产 RPO/RTO 未认证。操作约束见 可靠消息后端与积压观测,验收见 M5 记录。
# 5. 统一消息合同
基础端口提供 Publish/Subscribe/Ack/Nack/Close 和 Capabilities 描述。延迟、事务消息、保序、重放等通过能力扩展表达,不把每个后端都强制伪装成完全一致。缺少所需能力时启动配置报错,不能静默降级成不可靠投递。
{
"eventId": "evt_opaque_unique_id",
"eventType": "system.user.disabled",
"schemaVersion": 1,
"tenantId": "101",
"aggregateType": "user",
"aggregateId": "1001",
"aggregateVersion": 3,
"occurredAt": "2026-10-22T02:00:00.000Z",
"traceId": "trace_example",
"payload": { "userId": "1001" }
}
用户 ID 是业务 actor,不等于可信调用身份。消费者验证消息来源,恢复受限 TenantScope 后再次校验租户和业务状态;来自任意外部生产者的 tenantId 不直接成为授权依据。
可靠投递链路:业务变更 + outbox 同库提交 → dispatcher 按路由发送 → 收到 transport 确认后标记已发 → 消费者 inbox 去重 + 业务事务 → 成功再 ack/提交 offset。发送成功后标记失败会导致重复,使用 eventId 和 (consumer,eventId) 唯一键去重。远程外部副作用使用业务幂等键与对账补偿,不声称端到端 exactly-once。
retry/DLQ 由适配器与应用共同规定次数、退避、最长处理时间及人工重放;本地 Event 可靠消费也需持久化失败状态。消费者处理成功但 ack 丢失允许重投,不能重复改变业务结果。
需要保序的聚合使用 tenantId + aggregateId 作为 routing key,并由数据库版本阻止过期事件覆盖新状态。重试可能影响顺序,必须单独声明;业务关键序列不能仅依赖 transport 的排列顺序。
# 6. 配置和认证
database:
driver: postgres
dsnSecretRef: APP_DATABASE_DSN
messaging:
default: redis
routes:
audit.notification: event
bpm.instance.changed: rabbitmq
cache:
driver: redis
此为设计示例,不是已有配置文件。生产启动校验 driver、schemaVersion、topic/consumer group、TLS/凭证和所需能力。切换后端需要暂停/排空、记录游标或双轨迁移、去重与对账;改配置不等于自动迁移未消费消息。
验收每个 transport:发送确认、同组分摊/不同组广播、崩溃恢复、重复、失败重试、死信与重放、租户污染、背压和停机。基础消息合同套件适用于所有可靠配置,增强能力单独记录。
# 单数据库产物约束
业务构建入口 scripts/build-go.sh 按当前项目的数据库选择只输出一套程序,固定放在 .cache/release/。装配器按构建标签裁剪其他数据库驱动,迁移资源按同一标签裁剪;配置与产物目标不一致时连接前拒绝。默认只编译一个 OS/架构,固定移除调试符号,不混入额外平台二进制。
scripts/test-database-profiles.sh 在临时目录验证两种可选配置的迁移文件系统和真实二进制依赖,验证结束清理,不为项目保留两种发布包。源码保留多个可选适配器,发布目录只有当前项目选择的依赖闭包。默认产物不附带 Oracle/SQL Server/DM 探针或供应商源码。具体命令见 后端启动。
# Cron与平台租户拆分(v29增量)
Cron库只做五段表达式与IANA时区日期计算,不作为可靠队列或业务提交的来源。计划锁内原子创建UTC occurrence与推进游标,错过策略为跳过、合并一次或最近24小时有界补执行。执行继续使用同库持久运行、租约、随机token、版本fencing和幂等业务键,拒绝旧执行者覆盖接管后的结果。
平台父运行拆分采用独立子运行表,不把所有租户放进一个大业务事务。父受理持平台控制/计划/运行锁,只插入有界目标工作;子业务分别按真实租户→计划→运行加锁,当前能力、每租户容量/一分钟速率、失败和恢复各自计算。父done表示子集合已持久受理,不能等同于所有目标成功。
平台目录写采用RC与控制行,租户模板列表用RR快照;复制在持有租户锁后取得父模板与子项的当前锁定读,保证MySQL既有旧快照不能把新父版本与旧条目混合。平台发布不反向获取租户锁。MySQL/PostgreSQL实际测试见M5增量记录;达梦v29只提供显式源码,不能沿用旧版本PoC宣称本次认证。