Files
PolyGateway/research-wiki/designs/issue13-schema-mode.md
T
iomgaa 39fcf2631d docs: fix the partitioning conflict the review caught
Postgres requires a partitioned table's unique constraints to cover the
partition key, so ranging on created_at forces the primary key to
(call_id, created_at) -- and ON CONFLICT (call_id) DO NOTHING then
matches no constraint at all. The retention design claimed INSERT stays
transparent under partitioning; that holds for the routing, not for the
conflict target, and telemetry would have failed outright on any
partitioned deployment. The write drops its conflict target, which is
byte-equivalent on a plain table and legal on both.

The cap design gains the three emitter construction sites it has to
touch and the relationship to the 200-char caps embed and OCR already
carry: they stay, and the new cap is the stricter of the two. Covering
all three call paths is deliberate -- their rows land in one table, and
issue #11 settled that argument already.
2026-08-19 08:59:30 -04:00

3.9 KiB

type, node_id, title, date
type node_id title date
design design:issue13-schema-mode issue #13: 遥测 schema 自动 ALTER 降级为按后端不对称的显式档位 2026-08-19

issue #13: 遥测 schema 自动 ALTER 降级为按后端不对称的显式档位

正文: 2026-08-19-issue13-schema-mode-design.md。状态: 待人类审批。同批交付 design:issue12-telemetry-retention

  • 选定方案: 新增 PGW_TELEMETRY_SCHEMA_MODE=auto|manual(三态,未设时按后端派生: SQLite→auto、Postgres→manual)。manual 档探测真实列集合后不发 DDL,改为 warning 逐列点名 + 打印可执行 SQL,并按现有列裁剪 INSERT 继续写入。新增公共函数 telemetry_schema_sql(backend) 供下游主动索取建表/补列脚本。
  • 为什么两侧不对称: issue 引用的全部先例(Hangfire 锁队列雪崩、Prefect 多实例竞态、Alembic 审计链)语境都是共享的生产 PG——ALTER TABLE ADD COLUMN 取 ACCESS EXCLUSIVE 锁,排在长事务后会阻塞该表其后所有查询,而遥测是业务路径上的内联 await。SQLite 侧则是下游自己的本地文件(VT/CHSAnalyzer/dissect 的 runs/*.db 全是这个形态): 无 DBA、无迁移工具、无第二个系统碰它。强加手工 SQL 是净损失。两侧有意不对称在本库已有先例(issue #9 的建表探测)。
  • 关掉 ALTER 必须配套裁剪写入: 今天 _INSERT 是 24 列固定语句,旧表缺列时若不 ALTER 则 INSERT 全部失败 → 逐行 warning → 遥测彻底丢失,比自动 ALTER 更严重地违反"遥测必录"。降级写入不是增强,是本变更成立的前提。
  • 打印的 SQL 必须与执行的 DDL 同源: _DDL/_BACKFILL/_COLUMNS 今天在两个 recorder 各存一份,公共函数再写一份则三份必然漂移,表现为"下游照打印的 SQL 建完表,库仍报缺列"。故收敛进新的 telemetry/schema.py 作单一事实源——这是正确性要求,不是顺手重构。
  • manual 档不停 CREATE TABLE: issue 把建表列为现状描述而非指控(已在 #3/#9 收口为先探测后建);新建表无既有数据、无并发访问者,不存在锁与数据风险,停掉它会断掉零配置起步。Celery 先例同样是"自动建表 + 永不 ALTER"。
  • 缺省规则落 config 层(人类决策): recorder 的 auto_migrate 为 keyword-only 必填,派生只写在 config 一处,不与类签名漂移。代价是 35 处直接构造点需改。
  • 被否决备选: 两侧统一默认 manual(现有 SQLite 下游升级即需人工干预,而这些场景没有承接手工 SQL 的角色);保持 auto 默认只加开关(默认状态仍是库在下游生产表发不受控 DDL,核心诉求未满足);Celery 式无开关永不 ALTER(SQLite 净损失且下游无出路);APScheduler 4.x 式"schema 不认识就拒绝启动"——与"遥测初始化失败必须静默降级、不得拖垮业务调用"的库铁律正面冲突,不可选。
  • 附带成文化: Expand/Contract 纪律(新列只增不删不改名、必可空或带非易失默认、INSERT 显式列名、库从不 SELECT *)升格为文档化承诺。它是 design:issue12-telemetry-retention 分区方案能成立的前提——下游把表建成分区表后,库的 to_regclass 探测与 INSERT 路由才对分区透明。
  • 审查留痕(Codex,2026-08-19): 报 3 项。采纳 1 项(阻断级)——PG 的 ON CONFLICT (call_id) DO NOTHING 与 issue #12 的分区方案不兼容: PostgreSQL 要求分区表的唯一约束必须包含分区键,按 created_at 分区后主键被逼成 (call_id, created_at),该语句再也匹配不到约束,遥测在分区部署下全线写不进去。改为无冲突目标的 ON CONFLICT DO NOTHING(两种表形态都合法,普通表上逐字等价),改动归本 issue(它已在重写 INSERT 构造逻辑),见正文 §4.6。原设计"INSERT 路由对分区表透明"的判断只对普通 INSERT 成立,对冲突目标不成立——这是"透明"二字被推得过宽的典型。