Skip to main content

Data Synchronization Platform - Detailed Design

1. 文档信息

1.1 目的

本文基于以下上游文档定义 V1 可实现的系统设计:

需求基线决定“必须实现什么”,技术选型报告决定“采用什么技术”,本文决定“组件如何协作”。若三者冲突,优先级依次为需求基线、已接受 ADR、本文。

1.2 设计状态

本文是 V1 详细设计草案。标记为“PoC 待定”的参数在 PoC 通过前不得视为生产默认值。

1.3 术语约束

沿用需求基线中的术语。本文新增的实现术语只用于落地已定义能力:consumer offset 指队列消费者提交的读取位置,source offset 指 CDC 采集组件在源数据库日志中的恢复位置,terminal_offset 指某分区已连续获得终态结果的队列位点。本文中“Redpanda”是已选定的 CDC 事件队列实现;“Kafka API”“Kafka Connect”“Debezium Envelope”是选型组件或协议术语,不改变需求基线中的业务概念。未加限定的“检查点”均指平台检查点。

本文中“事件”默认指标准变更事件;“命令”默认指岸端生成并发往船端的可靠下行命令。RECEIVED 是接收状态,APPLIEDCONFLICT_RECORDEDREJECTEDEXPIRED_APPROVEDFORWARDED 是按处理契约定义的事件处理结果;COMMAND_APPLIEDCOMMAND_ALREADY_APPLIEDCOMMAND_CONFLICT_REPORTEDCOMMAND_REJECTED 是下行命令处理结果。两套结果不可互相替代。

本文状态机中的 PENDINGAPPLYINGRETRY_WAITTARGET_COMMITTEDFINALIZEDCONFLICT_CAPTURED 是岸端内部事件应用状态;其中内部状态通过 apply_job.state 保存,对外结果通过 event_receipt.result 保存。APPLIEDCONFLICT_RECORDEDREJECTEDEXPIRED_APPROVEDFORWARDED 按处理契约对外返回,其中 FORWARDED 仅适用于岸端本地 CDC 事件。DETECTEDAUTO_DECIDINGPENDING_MANUALDECIDEDDISPATCHEDACKNOWLEDGEDEXPIREDCANCELLED 是冲突实例状态,不是上行事件状态。DRAFTACTIVE 是规则发布状态,不是数据处理状态。

2. 设计目标

2.1 核心目标

  • 船端断网数月时,Debezium 变更持续进入独立 Redpanda,恢复联网后由 Go 同步客户端可控补传。
  • 网络采用至少一次传输,业务应用和裁决结果具备幂等效果。
  • 接收确认与终态事件处理结果分离,任何阶段均可查询和恢复。
  • 双向表通过版本基线检测并发冲突,岸端统一裁决。
  • 按节点限速、按规则调度,并可统计剩余量和 ETA。
  • 岸端 V1 采用模块化单体,保留连接器、队列和协议替换边界。

2.2 不解决的问题

  • 不提供跨业务数据库与平台数据库的分布式事务。
  • 不保证任意业务表自动支持双向冲突检测。
  • 不同步 DDL、附件和大对象。
  • 不提供船对船直连。
  • 不将平台 Topic 写入既有业务 Kafka,不以岸端消息总线或微服务拆分作为 V1 前提。

3. 总体架构

船端 CDC 由 Kafka Connect 运行 Debezium MySQL Source Connector,完整 CDC 事件通过 Kafka API 写入船端独立 Redpanda。Kafka Connect 独立部署,或复用已认证且与业务负载隔离的 Worker。同步客户端通过 Kafka API 消费船端 Redpanda 并实现平台差异化能力。

岸端 MySQL 的本地业务写入由 Kafka Connect/Debezium 捕获并写入岸端 Redpanda,Shore CDC Consumer 消费后生成面向目标船端的可靠下行命令。平台应用到岸端 MySQL 的远端事件通过来源标记抑制回环。岸端 V1 为 Go 模块化单体,以同一镜像按接收、事件处理、岸端 CDC 消费和管理职责运行;这些职责可按容量合并或拆分启动模式。Pod 可水平扩展,PostgreSQL 是平台工作流状态的权威来源,业务 MySQL 是业务记录的权威来源。

4. 模块边界

4.1 船端同步客户端

模块职责输入/输出
Connect Observer读取 Connector/Task、快照、source lag 和 schema history 状态,不修改 Kafka Connect 内部 offset。Connect REST/指标 -> 采集健康。
CDC Queue Consumer通过 Kafka API 按 Topic/Partition 消费 Debezium Envelope,维护队列消费位点;它不直接决定平台检查点。Redpanda CDC Topic -> 原始 CDC 记录。
Event Normalizer解析 Debezium Envelope、生成稳定事件 ID、提取版本和业务时间。原始记录 -> 标准变更事件。
Rule Runtime执行方向、过滤、列选择、映射、转换和数据集窗口。标准事件 -> 可投递事件或处置结果。
State Store保存规则、队列消费位点映射、平台检查点、事件处理结果、下行命令及其处理结果、统计和审计摘要;不重复保存完整 CDC 积压。SQLite 事务读写。
Scheduler按优先级、时间窗、公平性和限速选择批次。待发送事件 -> 上行批次。
Transport Client上传批次、拉取事件处理结果与下行命令、重试。HTTPS + Protobuf + zstd。
Remote Applier条件应用岸端变更或裁决,并抑制 CDC 回环。下行命令 -> 船端 MySQL。
Telemetry上报健康、积压、吞吐、ETA 输入和错误。指标、日志、心跳。

4.2 岸端模块化单体

