fix: PG init backfill rollback scope + audit-loop state upsert registration
- db: translate INSERT OR IGNORE ... SELECT to ON CONFLICT DO NOTHING (VALUES-only regex left SELECT form untranslated -> PG syntax error); register task_audit_loop_state (task_id) in _OR_REPLACE_PK so PG INSERT OR REPLACE becomes a real upsert instead of DO NOTHING. - server: _backfill_default_quotas uses SAVEPOINT containment and no longer commits the caller's transaction, so a backfill failure cannot roll back init_db's migrate ALTERs. - worker: _coord_child_rows rolls back before column-fallback retries so a missing-column failure on PG does not cascade into InFailedSqlTransaction. - tests: test_pg_init_fix.py (translation, SQLite backfill/upsert, real-PG fresh-init + merge + upsert + backfill containment, skipped without CLOUD_TEST_PG_DSN/driver).
This commit is contained in:
1 parent
d99853b684
commit
b0347b374d
4 files changed
+381
-4
No files matched your search
+27
-3
@@ -1642,8 +1642,28 @@ def _backfill_default_quotas(conn):
|
||||
|
||||
只补“users 里有、user_quotas 里无”的用户,已有行(含
|
||||
管理员显式设为 NULL 的不限额行)一律不动。返回补写行数。
|
||||
|
||||
失败只回滚到保存点,绝不回滚调用方的整个事务:init_db
|
||||
里 migrate 的 ALTER 与它同事务,旧版 except 里整事务
|
||||
rollback 会把 ALTER 一并丢掉(2026-10-31 新鲜 PG 库实测:
|
||||
init 后 tasks 缺 paused_by_user / interrupted_reason,合流
|
||||
随后在 PG“语句失败即 abort 整个事务”的语义下连环报
|
||||
InFailedSqlTransaction)。成功时也不再由本函数提交,提交
|
||||
由调用方负责(init_db 结尾统一提交;旧版成功时提交等于
|
||||
提前提交 init 的全部 DDL,改后 init 恢复原子性)。调用方
|
||||
只有 init_db 两处(均以提交收尾),改动不影响其它路径。
|
||||
保存点在 SQLite 与 PG 下都支持;保存点本身建不起来时
|
||||
(如连接已处 abort 态)退化为整事务 rollback 再返回 0。
|
||||
"""
|
||||
_ensure_quotas_table(conn)
|
||||
try:
|
||||
conn.execute("SAVEPOINT backfill_default_quotas")
|
||||
except Exception:
|
||||
try:
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
return 0
|
||||
try:
|
||||
cur = conn.execute(
|
||||
"INSERT OR IGNORE INTO user_quotas(user_id,"
|
||||
@@ -1652,13 +1672,17 @@ def _backfill_default_quotas(conn):
|
||||
" LEFT JOIN user_quotas q ON q.user_id = u.id"
|
||||
" WHERE q.user_id IS NULL",
|
||||
(_default_quota_usd_micros(), time.time()))
|
||||
conn.commit()
|
||||
conn.execute("RELEASE SAVEPOINT backfill_default_quotas")
|
||||
return cur.rowcount or 0
|
||||
except Exception:
|
||||
try:
|
||||
conn.rollback()
|
||||
conn.execute("ROLLBACK TO SAVEPOINT backfill_default_quotas")
|
||||
conn.execute("RELEASE SAVEPOINT backfill_default_quotas")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user