Files
Web_BLS_Heartbeat_Server/openspec/specs/db/spec.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 Blame History

数据库操作规范

Purpose

本规范定义本服务对 PostgreSQL 的连接池配置、表结构初始化(含分区表)、分区预创建维护策略、批量写入与约束错误处理等行为。

Requirements

Requirement: 数据库连接管理

系统 MUST 能够建立和维护与 PostgreSQL 数据库的连接。

Scenario: 成功连接数据库

  • WHEN 系统启动时
  • THEN 应该成功连接到配置的PostgreSQL数据库
  • AND 应该监控连接状态

Scenario: 数据库连接断开重连

  • WHEN 数据库连接断开时
  • THEN 系统应该自动尝试重连
  • AND 重连失败时应该记录错误日志

Requirement: 心跳数据写入

系统 MUST 能够将处理后的心跳数据写入 PostgreSQL 数据库。

Scenario: 写入单条心跳数据

  • WHEN 接收到单条处理后的心跳数据时
  • THEN 系统应该将数据写入数据库
  • AND 返回写入结果

Scenario: 批量写入心跳数据

  • WHEN 接收到批量处理后的心跳数据时
  • THEN 系统应该使用批量写入机制将数据写入数据库
  • AND 提高写入效率

Requirement: 数据完整性保障

系统 MUST 保障写入数据库的心跳数据完整性。

Scenario: 事务管理

  • WHEN 写入多条相关数据时
  • THEN 系统应该使用事务确保数据一致性
  • AND 要么全部写入成功,要么全部失败

Scenario: 数据约束验证

  • WHEN 写入的数据违反数据库约束时
  • THEN 系统应该捕获约束错误
  • AND 记录错误日志
  • AND 根据配置决定是否重试

Requirement: 数据库表结构管理

系统 MUST 支持 room_status 实时状态表与心跳历史表的协同写入能力。

Scenario: room_status 结构与约束支持

  • WHEN 心跳服务执行 room_status upsert 同步
  • THEN 目标表应具备支撑 UPSERT 的唯一约束hotel_id, room_id, device_id
  • AND 需要的同步字段应存在并可写入

Requirement: 数组字段存储与索引

系统 MUST 支持将电力与空调子设备数据以数组列形式存储,并为指定数组列建立针对元素查询的索引。

Scenario: 新增数组列用于存储电力与空调子设备数据

  • WHEN 系统初始化 v2 心跳明细表结构时
  • THEN 表结构应包含以下新增列:
    • elec_addresstext[]
    • air_addresstext[]
    • voltagedouble precision[]、amperedouble precision[]、powerdouble precision[]、phasetext[]、energydouble precision[]、sum_energydouble precision[]
    • stateint2[]、modelint2[]、speedint2[]、set_tempint2[]、now_tempint2[]、solenoid_valveint2[]

Scenario: 针对数组元素的索引

  • WHEN 对 elec_address、air_address、state、model 执行“元素包含”类查询时
  • THEN 系统应提供 GIN 索引以优化查询

Requirement: 数据查询支持

系统 MUST 支持基本的数据查询操作,用于监控和调试。

Scenario: 查询最新心跳数据

  • WHEN 需要查询最新的心跳数据时
  • THEN 系统应该提供查询接口
  • AND 返回符合条件的数据

Scenario: 按条件查询心跳数据

  • WHEN 需要按特定条件查询心跳数据时
  • THEN 系统应该支持条件过滤
  • AND 返回符合条件的数据

Requirement: 高吞吐写入友好

系统 MUST 在高吞吐场景(约 5 万条/分钟量级)下避免单点瓶颈。

Scenario: 批量写入与分区裁剪

  • WHEN 进行批量写入
  • THEN 写入应路由到正确日分区
  • AND 常见查询hotel_id + 时间范围)应触发分区裁剪

Requirement: 服务运行时不得承担建库与定时分区维护

系统 MUST 仅负责数据库连接与业务写入,不在服务启动时执行数据库结构初始化,也不在进程内执行定时分区维护。

Scenario: 服务启动仅建立连接

  • WHEN 服务进程启动并连接 PostgreSQL
  • THEN 系统应只创建连接池并完成连通性检测
  • AND 不应在运行时执行表/索引/函数等 DDL 初始化
  • AND 不应在进程内启动分区维护定时任务

Scenario: 写入遇到缺分区时的运行时兜底

  • WHEN 批量写入 heartbeat.heartbeat_events 遇到缺分区错误
  • THEN 系统应基于该批次的时间范围调用 heartbeat.ensure_partitions
  • AND 调用后应重试当前批次写入
  • AND 系统不应在运行时创建或替换数据库 schema 对象

Requirement: G4 热表独立写入能力

系统 MUST 支持向 heartbeat.heartbeat_events_g4_hot 分区表执行批量 COPY 写入,并与旧表 heartbeat.heartbeat_events 共享同一套通用写入内核。

Scenario: G4 热表批量 COPY 写入

  • WHEN g4HotHeartbeatEnabled=true 且有一批心跳数据待写入
  • THEN 系统应使用批量 COPY 将数据写入 heartbeat.heartbeat_events_g4_hot
  • AND 写入流程应与旧表共享同一通用写入内核

Scenario: G4 热表分区由外部维护

  • WHEN 写入 heartbeat.heartbeat_events_g4_hot
  • THEN 系统不应在运行时为该表执行分区预创建、缺分区补建或其他分区维护逻辑
  • AND 分区应由外部脚本或外部调度系统维护

Scenario: G4 热表 COPY 失败降级

  • WHEN G4 热表批量 COPY 写入失败且重试次数耗尽
  • THEN 系统应降级为逐条 INSERT 写入
  • AND 单条失败不影响同批次其他记录