模块职责状态所有权
Node & Identity节点注册、证书、权限和心跳。PostgreSQL。
Ingress批次认证、解压、校验、持久化接收与接收确认。PostgreSQL Inbox。
Event Processor去重、排序、规则校验、窗口判断和处理编排。PostgreSQL 处理状态。
Target Applier对岸端 MySQL 执行条件写入并核对结果。岸端 MySQL + PostgreSQL 工作流。
Conflict Arbiter冲突检测、自动/人工裁决及过期处理。PostgreSQL 冲突记录。
Downlink生成并持久化岸到船变更、裁决和配置命令,记录命令处理结果并按游标投递。PostgreSQL 下行队列。
Shore CDC Consumer消费岸端 Redpanda 中的 Debezium 事件,标准化并按规则生成下行命令;抑制平台远端应用回环。岸端 Redpanda -> PostgreSQL 下行队列。
Rule Management规则校验、版本发布、回退和节点生效反馈。PostgreSQL。
Backlog & ETA聚合积压、有效吞吐和预测结果。PostgreSQL 时序采样。
Management API为控制台提供节点、规则、冲突、审计和观测 API。只通过领域模块访问状态。

模块之间通过进程内接口和 PostgreSQL 持久任务协作。V1 禁止仅依靠内存消息完成需要故障恢复的状态迁移。

5. 双向表接入规范

5.1 必需同步元数据

每张双向表必须具有以下逻辑元数据;物理列名可按数据库规范映射:

字段类型说明
_sync_revisionCHAR(36)BINARY(16)当前记录版本令牌,每次本地业务写入生成新值。
_sync_origin字符串/UUID最后一次业务变更的原始节点。
_sync_event_id字符串/UUID,可空最近应用的远端事件,用于防环和幂等诊断。
_sync_updated_atUTC 时间平台观测时间,不替代业务更新时间。

本地业务写入必须保留写入前 _sync_revision,并生成新的结果版本。CDC 的 before 提供基线版本,after 提供结果版本:

base_revision = before._sync_revision
result_revision = after._sync_revision

插入事件的基线版本为空。删除事件使用删除前版本作为基线,并由同步客户端生成确定性的墓碑结果版本。

5.2 元数据维护方式

V1 首选由业务写入 SDK 或数据库接入脚本维护元数据。接入脚本必须区分本地业务写入和同步客户端远端应用:

  • 本地业务写入:生成新版本,来源设为本节点,远端事件标识清空。
  • 远端应用:使用事件给定的结果版本、原始节点和事件标识,不再次生成版本。

具体采用 SDK、触发器还是受控存储过程按表登记。接入验收必须证明绕过接入方式的直接写入会被拒绝或告警。

5.3 不满足规范的处理

无版本元数据的表只能配置为:

  • 单向同步;或
  • 业务明确接受的覆盖同步,并标记为“无法检测并发冲突”。

不得将基于网络到达顺序的覆盖描述为可靠冲突检测。

6. 标准事件模型

6.1 事件结构

ChangeEvent
schema_version
event_id
origin_node_id
source_connector_id
source_partition
source_position
source_sequence
transaction_id?
transaction_order?
rule_id
rule_version
database
schema
table
primary_key
operation // INSERT | UPDATE | DELETE | SNAPSHOT
before?
after?
base_revision?
result_revision
business_occurred_at?
captured_at
trace_id

schema_version 控制协议兼容。接收方必须拒绝不支持的主版本;新增可选字段只提升次版本。

字段语义约束:source_partitionsource_position 是平台标准事件中的队列分区及位点;source_sequence 是在标准化后用于同一来源分区顺序检查的连续序号,不能把数据库源位点或网络到达顺序直接当作该序号;transaction_idtransaction_order 只有采集组件提供事务元数据且平台启用事务顺序时才有值;captured_at 是 CDC 采集时间;business_occurred_at 是业务时间;base_revision 是变更产生时观察到的目标版本;result_revision 是本次写入预期生成的版本。字段缺失、来源不可信或语义不明时,事件必须进入可查询失败或阻塞状态,不得猜测填充。

标准事件标识字段:schema_version 是平台事件协议版本;event_id 是在来源和受控重放代次内稳定且全局可去重的事件标识;origin_node_id 是产生业务写入的节点;source_connector_id 是产生该原始 CDC 事件的采集实例标识;databaseschematableprimary_key 定位业务记录;operation 表示 INSERTUPDATEDELETESNAPSHOTbefore/after 是变更前后值;trace_id 串联一次同步链路的诊断信息。source_position 的唯一性和可回放性由队列适配器负责证明;是否连续由具体队列协议决定,不能把非连续位点当作数据缺口。

6.2 事件标识与不可变性

事件 ID 使用来源 Topic、Partition、Offset 和受控重放代次确定生成,具体公式见 7.4。PoC 必须验证快照、更新、删除、Connector 重启和人工重放不会发生 ID 碰撞或误去重。

Debezium 写入 Redpanda 后的原始事件不可修改。规则变化通过新 rule_version 产生新的标准事件解释或受控重放任务,不改写队列历史记录。敏感列必须优先通过 Debezium 配置或受控 SMT 在进入 CDC Topic 前排除;不得只在 Go 客户端发送前删除而让敏感明文长期留在 Redpanda。

7. 船端 CDC、Redpanda 与平台状态

7.1 Kafka Connect、Debezium 与 Redpanda 基线

  • MySQL binlog 使用 ROWbinlog_row_image=FULL
  • 使用独立最小权限复制账户。
  • 每个连接器具有稳定名称、唯一 database.server.id 和固定 Topic 前缀。
  • Kafka Connect config、offset、status 内部 Topic 持久化在 Redpanda,复制因子与 Redpanda 部署拓扑一致。
  • Debezium schema history 存储在 Redpanda 专用 Topic,禁止使用临时或单机文件。
  • 首次接入使用 Debezium initial snapshot;恢复从 Connect source offset 继续。
  • Connector 配置纳入版本控制,由平台发布流程调用 Connect REST API,不允许只在控制台手工修改。
  • Debezium、Kafka Connect、Redpanda 和所用 Kafka API 的兼容矩阵由 POC-013 固化。
  • 心跳、快照锁方式、事务元数据和 Connector task 数为 PoC 待定参数。

7.2 Topic 与分区

