选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

百科 / 错误代码 / Class 40 事务回滚

40001 serialization_failure

序列化失败

ERROR 已实测 详解 实测通过

类别
Class 40 事务回滚
严重等级
ERROR
条件名
serialization_failure
宏名称
ERRCODE_T_R_SERIALIZATION_FAILURE
启用版本
7.4
状态
活跃

版本覆盖

速览

40001 是 PostgreSQL 类别 40 transaction_rollback 中的 serialization_failure 条件。它告诉客户端,事务观察到的顺序无法与并发事务组成可串行化顺序,因此 PostgreSQL 中止冲突事务,让客户端重新执行。

代表性案例启动两个 SERIALIZABLE 事务,让它们读取同一值。第一个事务更新并提交;旧快照事务随后收到 could not serialize access due to concurrent update,进入 INERROR,只有 ROLLBACK 后才回到 IDLE。新的可串行化事务读取已提交值,执行 registry 中固定的 SET value = 2 操作并提交最终结果;这证明的是事务边界,不是业务计算逻辑。

运行 40001-manual-boundary-final-20260909 在 PostgreSQL 18.6 和隔离的 PostgreSQL 10.21 上均通过。逐目标断言和结构化观察见公开证据 JSON

含义与触发路径

SERIALIZABLE 隔离级别下,PostgreSQL 跟踪谓词和元组冲突;如果事务结果依赖一个无法串行化的顺序,就会拒绝该事务。旧事务更新之前已读取、后来被并发事务更新的行,是其中一个具体路径。SQLSTATE 说明事务结果,不说明业务操作是否应该重试。

执行器的更新路径使用 ERRCODE_T_R_SERIALIZATION_FAILUREcould not serialize access due to concurrent update 源码报文。其他序列化冲突可能使用不同报文,恢复冲突也可能附带 detail,但仍表达事务回滚语义。应用日志应保存完整诊断以及事务的读写集合。

4000123505 不同:用户主动请求的重复值不自动成为序列化失败,即使某些并发生成键的设计会产生应用认为可重试的唯一冲突。应根据操作语义和完整事务历史决定重试策略。

报文与诊断

下面的调度需要两个会话分别建立最初的快照。最后一段使用新的连接和新的可串行化快照,这是完整重试的必要部分。

CREATE TABLE serial_rows(id integer PRIMARY KEY, value integer NOT NULL);
INSERT INTO serial_rows VALUES (1, 0);

-- 在第一次提交前打开两个 SERIALIZABLE 快照。
BEGIN ISOLATION LEVEL SERIALIZABLE;
BEGIN ISOLATION LEVEL SERIALIZABLE;

-- first 会话读到 0;stale 会话也读到同一个 0。
SELECT value FROM serial_rows WHERE id = 1;
SELECT value FROM serial_rows WHERE id = 1;

-- first 会话写入 1 并提交;随后 stale 会话用旧快照写入。
UPDATE serial_rows SET value = 1 WHERE id = 1;
COMMIT;
UPDATE serial_rows SET value = 2 WHERE id = 1;
-- UPDATE 报告 40001,事务变为 INERROR。
ROLLBACK;

-- 新的重试事务:读取新值,重新执行操作并提交。
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT value FROM serial_rows WHERE id = 1;
UPDATE serial_rows SET value = 2 WHERE id = 1;
COMMIT;
SELECT value FROM serial_rows WHERE id = 1;

PostgreSQL 18.6 返回:

SQLSTATE: 40001
severity: ERROR
message_primary: could not serialize access due to concurrent update
message_detail: <none>
source: nodeModifyTable.c / ExecUpdate / line 2604

PostgreSQL 10.21 的主报文相同,nodeModifyTable.c 行号随版本变化。本路径没有 message_detail;其他冲突来源可能附带更多字段。SQLSTATE 和事务已经中止的状态是稳定的重试信号。

报文模板

源码里的格式串,不是某一次运行的输出。%s 之类是占位符,实际报文会填入对象名与取值。适用范围一栏是核验时留下的原始英文记录,未经翻译。

主消息 could not serialize access due to concurrent update

来源:src/backend/executor/nodeModifyTable.c(lines 2595-2605) @ REL_18_6

适用范围:The template is the observed executor update path; other serialization failures may carry different text or detail.

诊断

记录 SQLSTATE、严重级别、主报文、detail、hint、上下文、隔离级别、建立快照的语句以及事务状态。确认哪个事务先提交,以及哪些读取已过期。runner 断言两个初始读取都是 0,first 提交后为 IDLE,stale 事务在回滚前为 INERROR

