Files
Web_BLS_Heartbeat_Server/.github/prompts/plan-finalExecutableIndependentDualWriteNewHotTable.prompt.md
XuJiacheng 43fa7505e5 feat: 新增 G4 热表独立双写能力
- 新增配置项以支持旧/新明细表的独立写入开关及目标表名。
- 重构 DatabaseManager,抽象通用批量 COPY 写入内核,支持不同目标表的复用。
- 新增双明细写入编排器,支持旧/新表独立执行、重试及 fallback。
- 调整 HeartbeatProcessor.processBatch(),确保 room_status 独立执行。
- 错误表仅记录新表写入失败,旧表失败不再写入错误表。
- 重新定义消费暂停策略,基于当前启用的关键 sink 判断。
- 补充按 sink 维度的统计项与启动日志。

新增 G4 热表相关的数据库规范与处理逻辑,确保系统在双写模式下的稳定性与可扩展性。
2026-03-09 15:49:12 +08:00

10 KiB
Raw Permalink Blame History

最终可执行的实施清单

以下清单基于你已经确认的最终约束整理,可直接作为实施顺序使用。


一、实施目标

不改变数据来源不改变现有批次处理节奏不改变 room_status 写法与目标 的前提下,实现:

  • 旧明细表 heartbeat.heartbeat_events 可独立开关
  • 新明细表 heartbeat.heartbeat_events_g4_hot 可独立开关
  • 新明细表继续使用批量 COPY
  • 旧/新明细写入完全独立
  • room_status 始终独立执行,不受明细写入开关影响
  • 错误表 heartbeat.heartbeat_events_errors 只保留一份,仅记录新表写入失败
  • 支持未来关闭旧表,仅保留新表

二、最终行为规则

1. 明细写入规则

  • legacyHeartbeatEnabled=true 时,写旧表 heartbeat.heartbeat_events
  • g4HotHeartbeatEnabled=true 时,写新表 heartbeat.heartbeat_events_g4_hot
  • 两者可同时开启,也可单独开启,也可同时关闭

2. room_status 规则

  • 始终执行现有 upsertRoomStatus()
  • 目标表不变
  • 写法不变
  • 不依赖旧明细是否开启
  • 不依赖新明细是否开启
  • 即使旧/新明细都关闭,仍然写 room_status

3. 错误表规则

  • 继续使用现有 heartbeat.heartbeat_events_errors
  • 字段完全不变
  • 只记录新表写入失败
  • 旧表写入失败不再写错误表,只记录日志/统计

4. 消费暂停规则

  • 不能因为“两路明细都关闭”而暂停消费
  • 不能因为“新表表级错误”直接当成全局数据库离线
  • 只有在当前启用且关键的写入路径发生连接级不可恢复故障时,才触发暂停消费

三、代码改造清单

Task 1扩展配置项

目标文件

  • src/config/config.js
  • src/config/config.example.js
  • README.md 或部署文档

新增配置建议

  • DB_LEGACY_HEARTBEAT_ENABLED
  • DB_G4_HOT_HEARTBEAT_ENABLED
  • DB_ROOM_STATUS_ENABLED
  • DB_G4_HOT_TABLE
  • DB_LEGACY_TABLE

建议默认值

  • DB_LEGACY_HEARTBEAT_ENABLED=true
  • DB_G4_HOT_HEARTBEAT_ENABLED=false
  • DB_ROOM_STATUS_ENABLED=true
  • DB_LEGACY_TABLE=heartbeat.heartbeat_events
  • DB_G4_HOT_TABLE=heartbeat.heartbeat_events_g4_hot

实施要求

  • 保持与当前 config.js 风格一致
  • 布尔值走现有 parseBoolean
  • 表名配置单独放在 db 配置节点下

完成标准

  • 应用启动时可从环境变量读取旧/新明细开关
  • room_status 开关独立可控
  • 新旧目标表名可配置

Task 2抽象通用明细写入内核

目标文件

  • src/db/databaseManager.js

当前基础

  • 现有 insertHeartbeatEvents() 已实现:
    • 分区预创建
    • COPY ... FROM STDIN
    • 缺分区补建重试
    • fallback 逐条 INSERT