CDC Topic 默认采用 sync.cdc.<logical-name>.<database>.<table>。消息键使用业务主键,保证同一记录进入同一分区;无主键表不允许接入 V1。

默认沿用 Debezium 每表 Topic。若表数量导致 Topic 数量不可运维,可在专项 ADR 中使用路由转换合并 Topic,但必须继续保证 table + primary_key 的分区顺序和来源元数据完整。

CDC Topic 使用 delete 策略,不使用 compaction 代替完整变化历史。业务 CDC Topic 默认关闭 retention.msretention.bytes 自动淘汰,由平台清理任务按每个分区的连续 terminal_offset 执行受控前缀清理;清理点还须扣除审计安全窗口。Connect 内部 Topic 和 schema history 使用各自独立的保留策略,不参与业务检查点清理。Redpanda 磁盘容量至少包含最大离线积压、复制因子、序列化开销和 30% 安全余量;数据目录不得与业务 Kafka 共用磁盘配额。

7.3 Debezium 事件适配

Go 同步客户端消费 Debezium Envelope,并将以下来源信息映射到标准事件:

Debezium 信息平台信息
Topic / partition / offset源端分区、顺序和 CDC 队列定位。
source.file / source.pos / source.gtid数据库源位点,用于诊断和稳定事件 ID。
op新增、更新、删除或快照。
before / after变更前后值。
ts_ms 与 source timestamp采集时间;不替代业务时间。
transaction metadata事务标识和事务内顺序(启用时)。

适配层按 Debezium schema 版本注册解码器。未知主版本、字段类型不兼容或主键缺失时不提交该分区的新平台水位,写入失败摘要并告警。平台 Protobuf 不暴露 Debezium Envelope。

7.4 稳定事件 ID

event_id = UUIDv5(
origin_node_id + replay_epoch + kafka_topic + kafka_partition + kafka_offset +
database + table + primary_key + operation
)

Topic/Partition/Offset 在保留期内唯一定位 Connector 产出的记录。重建 Topic、重置 Connector 或人工重放必须使用新的 replay_epoch,并纳入事件 ID,避免与历史事件混淆。

7.5 SQLite 逻辑表

关键字段用途
inbound_commandcommand_id、类型、前置版本、命令状态、命令处理结果岸端变更和裁决命令的可靠下行队列。
event_stateevent_id、方向/处理契约、Topic/Partition/Offset、规则、接收状态、事件处理结果、字节和业务时间CDC 事件与对应契约结果的索引。
partition_watermark方向/处理契约、consumer group、Topic/Partition、读取/接收/终态连续 offset恢复、平台检查点、队列提交和展示;上行和下行不得共用同一水位。
rule_snapshot规则版本、内容、状态、校验结果最后生效配置。
applied_commandcommand_id、应用结果、本地结果版本下行幂等。
queue_metric采样时间、规则、数量、字节和时间范围离线统计与 ETA。

event_state 只保存标准事件摘要和必要的处理证据,完整 before/after 默认从 Redpanda 获取。活跃发送窗口允许短期缓存已转换载荷以减少重复序列化,但不得形成第二份长期主队列。岸端本地 CDC 事件生成 downlink_command 后,其事件结果为 FORWARDED;船端命令是否产生业务效果只由命令处理结果确认。

主要索引采用 (topic, partition, offset)(terminal_result, rule_id)(rule_id, business_occurred_at)。开启 WAL、外键、校验和 busy_timeout

7.6 队列消费位点与平台检查点

  • consumer_read_offset:Go 客户端已从底层 CDC 事件队列读取的位置;它不表示事件已发送或已获得终态结果。
  • received_offset:岸端已可靠接收的连续位置。
  • terminal_offset:当前处理契约中的事件已获得终态结果的连续位置,是该契约的平台检查点;上行使用事件处理结果,岸端本地 CDC 生成下行命令使用 FORWARDED
  • consumer group offset 只提交到连续 terminal_offset + 1;这里的 +1 表示下一条待读位置。已读取但未终态的事件在客户端重启后由 Redpanda 重放,并通过稳定事件 ID 复用已有 event_state 和岸端事件处理结果。
  • EXPIRED_APPROVED 只允许单向规则经审批进入。

队列消费状态的含义:CONSUMED 表示已从队列读取,READY 表示已完成标准化并具备调度条件,IN_FLIGHT 表示已进入当前批次或处理租约,RECEIVED 表示岸端已可靠接收,RETRY_WAIT 表示等待临时失败重试,TERMINAL 表示已获得上行事件终态结果,EXPIRED_APPROVED 表示经审批不再投递,BLOCKED 表示因永久错误、容量或安全条件暂停且需要处置。只有 TERMINALEXPIRED_APPROVED 才能参与连续平台检查点推进。

Go 客户端为每个 Topic/Partition 保持有限的在途窗口。遇到永久错误时后续 offset 可继续读取并保存状态,但 terminal_offset 和已提交 consumer offset 不越过缺口,除非错误已转为明确终态。重复回放会增加本地读取成本,但不会重复产生网络或业务效果。

7.7 Redpanda 容量与大积压

管理端从 Redpanda end_offset、平台水位和消息大小采样估算积压;end_offset 定义为该分区下一条待写入位置。从 CDC 采集到平台终态的剩余事件数近似为各分区 end_offset - terminal_offset;字节数按分区日志尺寸或采样消息大小估算。

Redpanda 达到 70% 磁盘水位时预警,85% 时暂停新的低优先级快照和规则扩展,90% 时进入严重状态;具体阈值由 POC-013 固化。磁盘水位不得触发越过连续平台检查点的清理;空间不足时进入 BLOCKED 并要求扩容或审批处置。任何水位处置不得修改业务 Kafka 的 Topic 或保留策略。

SQLite 仅随事件索引增长。终态事件超过审计保留期后可删除 event_state 明细并保留分区水位与聚合摘要;不得影响 Redpanda Topic 的独立保留治理。

8. 规则模型与执行