不要在失败事务上继续查询。先回滚,再以新的事务开始,并重复完整的读取、决策和写入。只重放最后一条 UPDATE 可能基于已经失效的快照作出决定。

处理与修复

当操作为此设计时,应把 40001 当作事务重试信号:

  • 回滚整个失败事务并释放锁。
  • 按要求的隔离级别启动新事务,重新读取业务决策依赖的所有值。
  • 使用有限退避和最大重试次数重新执行操作。
  • 让操作具备幂等性,并在提交后验证最终业务结果。

代表性重试读取 1、写入 2、以 IDLE 状态提交,独立读取观察到最终值 2。这个隔离案例中的固定赋值不能证明生产环境任意计算都可重放;应用必须从新快照重新计算。

可复现案例

在一次性实例上执行过的场景。其中 1 个附有可执行 SQL,正文相应小节里给出。

serializable_stale_update PG 10 / 18 有 SQL

前置条件

  • Two sessions using SERIALIZABLE
  • Both read the same row before either writes

触发

Commit one writer, then update the row from the stale serializable transaction.

断言

  • The stale writer receives 40001
  • Its transaction is rolled back
  • The first committed value remains before retry
  • A fresh serializable snapshot is read, applied, and committed as a complete retry

处置

Retry the complete serializable transaction with bounded backoff and an idempotency policy; begin from a fresh snapshot and verify the final business result.

清理

Roll back workers and drop the case schema.

版本与边界

目录在 PostgreSQL 7.4 的锁定定义中已观察到 40001,并持续到 8.4.22 的 pre-9.0 定义;随后在列出的所有正式快照直到 PostgreSQL 18.6 以及 PostgreSQL 19 Beta 3 预览中存在。这是 definition_only 的存在边界,不是确切实现引入版本或运行时使用断言。9.0 到 9.1 之间记录了类别标题变化;目录保留该变化,扫描范围内条件没有其他定义变化。

旧快照更新案例在 PostgreSQL 18.6 和 10.21 上均通过。源码行号和冲突 detail 会因版本和冲突类型变化。本证据只覆盖旧快照更新及其新的完整重试,不声称所有 40001 路径使用相同报文,也不声称所有事务都能安全重试。

来源

证据

断言

每条断言都写明了是怎么核实的,以及它不覆盖什么。这一层是核验时留下的原始英文记录,照原样呈现,未经翻译。

  • 40001 is the serialization_failure condition in Class 40 transaction_rollback.

    核实方式Read the Class 40 section and 40001 row in the frozen errcodes.txt snapshot.

    不覆盖Directory identity does not identify one serialization-conflict origin.

    来源src/backend/utils/errcodes.txt(lines 329-333)

  • The executor update path reports ERRCODE_T_R_SERIALIZATION_FAILURE with could not serialize access due to concurrent update.

    核实方式Trace the ExecUpdate concurrent-update branch and compare the diagnostic from the stale serializable writer.

    不覆盖Other serialization conflicts can use different messages or source paths while retaining SQLSTATE 40001.

    来源src/backend/executor/nodeModifyTable.c(lines 2595-2605)

  • The stale serializable writer entered INERROR after 40001; after ROLLBACK, a fresh serializable transaction read 1, wrote 2, committed, and left final value 2 on both targets.

    核实方式Open both initial serializable snapshots before the first commit, trigger the stale update, roll it back, and assert the complete fresh read/fixed assignment/write retry and final value with registry transaction boundaries executed under autocommit=True.

    不覆盖The fixed assignment is a controlled retry proof; it does not establish that an arbitrary business calculation is safe to replay. The run reads 1 and sets value=2; it does not test a business calculation decision.

    来源doc/src/sgml/mvcc.sgml(serialization failure handling) · verify/cases/40001/cases.json(serializable_stale_update)

  • The locked catalogue records 40001 in every listed formal snapshot from 9.0.23 through 18.6 and in 19beta3; pre-9.0 history is not scanned.

    核实方式Read the manifest snapshots and definition references for the code.

    不覆盖The first scanned release is a lower bound, not an asserted introduction version.

    来源sources/manifest.lock.json(snapshots and definition_blobs entries for the 40001 definition)

运行记录

目标服务器版本结果覆盖案例
latest 18.6 (Homebrew) passed serializable_stale_update
pg10 10.21 (Debian 10.21-1.pgdg90+1) passed serializable_stale_update
latest 18.6 (Homebrew) passed serializable_stale_update
pg10 10.21 (Debian 10.21-1.pgdg90+1) passed serializable_stale_update

同类错误代码

Class 40 事务回滚 下的其他成员。