要做的事 把现有写旧表的强绑定逻辑抽象为通用方法,例如:

  • _insertHeartbeatEventsToTarget(events, targetConfig)
  • _buildHeartbeatCopySql(targetTable, columns)
  • _buildHeartbeatInsertSql(targetTable, columns)

目标参数至少包含

  • tableName
  • columns
  • logPrefix
  • enablePartitionEnsure
  • 缺分区识别规则

实施要求

  • 不能复制两份几乎相同的 COPY 代码
  • 保证旧表和新表复用同一套批量写入能力
  • 保证新表也走 COPY

完成标准

  • 写旧表和写新表都可通过同一个通用写入内核完成
  • 旧逻辑行为不变
  • 新表能力复用旧逻辑

Task 3实现双明细独立编排

目标文件

  • src/db/databaseManager.js

新增编排方法建议

  • writeHeartbeatDetails(events)
  • insertHeartbeatEventsDual(events)

它需要负责

  • 判断旧表是否开启
  • 判断新表是否开启
  • 分别调用:
    • legacy writer
    • g4Hot writer
  • 聚合结果并返回结构化结果

返回结果建议 至少包含:

  • legacy.enabled
  • legacy.success
  • legacy.insertedCount
  • legacy.failedRecords
  • legacy.error
  • g4Hot.enabled
  • g4Hot.success
  • g4Hot.insertedCount
  • g4Hot.failedRecords
  • g4Hot.error

实施要求

  • 两路执行逻辑互不影响
  • 一路失败不能吞掉另一路结果
  • 两路都关闭时返回“跳过写入”的明确结果,不报错

完成标准

  • 系统能正确识别四种模式:
    • 仅旧
    • 仅新
    • 双写
    • 双关

Task 4调整 HeartbeatProcessor.processBatch() 的主流程

目标文件

  • src/processor/heartbeatProcessor.js

当前逻辑问题 当前流程中:

  • 先写 insertHeartbeatEvents(batchData)
  • 只有成功后才 best-effort 写 room_status

这与当前最终要求不一致。

改造目标 把流程改成三段独立逻辑:

A. 明细写入

调用新的双写编排方法,获取旧/新明细写入结果

B. room_status 写入

无论旧/新明细写入开关状态如何,都执行:

  • upsertRoomStatus(batchData)

注意:

  • 只要 DB_ROOM_STATUS_ENABLED=true
  • 就执行当前逻辑
  • 不依赖明细成功与否

C. 错误表写入

仅把新表失败记录送入 insertHeartbeatEventsErrors()

完成标准

  • room_status 从“依赖旧表成功”变成“独立执行”
  • 错误表只接新表失败
  • 旧表失败不进错误表

Task 5重新定义错误表调用策略

目标文件

  • src/db/databaseManager.js
  • src/processor/heartbeatProcessor.js

要求

  • 保留 insertHeartbeatEventsErrors() 表结构和 SQL
  • 仅调整调用来源:
    • 新表失败 → 写错误表
    • 旧表失败 → 不写错误表

实施要求 建议在双写结果聚合后,只提取:

  • g4Hot.failedRecords

转换成当前错误表 payload 后调用 insertHeartbeatEventsErrors()

完成标准

  • 错误表字段、表结构、SQL 全不变
  • 旧表失败不会污染错误表
  • 新表失败可追踪

Task 6调整连接状态与暂停消费策略

目标文件

  • src/processor/heartbeatProcessor.js
  • src/db/databaseManager.js

当前问题 目前 _isConnectionError()_scheduleDbCheck() 基本围绕单一 DB 写入路径设计。

改造要求 把“是否需要暂停 Kafka 消费”改成基于启用中的关键 sink判断。

建议规则

  • 仅旧表开启:旧表连接故障才可能触发暂停
  • 仅新表开启:新表连接故障才可能触发暂停
  • 双开:如果两路都因连接级错误不可写,可触发暂停
  • 双关但 room_status 开启:只要 room_status 还能正常写,就不应因明细关闭而暂停

重要区分 以下不能都算成“数据库离线”:

  • 连接失败
  • 缺分区
  • 表不存在
  • 表字段不匹配
  • 权限不足

其中真正应触发离线处理的优先是:

  • 08006
  • 08001
  • 08003
  • 08004
  • 08007
  • 57P03
  • 以及明显的网络连接错误