8.1 规则结构

SyncRule
rule_id, version, status
source_node_selector, target_node_selector
source_table, target_table
direction
primary_key_mapping
row_filter
included_columns, excluded_columns
field_mappings, transforms
conflict_policy
dataset_window
priority, rate_class, transfer_schedule
queue_quota
retention_policy

8.2 发布流程

节点先下载候选版本,在本地验证表、列、类型、主键、时间字段和冲突前提。全部硬校验通过后原子替换 rule_snapshot 的活动指针;失败时继续使用最后生效版本并上报原因。

发布状态含义:DRAFT 为可编辑草稿,VALIDATED 为硬校验通过,APPROVED 为完成授权审批,DISTRIBUTING 为向节点分发,ACTIVE 为节点已校验并正在执行,SUPERSEDED 为被新版本替代,ROLLBACK_TARGET 为被选作回退目标。任何未进入 ACTIVE 的版本都不是节点生效版本。

8.3 事件处理顺序

  1. 识别适用规则和方向。
  2. 检查来源标识,抑制远端应用形成的回环。
  3. 执行行过滤和数据集窗口判断。
  4. 执行列选择、字段映射和确定性转换。
  5. 生成标准事件并计算大小。
  6. 在 SQLite 建立处理状态索引后进入有界调度窗口;完整事件仍以 Redpanda 为主存储。

规则运行时不得访问外部网络或执行用户脚本。转换函数以白名单注册并具有版本。

9. 船岸传输协议

9.1 通用约束

  • TLS 1.2 以上,生产环境优先双向 TLS。
  • 数据 API 使用 Protobuf;管理 API 使用 JSON。
  • 批次正文超过阈值时使用 zstd,阈值 PoC 待定。
  • 每次请求带节点 ID、请求 ID、协议版本、时间戳和签名/证书身份。
  • 服务端按节点限制请求大小、并发数和速率。

9.2 API

方法路径用途
POST/sync/v1/batches上传上行事件批次并返回逐事件接收状态。
GET/sync/v1/receipts按游标拉取终态事件处理结果。
GET/sync/v1/commands按游标拉取岸端变更、裁决和控制命令。
POST/sync/v1/command-acks回报下行命令处理结果。
POST/sync/v1/heartbeat上报版本、健康、队列和吞吐摘要。
GET/sync/v1/config拉取节点候选规则和控制参数。

V1 使用客户端主动拉取下行数据,避免要求岸端主动连接处于 NAT 或离线状态的船端。

9.3 批次模型

UploadBatch
protocol_version
node_id
batch_id
created_at
events[]
uncompressed_bytes
checksum

batch_id 用于诊断,不作为事件去重键。服务端以 origin_node_id + event_id 去重。单批默认上限为 500 条或压缩前 4 MiB,任一先达到即发送;该参数由链路 PoC 调整。

9.4 接收确认与事件处理结果

接收状态表示 HTTP 批次进入岸端可靠存储的结果,不是业务处理结果。状态定义如下:

  • RECEIVED:已可靠持久化或已存在,可在故障后继续处理。
  • RETRYABLE_FAILURE:暂时无法接收,客户端按退避策略重试;该状态不推进平台检查点。
  • PERMANENT_REJECTED:协议、身份、规则或载荷不可恢复错误,进入阻塞队列。

接收状态与事件处理结果一一关联,但互不替代。事件处理结果是上行事件的终态:

  • APPLIED:目标业务效果和平台状态均已确认。
  • CONFLICT_RECORDED:冲突证据已持久化并创建或复用唯一冲突实例;该上行事件处理已经终止,冲突实例继续独立裁决。
  • REJECTED:事件无法应用,包含稳定原因码。
  • EXPIRED_APPROVED:按已审批策略不再投递。
  • FORWARDED:仅用于岸端本地 CDC 事件,表示可靠下行命令已持久化;不表示船端已应用。

下行命令另有命令处理结果:COMMAND_APPLIEDCOMMAND_ALREADY_APPLIEDCOMMAND_CONFLICT_REPORTEDCOMMAND_REJECTEDCOMMAND_CONFLICT_REPORTED 只表示船端报告了版本冲突,不表示船端已完成裁决;岸端收到并持久化证据后,才能推进相应下行平台检查点。

RECEIVED 绝不展示为同步成功。客户端只在获得上行事件终态或下行命令终态,并满足对应分区连续性条件后推进平台检查点。

9.5 重试

指数退避采用 full jitter:初始 1 秒、上限 5 分钟。HTTP 408、429、5xx 和网络错误可重试;认证失败、协议主版本不兼容和永久规则错误不可无限重试。服务端返回 Retry-After 时优先遵循。

10. 岸端可靠接收与应用

10.1 PostgreSQL 逻辑表

主键/唯一约束用途
sync_nodenode_id节点身份、状态和能力。
sync_rulerule_id, version版本化规则。
inbox_eventorigin_node_id, event_id可靠接收、去重和处理状态。
event_receiptorigin_node_id, event_id上行事件对外终态结果及原因;不保存冲突实例的完整生命周期。
record_headrule_id, record_key岸端已知当前版本、来源和墓碑。
apply_jobjob_idstate跨业务库可恢复应用任务;state 保存内部应用状态,不替代 event_receipt.result
conflict_caseconflict_id;活跃冲突键唯一冲突证据和状态。
conflict_decisiondecision_id不可变裁决结果。
downlink_commandtarget_node_id, command_id可靠下行任务。
audit_eventaudit_id追加式审计。
metric_samplenode_id, sampled_at, dimension积压、吞吐和 ETA 输入。

大表按月或节点哈希分区的阈值通过 PoC 决定。Inbox 事件正文的保留期与审计证据分离,避免永久保存敏感载荷。

10.2 接收事务

单批接收在 PostgreSQL 事务中完成:

  1. 验证节点身份、协议、载荷校验和及规则授权。
  2. 逐事件执行幂等插入;已存在事件返回已有接收状态。
  3. 保存原始标准事件、接收时间和待处理状态。
  4. 提交后才返回 RECEIVED

