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
Update 2PC plan: dedicated blocking thread for MutTxId
Replace the open problem section with the concrete solution: a dedicated
blocking thread per participant transaction holds the MutTxId for its
entire lifetime. Async HTTP handlers communicate via channels. The MutTxId
never crosses a thread boundary.
Includes the TxCommand enum design, session management, and ASCII diagram
of the HTTP handler / blocking thread interaction.
Copy file name to clipboardExpand all lines: crates/core/2PC-IMPLEMENTATION-PLAN.md
+51-16Lines changed: 51 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@
4
4
5
5
The TPC-C benchmark on branch `origin/phoebe/tpcc/reducer-return-value` (public submodule) uses non-atomic HTTP calls for cross-database operations. We need 2PC so distributed transactions either commit on both databases or neither. Pipelined 2PC is chosen because it avoids blocking on persistence during lock-holding, and the codebase already separates in-memory commit from durability.
6
6
7
-
## Protocol (Corrected)
7
+
## Protocol
8
8
9
9
### Participant happy path:
10
10
@@ -37,14 +37,59 @@ The TPC-C benchmark on branch `origin/phoebe/tpcc/reducer-return-value` (public
37
37
10. Send COMMIT to durability worker
38
38
11.**Barrier down** -- flush buffered requests
39
39
40
-
###Key correctness properties:
40
+
## Key correctness properties
41
41
42
42
-**Serializable isolation**: Participant holds write lock from CALL through END_CALLS. Multiple CALLs from the same coordinator transaction execute within the same MutTxId on the participant. The second call sees the first call's writes.
43
43
-**Persistence barrier**: After PREPARE is sent to durability (step 7/8 on participant, step 5/6 on coordinator), no speculative transactions can reach the durability worker until COMMIT or ABORT. Anything sent to the durability worker can eventually become persistent, so the barrier is required.
44
44
-**Two responses from participant**: The immediate result (step 3) and the later PREPARED notification (step 10). The coordinator collects both: results during reducer execution, PREPARED notifications before deciding COMMIT.
45
45
-**Pipelining benefit**: Locks are held only during reducer execution (steps 1-6), not during persistence (steps 7-14). The persistence and 2PC handshake happen after locks are released on both sides.
46
46
47
-
### Abort paths:
47
+
## Holding MutTxId: dedicated blocking thread
48
+
49
+
`MutTxId` is `!Send` (holds `SharedWriteGuard`). The participant must hold it across multiple CALL requests from the coordinator for serializable isolation. The solution: a **dedicated blocking thread per participant transaction** that holds the `MutTxId` for its entire lifetime. Async HTTP handlers communicate with this thread via channels. The `MutTxId` never crosses a thread boundary or touches an async context.
COMMIT arrives ---> send COMMIT to durability, barrier down
69
+
thread exits
70
+
```
71
+
72
+
On first CALL for a new 2PC transaction:
73
+
1. Spawn a blocking thread (`std::thread::spawn` or `tokio::task::spawn_blocking`)
74
+
2. Thread creates `MutTxId` (acquires write lock)
75
+
3. Thread blocks on a command channel (`mpsc::Receiver<TxCommand>`)
76
+
4. Store the command sender (`mpsc::Sender<TxCommand>`) in a session map keyed by session_id
77
+
5. Return session_id to coordinator along with the first CALL's result
78
+
79
+
Subsequent CALLs and END_CALLS look up the session_id, send commands on the channel. The blocking thread processes them sequentially on the same `MutTxId`.
80
+
81
+
The blocking thread also needs access to a WASM module instance to execute reducers. The instance must be taken from the pool on thread creation and returned on thread exit (after COMMIT or ABORT).
- Send ABORT to all participants (they still hold write locks)
@@ -64,17 +109,7 @@ The TPC-C benchmark on branch `origin/phoebe/tpcc/reducer-return-value` (public
64
109
- Coordinator inverts its own in-memory state, discards buffered durability requests
65
110
66
111
**Crash during protocol:**
67
-
- See proposal §8 for recovery rules
68
-
69
-
### Open problem: MutTxId is !Send
70
-
71
-
The participant holds MutTxId across multiple HTTP requests (CALL, more CALLs, END_CALLS). MutTxId is !Send (holds SharedWriteGuard). Options:
72
-
73
-
1.**Dedicated blocking thread per participant transaction**: spawn_blocking holds the MutTxId, communicates via channels. HTTP handlers send messages, blocking thread processes them.
74
-
2.**Session-based protocol**: Participant creates a session on first CALL, routes subsequent CALLs and END_CALLS to the same thread/task that holds the MutTxId.
75
-
3.**Batch all calls**: Coordinator sends all reducer calls + args in a single request. Participant executes them all, returns all results, then commits. Single HTTP round-trip, no cross-request MutTxId holding.
76
-
77
-
Option 3 is simplest but limits the coordinator to not making decisions between calls. Option 1 is most general. TBD.
112
+
- See proposal in `proposals/00XX-inter-database-communication.md` section 8 for recovery rules
78
113
79
114
## Commitlog format
80
115
@@ -94,12 +129,12 @@ On replay, when encountering a PREPARE:
0 commit comments