完成标准

  • 不会因为新表单独异常而误停整个消费
  • 不会因为两路明细关闭而误停消费

Task 7补充启动日志与配置摘要

目标文件

  • src/index.js

新增日志建议 启动时输出:

  • 旧明细写入是否开启
  • 新明细写入是否开启
  • room_status 是否开启
  • 旧表目标
  • 新表目标

完成标准

  • 运行日志中能直接看出当前处于哪种模式
  • 运维排障时不用翻配置文件

Task 8补充统计项

目标文件

  • src/stats/statsManager.js
  • 如有 Redis 控制台输出,也同步补充

新增统计建议

  • legacy detail success count
  • legacy detail failed count
  • g4Hot detail success count
  • g4Hot detail failed count
  • room_status success count
  • room_status failed count
  • g4Hot error-table inserted count

完成标准

  • 双写观察期可以快速对比旧表/新表健康状态
  • 能单独看出新表失败是否升高

四、测试清单

Task 9补充单元测试

目标文件

  • test/smoke.test.js
  • 如有必要新增专门的 databaseManager 测试文件

必须覆盖的场景

  1. 仅旧表开启
  2. 仅新表开启
  3. 旧新双开
  4. 旧新双关,但 room_status 开启
  5. 旧成功、新失败
  6. 旧失败、新成功
  7. 双失败
  8. 新表失败时写错误表
  9. 旧表失败时不写错误表
  10. room_status 始终执行
  11. COPY 失败时新表降级逐条 INSERT
  12. 缺分区时自动补分区重试

完成标准

  • npm test 可覆盖新旧双写与独立逻辑
  • room_status 独立性有明确断言

Task 10补充数据库 smoke 验证

目标文件

  • scripts/db/smokeTest.js
  • 或新增 scripts/db/smokeG4Hot.js

验证项

  • 新表 heartbeat.heartbeat_events_g4_hot 能写入
  • 新表分区能正确附着
  • 新表唯一键/索引存在
  • 新表可按主查询维度检索
  • 旧表关闭、新表开启时仍正常
  • 旧新都关但 room_status 开启时系统不报配置错误

完成标准

  • 有一套上线前可重复执行的 smoke 脚本
  • 可快速确认新表接入正确

五、上线实施顺序

Step 1代码部署保持现状

配置:

  • 旧表开启
  • 新表关闭
  • room_status 开启

目标:

  • 确认改造后代码在“旧模式”下行为不变

Step 2开启新表双写

配置:

  • 旧表开启
  • 新表开启
  • room_status 开启

目标:

  • 观察新表写入是否稳定
  • 检查新表失败是否进入错误表
  • 比对旧/新明细数据量

Step 3观察稳定性

重点观察:

  • 新表 COPY 成功率
  • 新表 fallback 频率
  • 错误表记录量
  • room_status 是否持续正常

Step 4关闭旧表

配置:

  • 旧表关闭
  • 新表开启
  • room_status 开启

目标:

  • 验证“仅保留新表”时系统稳定运行

Step 5保留过渡观察期

目标:

  • 持续观察新表、错误表、room_status

六、验收标准

满足以下全部条件即可验收:

  1. 新表 heartbeat.heartbeat_events_g4_hot 接入成功
  2. 新表仍使用批量 COPY
  3. 旧/新明细可独立开关
  4. room_status 始终独立执行
  5. 两路明细都关闭时,系统仍允许继续写 room_status
  6. 错误表仅记录新表失败
  7. 旧表失败不会写错误表
  8. 不修改 Kafka 数据来源
  9. 不修改现有数据转换来源
  10. 可支持后续关闭旧表,仅保留新表

七、建议的实施顺序编号版

  1. 扩展 config.js 配置项
  2. 重构 databaseManager.js,抽通用 COPY writer
  3. databaseManager.js 增加旧/新明细双写编排
  4. 调整 heartbeatProcessor.js,让 room_status 独立执行
  5. 调整错误表调用,仅接新表失败
  6. 重构连接异常与暂停消费判定
  7. 增加启动配置摘要日志
  8. 增加 stats 指标
  9. 补测试
  10. 补 smoke 验证脚本
  11. 按“旧开新关 → 双开 → 关旧留新”上线