请求超时后客户端可安全重传整个批次。

10.3 目标应用工作流

由于业务 MySQL 和平台 PostgreSQL 不能进行分布式事务,使用可恢复状态机:

  1. 处理器锁定 record_head 和对应 inbox_event
  2. 若已有终态事件处理结果,直接结束。
  3. 比较 base_revisionrecord_head.current_revision
  4. 基线不一致时创建或复用冲突,内部应用状态进入 CONFLICT_CAPTURED,对外事件结果为 CONFLICT_RECORDED;对应冲突实例继续独立裁决。
  5. 基线一致时,在 MySQL 使用条件语句应用:WHERE _sync_revision = :base_revision
  6. MySQL 提交后记录 TARGET_COMMITTED;若进程此时崩溃,恢复任务按事件 ID 查询目标行元数据确认是否已应用。
  7. 在 PostgreSQL 更新 record_head、终态事件处理结果和审计,进入 FINALIZED

目标行必须保存 _sync_event_id,使第 6 步可确定恢复。若数据库返回受影响行数为 0,则重新读取目标版本:已是相同事件表示幂等成功,否则进入冲突检测。

10.4 同源顺序与并发

  • 同一 rule_id + primary_key 在岸端串行处理。
  • 不同主键可并行。
  • 同一源分区优先按队列 source_position 检测缺口;仅当适配器声明并验证了连续 source_sequence 时才使用它作为业务顺序依据。发现缺口时暂停该分区后续依赖事件并请求重传。
  • 对无因果关系的不同表不承诺全局顺序。
  • 需要跨行事务一致性的业务表不进入 V1,除非 PoC 明确验证事务分组应用。

11. 冲突检测与裁决

11.1 检测规则

操作无冲突条件冲突示例
INSERT目标无当前记录或墓碑满足规则。同主键已由另一来源创建。
UPDATE目标当前版本等于 base_revision两端基于同一旧版本分别更新。
DELETE目标当前版本等于 base_revision目标已被更新或已由不同事件删除。

时间戳和网络到达顺序不参与冲突检测,只在规则明确使用最后写入优先时参与裁决。

11.2 冲突唯一键

active_conflict_key = rule_id + record_key + current_revision + incoming_result_revision

对称排序两个版本令牌,使两侧重复到达归并为同一实例。每个 rule_id + record_key 同时只允许一个活跃冲突;新证据到达时追加证据或使旧裁决过期。

11.3 状态机

状态持久化要求
DETECTED固化双方事件、当前记录头和规则版本。
AUTO_DECIDING记录策略执行租约,超时可被其他实例接管。
PENDING_MANUAL创建唯一人工任务并记录 SLA。
DECIDED追加不可变 conflict_decision,不得覆盖历史。
DISPATCHED裁决结果已进入所有相关节点的下行队列。
ACKNOWLEDGED相关节点均返回已应用或无需应用。
EXPIRED当前记录版本不再属于裁决证据集合。
CANCELLED仅允许未产生业务效果的授权取消。

冲突状态与事件结果的关系:DETECTEDAUTO_DECIDINGPENDING_MANUALDECIDEDDISPATCHEDACKNOWLEDGEDEXPIREDCANCELLED 只描述冲突实例;它们不改变已经产生的上行 event_receipt.result。上行事件在冲突证据持久化后即返回 CONFLICT_RECORDED,裁决通过 conflict_decisiondownlink_command 另行追踪。

11.4 自动策略

岸端优先:若岸端当前版本仍等于冲突记录中的岸端证据版本,则保留岸端值,并向船端下发该版本;否则裁决过期。

最后写入优先:比较规则指定的业务更新时间字段。缺值、格式错误、超出允许时钟偏差时进入人工处理。时间相同按固定节点 ID 升序或降序决胜,规则中必须固化方向。

11.5 人工裁决

人工操作必须包含选择结果、理由和操作者,可选择岸端版本、船端版本或输入完整替代值。V1 不提供字段级自动合并。提交前再次校验当前版本;版本变化则拒绝提交并提示刷新证据。

11.6 裁决下行

ConflictCommand
command_id
decision_id
conflict_id
rule_id, rule_version
record_key
expected_local_revision
decided_value / tombstone
decided_revision
reason

船端仅在当前版本等于 expected_local_revision 时条件应用。若命令已应用则幂等确认;若当前版本不同则返回 DECISION_STALE,岸端把旧冲突置为 EXPIRED 并按新证据创建冲突。

12. 双向防环

远端应用写入 _sync_origin_sync_event_id 和事件给定的 _sync_revision。Debezium 捕获并写入 Redpanda 后,Go 同步客户端的 Rule Runtime 检测 _sync_event_id

  • 若该事件已存在于 applied_command,只记录采集位置,不产生上行事件。
  • 若来源为远端且本地值未发生新的业务修改,不回传。
  • 若业务随后修改记录,接入层生成新的版本并清空 _sync_event_id,作为新的本地事件上行。

防环判断失败不得静默丢弃;无法确认来源的事件进入阻塞状态并告警。

岸端采用相同原则:平台把船端上行事件应用到岸端 MySQL 时写入来源事件和版本标记;岸端 Debezium 捕获该写入后,Shore CDC Consumer 只推进岸端队列位点,不再次生成岸到船命令。岸端本地业务随后修改记录时必须生成新版本并清空远端事件标记,才能作为新的岸端事件下行。

13. 限速、调度与批次

13.1 令牌桶

每个节点维护独立上行和下行令牌桶:

  • rate_bytes_per_sec:长期平均上限。
  • burst_bytes:允许突发量。
  • 消耗按实际压缩后线上字节计量。
  • 配置变化平滑生效,不清空已有令牌为无限突发。

13.2 公平调度

规则优先级分为 CRITICALHIGHNORMALBULK。同优先级采用加权轮询;低优先级设置最大等待年龄,超过后临时提升调度权重,防止永久饥饿。