Requirement: G4 热表 guid 写入规则

系统 MUST 在写入 heartbeat.heartbeat_events_g4_hot 时为每条记录提供 guid 字段值,格式为无连接符的小写 GUID。

Scenario: 缺失 guid 时自动生成

  • WHEN 待写入记录未提供 guid
  • THEN 系统应为该记录生成一个新的 GUID
  • AND 生成结果应为 32 位十六进制小写字符串
  • AND 结果中不应包含 -

Scenario: 已提供 guid 时规范化

  • WHEN 待写入记录已提供 guid
  • THEN 系统应将该值转换为小写
  • AND 应去除其中的 -
  • AND 规范化后的值应写入 heartbeat.heartbeat_events_g4_hot

Requirement: G4 热表 power 辅助字段当前阶段写空

系统 MUST 在当前阶段将 power_carbon_onpower_carbon_offpower_person_existpower_person_left 固定写为 null,待后续计算逻辑接入后再启用实际赋值。

Scenario: 当前阶段统一写 null

  • WHEN 系统写入 heartbeat.heartbeat_events_g4_hot
  • THEN power_carbon_on 应写入 null
  • AND power_carbon_off 应写入 null
  • AND power_person_exist 应写入 null
  • AND power_person_left 应写入 null
  • AND 不应从来源数据的 extra 或其他字段提取这 4 个值

Requirement: 双明细独立编排

系统 MUST 提供双明细写入编排能力,按启动配置分别控制旧表与 G4 热表的写入,两路写入结果完全独立。

Scenario: 仅旧表开启

  • WHEN legacyHeartbeatEnabled=trueg4HotHeartbeatEnabled=false
  • THEN 系统应仅写入旧表 heartbeat.heartbeat_events
  • AND 不应尝试写入 G4 热表

Scenario: 仅新表开启

  • WHEN legacyHeartbeatEnabled=falseg4HotHeartbeatEnabled=true
  • THEN 系统应仅写入 G4 热表 heartbeat.heartbeat_events_g4_hot
  • AND 不应尝试写入旧表

Scenario: 双写模式

  • WHEN legacyHeartbeatEnabled=trueg4HotHeartbeatEnabled=true
  • THEN 系统应分别写入旧表与 G4 热表
  • AND 旧表写入失败不影响 G4 热表写入
  • AND G4 热表写入失败不影响旧表写入

Scenario: 双关模式

  • WHEN legacyHeartbeatEnabled=falseg4HotHeartbeatEnabled=false
  • THEN 系统应跳过所有明细写入
  • AND 不应报错或阻止消费

Requirement: 写入目标启动配置

系统 MUST 通过启动配置环境变量分别控制旧表、G4 热表和 room_status 的写入开关与目标表名。

Scenario: 配置加载

  • WHEN 系统启动时
  • THEN 应从环境变量读取 DB_LEGACY_HEARTBEAT_ENABLED(默认 trueDB_G4_HOT_HEARTBEAT_ENABLED(默认 falseDB_ROOM_STATUS_ENABLED(默认 true
  • AND 应从环境变量读取 DB_LEGACY_TABLE(默认 heartbeat.heartbeat_events)、DB_G4_HOT_TABLE(默认 heartbeat.heartbeat_events_g4_hot

Scenario: 启动日志输出配置摘要

  • WHEN 系统启动完成时
  • THEN 应在日志中输出旧/新明细写入开关状态、目标表名和 room_status 开关状态

Requirement: 错误表仅服务 G4 热表

系统 MUST 将 heartbeat.heartbeat_events_errors 仅用于记录 G4 热表写入失败的记录,旧表写入失败不写入错误表。

Scenario: G4 热表写入失败记录错误

  • WHEN G4 热表写入产生失败记录
  • THEN 系统应将失败记录写入 heartbeat.heartbeat_events_errors
  • AND 错误表字段和结构不变

Scenario: 旧表写入失败不记录错误表

  • WHEN 旧表写入产生失败记录
  • THEN 系统不应将失败记录写入 heartbeat.heartbeat_events_errors
  • AND 应仅记录日志/统计

Requirement: 建库与分区维护能力必须以外部脚本提供

系统 MUST 在仓库根目录 SQL_Script/ 提供可被外部程序调用的建库/分区维护脚本。

Scenario: 提供初始化脚本

  • WHEN 运维或其他程序需要初始化数据库结构
  • THEN 应可使用 SQL_Script/01_init_schema.sqlSQL_Script/run_init.js
  • AND 脚本应支持幂等重复执行

Scenario: 提供分区预创建脚本

  • WHEN 运维或外部调度需要预创建分区
  • THEN 应可使用 SQL_Script/02_create_partitions.sqlSQL_Script/run_ensure_partitions.js
  • AND 无需启动主服务进程即可执行分区维护

ADDED Requirements

Requirement: 分区表新增数组列与数组元素索<E7B4A0>?系统 SHALL <20>?heartbeat.heartbeat_events 中新增用于存储电力与空调子设备的数组列并为指定数组列提供数组元素级查询索引<E7B4A2>?

Scenario: 新增数组<E695B0>?- WHEN 部署或升级数据库结构<E7BB93>?- THEN 表应包含 elec_address、air_address、voltage、ampere、power、phase、energy、sum_energy、state、model、speed、set_temp、now_temp、solenoid_valve

Scenario: 数组元素索引

  • WHEN 需要按 elec_address/air_address/state/model 的数组元素进行查<E8A18C>?- THEN 数据库应具备 GIN 索引以优化包含类查询