# 25P04 — 事务超时（transaction_timeout）

> PostgreSQL SQLSTATE 25P04：显式或隐式事务超过 transaction_timeout 后终止会话。
---

# 25P04 — 事务超时（transaction_timeout）

## 速览 {#at-a-glance}

`25P04` 是 `transaction_timeout` 触发的 FATAL 条件，覆盖显式 `BEGIN` 事务和由单条语句隐式启动的事务。选定的 18.6 案例使用 300 ms；PG10 因设置不可用而在源码/版本预检阶段跳过，不会探测未知设置。另有 PG16.15/PG17.11 的版本边界对照：PG16 返回设置不可用的 `42704`，PG17 实际产生 FATAL `25P04`；该对照独立于选定基础案例。

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `25P04` |
| 条件名 | `transaction_timeout` |
| 状态 | `有效` |
| 已知存在于 | `17.0` |
| 锁定快照 | `17.11, 18.6, 19beta3` |
| 宏 | `ERRCODE_TRANSACTION_TIMEOUT` |
| 别名 | `—` |

<!-- source facts: data/errcodes/25P04.json -->
<!-- END SQLSTATE FACTS -->

## 含义 {#meaning}

`transaction_timeout` 限制显式或隐式事务的存活时间，包括事务打开期间执行或等待的时间。触发后 PostgreSQL 发出 `terminating connection due to transaction timeout` 并终止会话。它不同于只中止一条语句的 `statement_timeout`，也不同于只覆盖客户端在开放事务中空闲时间的 `idle_in_transaction_session_timeout`。若 `transaction_timeout` 小于或等于其中任一设置，较长的超时会被忽略；预备事务不受此设置约束。

## 诊断 {#diagnosis}

在支持该设置的服务器上，先在目标会话自身检查有效的 `SHOW transaction_timeout`、`SHOW statement_timeout` 和 `SHOW idle_in_transaction_session_timeout`。控制连接上的 `SHOW` 或 `pg_settings` 反映的是观察者后端，不能用来确认另一个会话实际通过 `SET` 得到的值。再从控制连接查看 `pg_stat_activity` 的 `pid`、`application_name`、`state`、`xact_start`、`state_change`、`query_start`、`query` 等字段，定位所属应用或连接池，并判断事务是在执行还是空闲。按 backend PID、SQLSTATE 25P04、`error_severity = FATAL` 和精确报文匹配日志收集器记录。选定的 18.6 运行中驱动也暴露了 25P04，原连接已关闭；新的连接成功执行 `SELECT 1`。PG10 在发送 `SET transaction_timeout` 之前就由最低版本预检跳过。

## 处理 {#response}

丢弃已终止的会话并重连。对合法的长事务，可缩短工作、拆分工作单元或提高有效超时以控制在预算内；不要为了掩盖错误而降低超时。只有部署明确接受取消该保护时才设为 `0`。服务器关闭终止的会话时会在退出前回滚开放且尚未完成的事务；死亡连接不能再接收 `ROLLBACK`。已知尚未在该会话上提交的事务，不能通过该连接恢复。若另一种网络故障使客户端无法确定 `COMMIT` 是否到达服务器，应从新连接对账业务结果再重试；选定案例没有发送提交，不能据此声称存在这种不确定性。本批 PG10 没有兼容设置。

## FATAL 报文 {#messages}

服务器源码固定报出 `terminating connection due to transaction timeout`，严重级别为 `FATAL`；原连接会被终止。

## 实测诊断 {#observed}

`18.6 (Homebrew) / latest`：FATAL SQLSTATE `25P04`；主报文 `terminating connection due to transaction timeout`；backend PID `30576`；原连接已关闭 `True`；CSV 日志 `25P04`；JSON 日志 `25P04`；新连接探测 `1`。
`10.21 (Debian 10.21-1.pgdg90+1) / pg10`：案例 `not_applicable`（最低版本预检；未探测该目标不支持的设置）。

单独的边界对照：`16.15 / pg16` 因设置不可用返回 `42704` 并保持 `IDLE`，因此不计作 25P04 覆盖；`17.11 / pg17` 在 300 ms 后由 PID `73` 产生 FATAL `25P04`，CSV 与 JSON 都记录 FATAL `25P04`，原连接关闭，新连接为 `IDLE`。这些记录不计入选定基础案例。

## 代表案例 {#case}

下列 SQL 片段不是一次性粘贴脚本：在测试连接上执行设置、PID 和 `BEGIN` 后停止发送查询，由独立观察连接或日志收集器等待 FATAL；原连接终止后，另开新连接执行最后的探测。运行器从共享语句清单（registry）读取这些语句，完整断言、日志收集器关联、环境和清理见 [案例导出](../../data/cases/25p04.json)。

