You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Combines the two prior-session 2PC corrections that share
distributed_txn.h.
Issue 01 (P0) — distributed 2PC must require safe session pinning
Pooled executors that cannot pin a physical connection across the
DML and PREPARE/COMMIT phases would silently corrupt 2PC:
phase 1 PREPARE could land on one backend session, phase 2 COMMIT
on another, and the participant would never see the commit. Old
code fell back to the unpinned path whenever checkout_session()
returned nullptr -- which is exactly what every pooled executor
returned, with no warning.
Fix:
- New virtual bool RemoteExecutor::allows_unpinned_distributed_2pc()
defaults to false. Executors must explicitly opt in to the
unpinned path.
- MySQLRemoteExecutor and PgSQLRemoteExecutor opt in (they keep one
durable physical connection per backend for the executor's
lifetime).
- MultiRemoteExecutor opts in (it composes the two single-conn
executors).
- ThreadSafeMultiRemoteExecutor (the pooled, production path) does
NOT opt in and so DistributedTransactionManager fails closed when
asked to enlist a backend without a pinned session.
Issue 02 (P1) — make 2PC phase timeouts deterministic
The previous code tried to set max_execution_time on MySQL XA
control statements, which silently does nothing (MySQL ignores it
for XA). PostgreSQL got a separate SET statement_timeout that ran
some indeterminate time before PREPARE / COMMIT PREPARED.
Fix:
- PostgreSQL: SET statement_timeout = N is issued on the same
pinned session immediately before each phase command, so the
timeout governs that exact statement.
- MySQL: dropped the misleading per-phase SQL timeout. XA control
statements are bounded only by connection-level read/write
timeouts (which the connection pool already configures). Doctrine
is now stated explicitly in the comments instead of pretending
there's a per-statement bound.
- distributed_txn.h is ~111 lines smaller thanks to the cleanup.
Tests:
- tests/test_distributed_txn.cpp updated to:
* exercise the fail-closed path via a mock executor that does not
opt in to unpinned 2PC,
* verify the per-phase PostgreSQL timeout SET is issued on the
pinned session,
* keep coverage for happy paths (XA + PREPARE TRANSACTION) and
in-doubt WAL recovery.
- tests/test_session.cpp gets a small update for the new flag.
Verification:
- make test: 1208 passed, 37 skipped (live-backend integration), 0
failed.
Plan:
- docs/superpowers/plans/2026-04-15-distributed-2pc-safe-pinning.md
is the implementation plan that drove issue 01. Committed
alongside the change.
# Distributed 2PC Safe Pinning Implementation Plan
2
+
3
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+
**Goal:** Prevent distributed 2PC from silently using unsafe unpinned fallback paths unless the executor explicitly declares that fallback safe.
6
+
7
+
**Architecture:** Extend the `RemoteExecutor` contract with an explicit capability for legacy-safe unpinned 2PC fallback. `DistributedTransactionManager` will keep using pinned sessions when available, but it will reject `nullptr` session fallback unless the executor opts in. Single-connection executors and test mocks can opt in; pooled executors will remain pinned-session-only.
Add tests that assert distributed 2PC fallback is rejected for an executor that returns `nullptr` from `checkout_session()` and does not explicitly opt in.
25
+
26
+
-[ ]**Step 2: Run targeted tests to verify failure**
0 commit comments