同一主键事件保持顺序。批次优先在同一规则和源分区内聚合,避免一个错误事件阻塞无关规则。

13.3 时间窗

时间窗按节点 IANA 时区计算。客户端保留最后可信时间;系统时间跳变超过阈值时暂停仅限时段的 BULK 规则并告警,CRITICAL 是否继续由规则指定。

14. 数据集窗口与历史积压

14.1 窗口判定

window_start = trusted_now_in_rule_timezone - window_duration
eligible = business_occurred_at >= window_start

边界默认左闭右开。业务时间为空、未来偏差超限或无法解析时进入失败状态,不以采集时间代替。

14.2 窗口收敛

新启用或缩小窗口时创建 WINDOW_RECONCILE 任务:

  1. 固化窗口边界和规则版本。
  2. 生成窗口内当前记录的受控快照。
  3. 衔接快照起始位点后的增量。
  4. 按规则决定窗口外目标记录保留或删除。
  5. 对账记录数、主键摘要和异常清单。

双向规则默认禁止自动删除滑出窗口的数据。启用前必须通过专项 PoC 和审批。

14.3 过期不再投递

仅适用于单向规则。审批对象包含规则版本、固定边界、事件数、原始/估算字节、业务责任人和处置理由。批准后事件进入 EXPIRED_APPROVED,不计为已应用;原始载荷按审计保留策略清理。

15. 积压统计与 ETA

15.1 统计维度

按节点、方向、规则、优先级和队列状态统计:

  • 事件数。
  • 原始字节与压缩后估算字节。
  • 最早/最新业务时间和采集时间。
  • 已接收待终态、冲突待裁决、失败和过期批准数量。
  • 当前队列磁盘占用。

压缩后估算通过最近成功批次的“压缩字节/原始字节”指数移动平均计算;无样本时使用规则级保守默认比率并标记低置信度。

15.2 有效确认吞吐

effective_throughput =
terminal_confirmed_payload_bytes / active_transfer_seconds

观测窗口默认 30 分钟。active_transfer_seconds 只计算网络可用且处于允许时间窗的时间;重试流量不计入有效载荷。同步展示物理网络吞吐,便于识别重试浪费。

15.3 ETA

net_drain_rate = effective_throughput - incoming_backlog_rate
  • net_drain_rate <= 0:不可可靠预测。
  • 样本时间少于 10 分钟:显示低置信度区间。
  • 正常情况:remaining_estimated_bytes / net_drain_rate,再映射到未来可用传输时间窗。
  • 预测同时给出 P50/P90 区间,区间依据近 24 小时有效吞吐分位数。

管理端必须显示计算时间、待传字节、流入速率、有效吞吐、限速和时间窗,不只显示一个完成日期。

16. 快照设计

16.1 生命周期

快照任务固化规则版本和起始位点。快照记录使用 SNAPSHOT 操作并具有稳定事件 ID;目标端使用 upsert 和版本条件保证重试幂等。

16.2 增量衔接

快照期间捕获的增量继续进入 Redpanda CDC Topic,但同一规则在快照基线应用完成前不越过其衔接屏障。进入 CATCHING_UP 后从 Redpanda 回放起始位点之后的增量,直至延迟低于阈值再标记完成。

17. 安全设计

17.1 身份与传输

  • 每个节点具有独立证书和 node_id,证书不可跨节点复用。
  • 岸端校验证书状态、节点状态和规则授权。
  • 证书支持重叠轮换期;吊销后拒绝新请求但保留已有积压。
  • 密钥存储使用操作系统或部署环境 secret,不写入镜像和普通配置文件。

17.2 数据最小化

  • 敏感列在船端持久化前排除。
  • 日志禁止输出 beforeafter 和完整主键;默认记录哈希或掩码。
  • 冲突证据按列级权限展示。
  • 失败记录保存错误位置和摘要,敏感载荷加密或不落库。

17.3 审计

审计覆盖规则、证书、积压过期审批、人工裁决、重放和高权限查询。审计记录追加写入,包含操作者、时间、对象、前后摘要、原因和追踪标识。

18. 可观测性

18.1 指标

核心指标:

  • sync_events_captured_total
  • sync_events_terminal_total{result}
  • sync_queue_events{state,rule}
  • sync_queue_bytes{state,rule}
  • sync_transfer_bytes_total{direction,retry}
  • sync_effective_throughput_bytes_per_second
  • sync_end_to_end_latency_seconds
  • sync_conflicts{state,rule}
  • sync_eta_seconds{confidence}
  • sync_rule_version{status}
  • sync_connected_nodes
  • sync_concurrent_uploads
  • sync_reconnect_queue_depth
  • sync_server_requests_in_flight

高基数的事件 ID、主键和冲突 ID只进入结构化日志与追踪,不作为指标标签。

18.2 健康状态

船端分别报告采集、队列、传输、下行应用和磁盘健康。总状态取最严重级别:HEALTHYDEGRADEDBLOCKEDOFFLINE

岸端分别报告接收、事件处理、目标数据库、裁决、下行和管理 API 健康。接收可用但事件处理积压时必须报告 DEGRADED,不能显示完全健康。

健康状态含义:HEALTHY 表示该子系统满足运行和延迟目标;DEGRADED 表示仍可提供部分能力但已低于目标或存在积压;BLOCKED 表示因永久错误、容量、安全或前置条件暂停处理,需人工或配置处置;OFFLINE 表示节点或链路不可达。健康状态是运行观测,不是事件处理结果,也不推进平台检查点。

18.3 告警

  • 节点心跳超时。
  • 队列达到 70% 预警、90% 严重阈值(PoC 待定)。
  • 最老未终态事件年龄超限。
  • 有效吞吐持续低于积压流入速率。
  • ETA 不可预测持续超过阈值。
  • 冲突待人工超过 SLA。
  • 规则分发失败或节点版本漂移。
  • CDC 位点、表结构快照或磁盘状态异常。
  • 同时重连节点数、请求排队或数据库连接池持续达到阈值。

