_BACKFILL 自动 ALTER 应考虑降级为默认关闭:库在下游生产表上发不受控 DDL #13
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
来源
issue #11 实施期间的调研发现(设计文档
2026-08-17-issue11-caller-dimensions-design.md§8 记录为「另开议题」)。与 #11 同源——都源于「库自管下游 schema」这件事——但属独立的架构变更,按反 gold-plating 未纳入 #11。现状
两个遥测后端在构造期会对下游数据库发 DDL:
CREATE TABLE(先探测后建,issue #3/#9 的教训已收口)_BACKFILL机制逐列ALTER TABLE ... ADD COLUMN补列没有开关,库升级后首次调用即自动执行。#11 又刚给这张表加了两列,这条路径的实际使用频率在上升。
调研到的同类先例
考察了 Celery / APScheduler / Alembic / Django contrib / Hangfire / Quartz.NET / dbt / Airbyte / Fivetran / Prefect / Airflow:
create_all()不改既有表,schema 变更自己负责)、APScheduler 3.xschema_version,> 1直接RuntimeError)EnableHeavyMigrations(默认 false),原文理由是防止「不受控的升级在高负载环境造成长时间停机或死锁」on_schema_change,默认ignore(最保守)没有找到任何先例支持「库在下游库里自动 ALTER 出列」是默认行为。 Hangfire 的自我设限与本项目的处境精确同构:
ALTER TABLE ADD COLUMN取 ACCESS EXCLUSIVE 锁,PG 11+ 加列本身很快,但锁会排在长事务后面并阻塞该表其后所有查询,形成锁队列雪崩。而遥测是业务路径上的内联await。反面教材是 Hibernate 的
hbm2ddl.auto=update:为零配置而生,被复制进生产后孤儿列静默堆积、升级失败即应用下线。SQLAlchemy 官方也自我限定create_all仅适合测试与小应用,长期 schema 管理应交给 Alembic。问题
可能的方向(未定,需要设计)
调研建议的组合是 B + D + C:
information_schema.columns/PRAGMA table_info)而非版本号——多版本共存下版本号没有单一真相,且探测只需 SELECT 权限,不需要任何 DDL 权限--sql模式、Djangosqlmigrate、Hangfire 把Install.sql放进包里,都是主流工具的标准能力,不是奇技淫巧即默认行为改为:探测到缺列 → 记 warning + 打印需要执行的 SQL,并按缺失列降级写入,而非自己 ALTER。
需要一并确认的红线
无论最终怎么改,多版本共存的兼容纪律应当写进明文(Expand/Contract 成文规范):新列只增不删、必可空或有默认、INSERT 显式列名、SELECT 禁止
*。目前库已经满足前三条,但没有文档化为承诺。注意
这是破坏性行为变更(下游从「升级即自动补列」变成「升级后需手工执行一条 SQL」),触及库对下游的公共承诺,按 CLAUDE.md §3 属 brainstorming 强制档 + 人类门。
已随 1.2.3 发布(https://gitea.iomgaa.online/iomgaa/PolyGateway/releases/tag/v1.2.3)。
采纳的是"按后端不对称默认"这一档:新增
PGW_TELEMETRY_SCHEMA_MODE=auto|manual,三态——未设时按后端派生(SQLite→auto、Postgres→manual),显式设置则两侧都可覆盖。manual 档探测真实列集合后不发任何 DDL,改为逐列点名缺失列 + 打印可执行 SQL + 按现有列裁剪 INSERT 继续写入。不对称的理由:issue 引用的先例(Hangfire 锁队列、Prefect 多实例竞态、Alembic 审计链)语境都是共享的生产 PG,而 SQLite 侧是下游自己的本地文件(VT / CHSAnalyzer / dissect 都是
runs/*.db形态),无 DBA、无迁移工具,强加手工 SQL 步骤是净损失。实现中确认的两点,值得记下:① 关掉 ALTER 必须配套裁剪写入,否则旧表缺列时 INSERT 会整行失败、逐行 warning,遥测彻底丢失——比自动 ALTER 更严重地违反"遥测必录";② 库执行的补列语句与打印给下游的脚本是两份:库内先探测后 ALTER(避开 ACCESS EXCLUSIVE 锁),打印出去的那份必须带
IF NOT EXISTS才可重复执行。另外
telemetry_schema_sql(backend)进了顶层导出,下游可主动索取库要求的最小 schema 写进自己的迁移文件;PG 写入去掉了ON CONFLICT的冲突目标(分区表要求唯一约束包含分区键,带目标的语句在分区部署下会全线写不进去)。Expand/Contract 五条纪律已成文进 README 与 ARCHITECTURE D15。