<!-- BEGIN SQLSTATE SNIPPET: transaction_timeout -->
```sql
-- set_timeout
SET transaction_timeout = '300ms';
-- backend_pid
SELECT pg_backend_pid();
-- begin
BEGIN;
-- probe
SELECT 1;
```
<!-- END SQLSTATE SNIPPET -->

上述片段的 SQLSTATE、诊断、状态和修复断言来自共享语句清单（registry）（SHA-256 `d05a26e56716c5c8c178f741843e6b14be87423ec9d1eb1a026ff67a57ce4658`）；[结构化证据](../../data/evidence/25p04.json)。

作者证据 ID：`identity`、`timeout-path`、`runtime`、`runtime.version-boundary`。选定基础运行记录：`runtime.25P04-batch2c-latest-20260909.latest`。单独的边界记录：`runtime.25P04-boundary-pg16-20260909.pg16` 和 `runtime.25P04-boundary-pg17-20260909.pg17`。

单独的边界对照已经记录了可用性结果：PG16 返回 `42704`，PG17 继续并实际产生 FATAL `25P04`。这个边界块是分支说明，不能整块一次性粘贴执行。在 PG16 上先执行 `SHOW transaction_timeout`；得到预期的 `42704` 后，如需证明连接仍健康，应在同一连接执行 `SELECT 1`，然后停止，跳过 `SET transaction_timeout`、`BEGIN` 和等待超时。在 PG17 或更高版本上，只有 `SHOW` 成功后才在目标连接执行 `SET transaction_timeout`、记录 PID 并执行 `BEGIN`；随后停止发送查询，由观察连接或日志收集器等待旧连接出现 FATAL 并关闭，最后只在新连接上执行探测。这些边界记录用于诊断对照，不增加选定基础案例数量。

<!-- BEGIN SQLSTATE SNIPPET: transaction_timeout_version_boundary -->
```sql
-- availability_probe
SHOW transaction_timeout;
-- set_timeout
SET transaction_timeout = '300ms';
-- backend_pid
SELECT pg_backend_pid();
-- begin
BEGIN;
-- probe
SELECT 1;
```
<!-- END SQLSTATE SNIPPET -->

## 版本与边界 {#versions}

锁定目录首次观察到 `transaction_timeout` 为 17.0。选定的 18.6 案例以 CSV/JSON 日志收集证据和新连接探测通过。单独的 PG17.11 边界案例实际记录了 FATAL `25P04`，PG16.15 则记录设置不可用的 `42704`；两者都不替代选定基础运行。PG10 是真实的 `not_applicable` 最低版本预检跳过：handler 不会发送或探测未知设置，因此这不构成更早版本边界证据。

## 相关 {#related}

[25P03 事务中空闲超时](../25p03/)、[40001 可串行化失败](../40001/)、[57014 查询取消](../57014/)。

## 来源 {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- `src.calls.REL_18_6` — `raw/calls/REL_18_6.jsonl` (SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`)
- [`src.postgres.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/tcop/postgres.c#L3473-L3501) (SHA-256 `9fb62275b1badf94d01ab351337b60410cd9b3ab1fe63fa9f23d6d2185a21061`)
- [`src.config.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/doc/src/sgml/config.sgml#L10252-L10347) (SHA-256 `ffca322c15315ba670733d6b320f23cbba7374db0d18937298ffc07b58799ef6`) · [official documentation](https://www.postgresql.org/docs/18/runtime-config-client.html)
- [`doc.monitoring.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/doc/src/sgml/monitoring.sgml#L706-L875) (SHA-256 `06b147f8d966fe46821871703ffcee8a3449cc3f7de5b64b9177f48569fe96f6`) · [官方监控文档](https://www.postgresql.org/docs/18/monitoring-stats.html)
- [`src.system_views.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/catalog/system_views.sql#L604-L605) (SHA-256 `418f4ae7472fe0742e6fbdc7a055581602c5131e082aa6f17e2c7269bf711972`) · [官方 `pg_settings` 文档](https://www.postgresql.org/docs/18/view-pg-settings.html)
- [`doc.protocol.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/doc/src/sgml/protocol.sgml#L61-L65) (SHA-256 `745339e07ebbc2bb67d98b258e25772ada644ef26011e3cd6b3fcbb435235f89`) · [官方协议文档](https://www.postgresql.org/docs/18/protocol.html)