19. 故障恢复

故障恢复行为
Go 同步客户端崩溃SQLite 恢复平台状态,从已提交 consumer offset 重放未终态事件;按稳定事件 ID 复用已有处理状态。
Kafka Connect/Debezium 故障从 Connect source offset 恢复采集;监控 source lag、Task 状态和 schema history。
Redpanda 故障停止 CDC 消费但不影响业务 Kafka;恢复后从已提交 consumer offset 重放。
船端断电Redpanda 与 SQLite 分别执行自身恢复;SQLite 异常时进入 BLOCKED,不推进 consumer offset。
上传超时整批重传;岸端逐事件去重并返回已有状态。
岸端接收实例崩溃未提交事务无 RECEIVED;已提交事件由其他处理器接管。
岸端 MySQL 提交后进程崩溃根据目标行 _sync_event_id 恢复为成功或重新进入冲突。
PostgreSQL 不可用岸端停止接收并返回可重试错误;不以内存暂存冒充可靠接收。
规则错误保持最后生效版本;候选版本标记失败并告警。
证书过期停止传输但继续本地采集;恢复身份后补传。

20. 部署设计

20.1 船端镜像与离线交付

容器必需持久卷
redpanda独立 CDC Topic、Connect 内部 Topic 和 schema history;数据卷与业务 Kafka 隔离。
kafka-connect / debezium采集 MySQL 变更并通过 Kafka API 写入 Redpanda。
sync-client/var/lib/sync:SQLite 平台状态、规则和下行命令。
otel-collector可选本地短期遥测缓冲。

sync-client 使用多阶段构建,最终镜像采用 scratch 或满足证书/时区要求的 distroless 基础镜像,以非 root 用户运行。镜像只包含 Go 可执行文件、CA、时区和必要模板;调试工具不进入生产镜像。

离线交付包结构:

sync-client-<version>-<os>-<arch>/
images/redpanda.tar.zst
images/kafka-connect-debezium.tar.zst
images/sync-client.tar.zst
compose.yaml
install.ps1 / install.sh
rollback.ps1 / rollback.sh
checksums.txt
VERSION

安装脚本校验摘要后导入镜像,保留当前与上一版本镜像。升级只切换镜像标签,不修改 /var/lib/sync、证书和节点配置;健康检查未在超时内通过时自动恢复上一版本。卸载默认保留数据卷,彻底清理需要显式二次确认。

同步客户端只读挂载证书和配置,根文件系统只读,/var/lib/sync 和临时目录使用独立挂载。资源目标为内存 128 MiB 和平均单核 10%,最终 requests/limits 由船端 PoC 固化。SQLite 卷按事件处理结果索引和下行数据规划;数月 CDC 积压容量由独立 Redpanda 数据卷承担。Redpanda、Connect 和 Debezium 资源及离线包体积单独验收。

20.2 岸端容器

容器职责
sync-server接收、处理、裁决、下行和管理 API。
sync-consoleReact 静态应用。
redpanda岸端 MySQL CDC 事件队列,不承载 PostgreSQL 工作流状态。
kafka-connect / debezium捕获岸端 MySQL 本地业务写入并写入岸端 Redpanda。
postgres平台状态和审计。
prometheus / grafana指标和可视化。

岸端业务 MySQL 是外部依赖,不由平台 Compose 自动创建生产实例。开发环境可提供测试数据库。

20.3 Kubernetes 资源

Helm Chart 至少包含:

  • 接收/处理服务 Deployment,初始不少于 3 副本。
  • 管理 API Deployment,可与处理服务使用同一镜像的不同启动模式。
  • Service、可选 IngressServiceAccount 和最小 RBAC。
  • ConfigMap 与外部 Secret 引用,不在 Chart values 保存明文密钥。
  • PodDisruptionBudget,保证滚动升级和节点维护期间仍有可用副本。
  • HorizontalPodAutoscaler,依据 CPU、请求并发和待处理任务深度扩缩。
  • startup、readiness、liveness probe;readiness 在数据库不可用或停止接收时失败。
  • requests/limits、拓扑分散、反亲和和优雅终止配置。

PostgreSQL 与业务 MySQL 默认使用集群外高可用实例;开发环境可由 Compose 提供。岸端 Redpanda 与 Kafka Connect/Debezium 可由 Chart 部署或引用平台外受管实例,但必须提供持久化、健康检查和版本化配置。Chart 不绑定云厂商存储、负载均衡或数据库服务。

20.4 水平扩展与集中重连

sync-server 无本地权威状态,可多实例部署。事件处理通过 PostgreSQL 行锁或租约领取任务;租约包含所有者和过期时间。对同一记录键使用数据库顾问锁或记录头行锁串行化。

船端请求带 0 至 30 秒随机启动抖动;服务端对每节点和全局并发限流,过载时返回 429 + Retry-After。HPA 扩容不能替代数据库容量控制,连接池总上限按“每 Pod 上限 × 最大副本数”校验,不得超过 PostgreSQL 可用连接预算。

Pod 终止时先使 readiness 失败,停止领取新任务,在宽限期内完成或释放租约;未完成 HTTP 请求由客户端幂等重试。滚动升级必须保持协议向后兼容,至少支持当前和前一客户端协议次版本。

21. 管理 API 与控制台

21.1 主要资源

  • /api/v1/nodes
  • /api/v1/rules
  • /api/v1/rule-releases
  • /api/v1/backlogs
  • /api/v1/receipts
  • /api/v1/conflicts
  • /api/v1/conflict-decisions
  • /api/v1/audit-events

写操作支持幂等键和乐观并发版本。人工裁决、规则发布、回退和积压过期审批要求权限与原因。

21.2 核心页面

  • 节点总览:在线状态、版本、积压、吞吐、ETA、磁盘和告警。
  • 节点详情:方向/规则维度队列、最老事件、链路和事件处理结果。
  • 规则管理:草稿、校验、审批、发布、节点生效和回退。
  • 冲突中心:证据对比、策略、状态、SLA 和人工裁决。
  • 积压处置:窗口外数据统计、审批和审计。
  • 审计查询:规则、裁决、证书和高权限操作。

22. 数据保留与清理

数据默认建议说明
船端 SQLite 终态索引7 天完整 CDC 正文由 Redpanda Topic 保留策略管理;SQLite 清理后保留分区水位和摘要。
船端 Redpanda CDC Topic连续检查点 + 审计安全窗口默认关闭时间/字节自动淘汰,按分区受控清理已终态前缀。
岸端 Inbox 正文30 天冲突证据单独保留。
事件处理结果摘要180 天用于对账和问题定位。
冲突证据与裁决1 年具体期限待业务确认。
审计记录1 年以上由合规要求确定。
指标明细30 天降采样数据可保留更久。

清理策略均配置化。处于非终态、活跃冲突或审计保全状态的数据不得清理。

23. 测试策略

23.1 单元测试

  • 事件 ID 稳定性与碰撞用例。
  • 规则过滤、映射、窗口边界和敏感列排除。
  • 状态机合法/非法迁移。
  • 冲突策略和裁决过期。
  • ETA 在零吞吐、负净排空率和时间窗下的计算。

23.2 集成测试

  • MySQL -> Kafka Connect/Debezium -> Redpanda -> Go 同步客户端 -> 岸端 -> MySQL 全链路。
  • 岸端 MySQL 本地写入 -> Kafka Connect/Debezium -> 岸端 Redpanda -> PostgreSQL 下行队列 -> 船端 MySQL 全链路。
  • 快照与增量衔接。
  • 重复批次和重复下行命令。
  • 岸端业务库与平台库之间各故障点恢复。
  • 规则发布、失败和回退。

23.3 故障注入

在 Debezium 写入 Redpanda、Go 客户端消费、consumer offset 提交、批次接收、目标提交、终态回执、裁决下行和船端确认前后分别终止进程。每个用例都必须对账:源数据、队列 offset、目标数据、平台检查点、事件处理结果、冲突和审计。

23.4 弱网与容量测试

  • 128 KiB/s、600 ms RTT、5% 丢包、每 10 分钟断线。
  • Redpanda 中 10 GiB 和 100 GiB 船端积压、磁盘水位与恢复回放;同时向业务 Kafka 施加基线负载并验证服务目标不退化。
  • 1,000 节点并发心跳、上传、结果拉取和断线后集中重连。
  • Kubernetes 滚动升级、Pod 驱逐、节点故障和 HPA 扩缩容。
  • 高优先级持续流量下验证低优先级不饥饿。
  • 实际恢复时间与 P50/P90 ETA 对比。

24. 需求追踪矩阵

需求设计章节
FR-0014、9、17、18、20
FR-0025、7、10
FR-003、FR-0073、4、8、12
FR-0046、10
FR-00516
FR-0067、9、19
FR-0085、12
FR-00911
FR-01018、21
FR-0118.2
FR-01213
FR-01314、15
FR-0147.5、7.6、9.4、10、19
FR-01520.1、20.2、20.3
FR-0163、4、7、12、19、20.1、20.2、20.3
NFR-001 至 NFR-00517
NFR-101 至 NFR-11213、15、18、19、20、23
NFR-113 至 NFR-1157、20.1、23.4、25
NFR-116、NFR-11718、20.3、20.4、23.4

25. 待 PoC 固化参数

参数初始建议验证依据
船端 CDC 主队列独立 Redpanda100 GiB 容量、保留、磁盘水位、恢复回放和业务 Kafka 隔离。
SQLite 同步级别FULL断电测试与性能。
船端及岸端 Debezium/Connect/Redpanda 版本POC-013 确定Kafka API 兼容矩阵与双向恢复测试。
CDC Topic 保留期/字节最大离线期与容量模型不早于业务处置边界淘汰。
上传批次500 条或 4 MiB弱网吞吐、失败重传成本。
zstd 级别3CPU 与压缩率。
上行并发每节点 1 至 2顺序、带宽和服务器容量。
吞吐观测窗口30 分钟ETA 稳定性。
队列阈值70% / 90%磁盘安全余量。
处理租约60 秒最长单事件处理与恢复时间。
审计保留期1 年合规确认。
船端镜像体积不超过 50 MiB多架构镜像构建结果。
完整船端离线包体积POC-010 确定Redpanda、Connect/Debezium、同步客户端、Compose、脚本和校验清单的实测总量。
岸端初始/最大副本3 / PoC 确定1,000 节点与集中重连压测。
HPA 指标CPU + 请求并发 + 任务深度扩缩容稳定性。

26. 实施顺序

  1. 盘点业务 Kafka 服务目标和船端资源,部署独立 Redpanda 与 Kafka Connect/Debezium,冻结兼容矩阵、隔离方案、Topic 与容量基线。
  2. 建立 Go 工程、Debezium 适配器、事件/规则模型、SQLite 状态库和 PostgreSQL 状态模型。
  3. 完成 MySQL -> Debezium -> Redpanda -> Go 客户端的单向上传、岸端应用、接收确认和终态事件处理结果。
  4. 完成故障恢复、consumer offset、平台检查点和全链路对账。
  5. 加入限速、优先级、时间窗、Redpanda 积压统计和 ETA。
  6. 接入双向表版本元数据、岸端 MySQL CDC、防环和岸到船下行。
  7. 实现冲突检测、自动裁决、人工裁决和裁决过期。
  8. 实现快照、数据集窗口和过期积压审批。
  9. 完成增量镜像/离线回退、1,000 节点、Kubernetes 故障、弱网、Redpanda 100 GiB 容量及业务 Kafka 隔离 PoC 后冻结生产参数。

每一步对应独立可验证里程碑;前一步的数据正确性和恢复测试未通过,不进入下一步。