@optimystic/db-core 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cohort-topic/membership/verifier.d.ts +8 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -1
- package/dist/src/cohort-topic/membership/verifier.js +17 -0
- package/dist/src/cohort-topic/membership/verifier.js.map +1 -1
- package/dist/src/cohort-topic/promotion.d.ts +25 -0
- package/dist/src/cohort-topic/promotion.d.ts.map +1 -1
- package/dist/src/cohort-topic/promotion.js +58 -11
- package/dist/src/cohort-topic/promotion.js.map +1 -1
- package/dist/src/cohort-topic/wire/primitives.d.ts +6 -0
- package/dist/src/cohort-topic/wire/primitives.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/primitives.js +6 -0
- package/dist/src/cohort-topic/wire/primitives.js.map +1 -1
- package/dist/src/cohort-topic/wire/types.d.ts +5 -0
- package/dist/src/cohort-topic/wire/types.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.js +10 -19
- package/dist/src/cohort-topic/wire/validate.js.map +1 -1
- package/dist/src/collection/collection.d.ts +8 -1
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +19 -3
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/matchmaking/wire.d.ts +2 -2
- package/dist/src/matchmaking/wire.d.ts.map +1 -1
- package/dist/src/matchmaking/wire.js +3 -3
- package/dist/src/matchmaking/wire.js.map +1 -1
- package/dist/src/network/struct.d.ts +5 -5
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/testing/index.d.ts +1 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/refresh-probe.d.ts +31 -0
- package/dist/src/testing/refresh-probe.d.ts.map +1 -0
- package/dist/src/testing/refresh-probe.js +39 -0
- package/dist/src/testing/refresh-probe.js.map +1 -0
- package/dist/src/testing/test-transactor.d.ts +58 -5
- package/dist/src/testing/test-transactor.d.ts.map +1 -1
- package/dist/src/testing/test-transactor.js +87 -4
- package/dist/src/testing/test-transactor.js.map +1 -1
- package/dist/src/transaction/coordinator.d.ts +90 -6
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +311 -90
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transaction/errors.d.ts +42 -0
- package/dist/src/transaction/errors.d.ts.map +1 -1
- package/dist/src/transaction/errors.js +50 -0
- package/dist/src/transaction/errors.js.map +1 -1
- package/dist/src/transaction/index.d.ts +1 -1
- package/dist/src/transaction/index.d.ts.map +1 -1
- package/dist/src/transaction/index.js +1 -1
- package/dist/src/transaction/index.js.map +1 -1
- package/dist/src/transaction/session.d.ts +7 -3
- package/dist/src/transaction/session.d.ts.map +1 -1
- package/dist/src/transaction/session.js +7 -3
- package/dist/src/transaction/session.js.map +1 -1
- package/dist/src/transaction/transaction.d.ts +12 -2
- package/dist/src/transaction/transaction.d.ts.map +1 -1
- package/dist/src/transaction/transaction.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +1 -1
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +6 -26
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/dist/src/transactor/transactor-source.d.ts +4 -6
- package/dist/src/transactor/transactor-source.d.ts.map +1 -1
- package/dist/src/transactor/transactor-source.js +4 -6
- package/dist/src/transactor/transactor-source.js.map +1 -1
- package/dist/src/transform/atomic.d.ts.map +1 -1
- package/dist/src/transform/atomic.js +7 -0
- package/dist/src/transform/atomic.js.map +1 -1
- package/dist/src/transform/base-pins.d.ts +48 -0
- package/dist/src/transform/base-pins.d.ts.map +1 -0
- package/dist/src/transform/base-pins.js +65 -0
- package/dist/src/transform/base-pins.js.map +1 -0
- package/dist/src/transform/digest.d.ts +4 -2
- package/dist/src/transform/digest.d.ts.map +1 -1
- package/dist/src/transform/digest.js +14 -19
- package/dist/src/transform/digest.js.map +1 -1
- package/dist/src/transform/tracker.d.ts +31 -3
- package/dist/src/transform/tracker.d.ts.map +1 -1
- package/dist/src/transform/tracker.js +105 -13
- package/dist/src/transform/tracker.js.map +1 -1
- package/package.json +1 -1
- package/src/cohort-topic/membership/verifier.ts +487 -461
- package/src/cohort-topic/promotion.ts +86 -14
- package/src/cohort-topic/wire/primitives.ts +195 -188
- package/src/cohort-topic/wire/types.ts +480 -475
- package/src/cohort-topic/wire/validate.ts +504 -512
- package/src/collection/collection.ts +19 -3
- package/src/matchmaking/wire.ts +605 -603
- package/src/network/struct.ts +5 -5
- package/src/testing/index.ts +1 -0
- package/src/testing/refresh-probe.ts +52 -0
- package/src/testing/test-transactor.ts +100 -4
- package/src/transaction/coordinator.ts +312 -99
- package/src/transaction/errors.ts +141 -91
- package/src/transaction/index.ts +1 -1
- package/src/transaction/session.ts +7 -3
- package/src/transaction/transaction.ts +12 -2
- package/src/transactor/network-transactor.ts +1025 -1045
- package/src/transactor/transactor-source.ts +170 -172
- package/src/transform/atomic.ts +6 -0
- package/src/transform/base-pins.ts +83 -0
- package/src/transform/digest.ts +14 -19
- package/src/transform/tracker.ts +108 -15
|
@@ -1,91 +1,141 @@
|
|
|
1
|
-
import type { CollectionId } from "../collection/index.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Thrown by {@link TransactionCoordinator.commit} when a multi-collection commit
|
|
5
|
-
* fails AFTER at least one collection has already DURABLY committed through the
|
|
6
|
-
* distributed consensus path (GATHER/PEND/COMMIT).
|
|
7
|
-
*
|
|
8
|
-
* ## Why this exists (and why we can't just "roll back")
|
|
9
|
-
*
|
|
10
|
-
* The COMMIT phase commits each collection's pended blocks independently (see
|
|
11
|
-
* `commitPhase`). A per-collection commit can *permanently* fail — e.g. a racing
|
|
12
|
-
* transaction advanced that collection's log tail between PEND and COMMIT (a stale
|
|
13
|
-
* loss) — while the other collections commit successfully. Those durable commits
|
|
14
|
-
* are per-collection and there is no cross-collection undo, so a failure on one
|
|
15
|
-
* collection cannot un-commit the ones that already landed.
|
|
16
|
-
*
|
|
17
|
-
* Uniformly restoring every collection's pre-commit local state (as a clean
|
|
18
|
-
* rollback would) is exactly wrong here: for a collection that DID durably commit
|
|
19
|
-
* it would re-stage its already-durable actions as still-pending, making local
|
|
20
|
-
* tracker memory disagree with cluster storage. Instead the coordinator gives the
|
|
21
|
-
* committed collections the success-path local treatment (fold to cache + reset)
|
|
22
|
-
* and only reverts the failed/never-committed collections, then surfaces THIS error
|
|
23
|
-
* naming both sets so the caller knows reconciliation is required and does NOT
|
|
24
|
-
* falsely report a clean rollback.
|
|
25
|
-
*
|
|
26
|
-
* This is the session-mode / distributed-consensus analog of the plugin's legacy
|
|
27
|
-
* `PartialCommitError` (single-node, per-tree `sync()`).
|
|
28
|
-
*
|
|
29
|
-
* ## The design decision is settled (not "still open")
|
|
30
|
-
*
|
|
31
|
-
* The default multi-collection guarantee is formally **atomicity of intent + eventual,
|
|
32
|
-
* reported visibility**, NOT all-or-nothing — see `docs/correctness.md` **Theorem 3** and
|
|
33
|
-
* `docs/transactions.md` (§ "Session-mode (distributed) commit is not atomic across
|
|
34
|
-
* collections"). This error IS that guarantee's reporting surface, not a placeholder for a
|
|
35
|
-
* stronger one. Genuine cross-collection all-or-nothing is a future opt-in strong mode
|
|
36
|
-
* (backlog `feat-cross-collection-atomic-commit`).
|
|
37
|
-
*
|
|
38
|
-
* ## Reconcile contract for the catcher
|
|
39
|
-
*
|
|
40
|
-
* A caller receiving this error MUST NOT blindly retry the whole transaction and MUST NOT
|
|
41
|
-
* treat it as a clean abort: `committedCollections` are durable and cannot be rolled back,
|
|
42
|
-
* so a whole-transaction retry would double-apply them. Reconcile the named committed set
|
|
43
|
-
* against `failedCollections` (re-drive only the failed collections, or repair the split).
|
|
44
|
-
*/
|
|
45
|
-
export class CoordinatorPartialCommitError extends Error {
|
|
46
|
-
constructor(
|
|
47
|
-
/** Collections durably committed via consensus before the failure (NOT rolled back). */
|
|
48
|
-
public readonly committedCollections: readonly CollectionId[],
|
|
49
|
-
/** Collections that never committed this attempt (local state reverted for retry). */
|
|
50
|
-
public readonly failedCollections: readonly CollectionId[],
|
|
51
|
-
/** The underlying commit-phase failure that aborted the commit. */
|
|
52
|
-
public readonly reason?: unknown,
|
|
53
|
-
) {
|
|
54
|
-
super(
|
|
55
|
-
`Multi-collection commit was not atomic: ${committedCollections.length} collection(s) ` +
|
|
56
|
-
`durably committed via distributed consensus before the commit failed and CANNOT be ` +
|
|
57
|
-
`rolled back — reconciliation is required. ` +
|
|
58
|
-
`Committed (durable, now out of sync with the failed collections): [${committedCollections.join(', ')}]. ` +
|
|
59
|
-
`Failed (never committed; local state reverted for retry): [${failedCollections.join(', ')}]. ` +
|
|
60
|
-
`Underlying failure: ${reason instanceof Error ? reason.message : String(reason)}`
|
|
61
|
-
);
|
|
62
|
-
this.name = 'CoordinatorPartialCommitError';
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Thrown by {@link TransactionCoordinator.commit} when a multi-collection commit failed as a
|
|
68
|
-
* CLEAN stale loss — an optimistic-concurrency conflict (a racing transaction advanced a log tail)
|
|
69
|
-
* in which NOTHING durably committed, so every participating collection's local tracker was
|
|
70
|
-
* restored to its pre-append state and the transaction is safe to re-drive.
|
|
71
|
-
*
|
|
72
|
-
* This is the retryable counterpart to {@link CoordinatorPartialCommitError}: a partial landing
|
|
73
|
-
* cannot be blindly retried (it would double-apply the durable half), but a clean loss can. The
|
|
74
|
-
* coordinator's built-in backoff+jitter retry catches this internally and re-drives after re-reading
|
|
75
|
-
* fresh revisions; it only escapes to the caller once the retry budget (`maxAttempts` / `deadlineMs`)
|
|
76
|
-
* is exhausted, at which point it signals "gave up after a clean loss" rather than a partial split.
|
|
77
|
-
*/
|
|
78
|
-
export class CoordinatorStaleLossError extends Error {
|
|
79
|
-
constructor(
|
|
80
|
-
/** Collections that lost the race this attempt (all had their local state reverted for retry). */
|
|
81
|
-
public readonly failedCollections: readonly CollectionId[],
|
|
82
|
-
/** The underlying stale/conflict reason surfaced by the failed pend/commit phase. */
|
|
83
|
-
public readonly reason?: string,
|
|
84
|
-
) {
|
|
85
|
-
super(
|
|
86
|
-
`Multi-collection commit failed on a clean stale loss (no collection durably committed) ` +
|
|
87
|
-
`for [${failedCollections.join(', ')}]` + (reason ? ` — ${reason}` : '')
|
|
88
|
-
);
|
|
89
|
-
this.name = 'CoordinatorStaleLossError';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
1
|
+
import type { CollectionId } from "../collection/index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thrown by {@link TransactionCoordinator.commit} when a multi-collection commit
|
|
5
|
+
* fails AFTER at least one collection has already DURABLY committed through the
|
|
6
|
+
* distributed consensus path (GATHER/PEND/COMMIT).
|
|
7
|
+
*
|
|
8
|
+
* ## Why this exists (and why we can't just "roll back")
|
|
9
|
+
*
|
|
10
|
+
* The COMMIT phase commits each collection's pended blocks independently (see
|
|
11
|
+
* `commitPhase`). A per-collection commit can *permanently* fail — e.g. a racing
|
|
12
|
+
* transaction advanced that collection's log tail between PEND and COMMIT (a stale
|
|
13
|
+
* loss) — while the other collections commit successfully. Those durable commits
|
|
14
|
+
* are per-collection and there is no cross-collection undo, so a failure on one
|
|
15
|
+
* collection cannot un-commit the ones that already landed.
|
|
16
|
+
*
|
|
17
|
+
* Uniformly restoring every collection's pre-commit local state (as a clean
|
|
18
|
+
* rollback would) is exactly wrong here: for a collection that DID durably commit
|
|
19
|
+
* it would re-stage its already-durable actions as still-pending, making local
|
|
20
|
+
* tracker memory disagree with cluster storage. Instead the coordinator gives the
|
|
21
|
+
* committed collections the success-path local treatment (fold to cache + reset)
|
|
22
|
+
* and only reverts the failed/never-committed collections, then surfaces THIS error
|
|
23
|
+
* naming both sets so the caller knows reconciliation is required and does NOT
|
|
24
|
+
* falsely report a clean rollback.
|
|
25
|
+
*
|
|
26
|
+
* This is the session-mode / distributed-consensus analog of the plugin's legacy
|
|
27
|
+
* `PartialCommitError` (single-node, per-tree `sync()`).
|
|
28
|
+
*
|
|
29
|
+
* ## The design decision is settled (not "still open")
|
|
30
|
+
*
|
|
31
|
+
* The default multi-collection guarantee is formally **atomicity of intent + eventual,
|
|
32
|
+
* reported visibility**, NOT all-or-nothing — see `docs/correctness.md` **Theorem 3** and
|
|
33
|
+
* `docs/transactions.md` (§ "Session-mode (distributed) commit is not atomic across
|
|
34
|
+
* collections"). This error IS that guarantee's reporting surface, not a placeholder for a
|
|
35
|
+
* stronger one. Genuine cross-collection all-or-nothing is a future opt-in strong mode
|
|
36
|
+
* (backlog `feat-cross-collection-atomic-commit`).
|
|
37
|
+
*
|
|
38
|
+
* ## Reconcile contract for the catcher
|
|
39
|
+
*
|
|
40
|
+
* A caller receiving this error MUST NOT blindly retry the whole transaction and MUST NOT
|
|
41
|
+
* treat it as a clean abort: `committedCollections` are durable and cannot be rolled back,
|
|
42
|
+
* so a whole-transaction retry would double-apply them. Reconcile the named committed set
|
|
43
|
+
* against `failedCollections` (re-drive only the failed collections, or repair the split).
|
|
44
|
+
*/
|
|
45
|
+
export class CoordinatorPartialCommitError extends Error {
|
|
46
|
+
constructor(
|
|
47
|
+
/** Collections durably committed via consensus before the failure (NOT rolled back). */
|
|
48
|
+
public readonly committedCollections: readonly CollectionId[],
|
|
49
|
+
/** Collections that never committed this attempt (local state reverted for retry). */
|
|
50
|
+
public readonly failedCollections: readonly CollectionId[],
|
|
51
|
+
/** The underlying commit-phase failure that aborted the commit. */
|
|
52
|
+
public readonly reason?: unknown,
|
|
53
|
+
) {
|
|
54
|
+
super(
|
|
55
|
+
`Multi-collection commit was not atomic: ${committedCollections.length} collection(s) ` +
|
|
56
|
+
`durably committed via distributed consensus before the commit failed and CANNOT be ` +
|
|
57
|
+
`rolled back — reconciliation is required. ` +
|
|
58
|
+
`Committed (durable, now out of sync with the failed collections): [${committedCollections.join(', ')}]. ` +
|
|
59
|
+
`Failed (never committed; local state reverted for retry): [${failedCollections.join(', ')}]. ` +
|
|
60
|
+
`Underlying failure: ${reason instanceof Error ? reason.message : String(reason)}`
|
|
61
|
+
);
|
|
62
|
+
this.name = 'CoordinatorPartialCommitError';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Thrown by {@link TransactionCoordinator.commit} when a multi-collection commit failed as a
|
|
68
|
+
* CLEAN stale loss — an optimistic-concurrency conflict (a racing transaction advanced a log tail)
|
|
69
|
+
* in which NOTHING durably committed, so every participating collection's local tracker was
|
|
70
|
+
* restored to its pre-append state and the transaction is safe to re-drive.
|
|
71
|
+
*
|
|
72
|
+
* This is the retryable counterpart to {@link CoordinatorPartialCommitError}: a partial landing
|
|
73
|
+
* cannot be blindly retried (it would double-apply the durable half), but a clean loss can. The
|
|
74
|
+
* coordinator's built-in backoff+jitter retry catches this internally and re-drives after re-reading
|
|
75
|
+
* fresh revisions; it only escapes to the caller once the retry budget (`maxAttempts` / `deadlineMs`)
|
|
76
|
+
* is exhausted, at which point it signals "gave up after a clean loss" rather than a partial split.
|
|
77
|
+
*/
|
|
78
|
+
export class CoordinatorStaleLossError extends Error {
|
|
79
|
+
constructor(
|
|
80
|
+
/** Collections that lost the race this attempt (all had their local state reverted for retry). */
|
|
81
|
+
public readonly failedCollections: readonly CollectionId[],
|
|
82
|
+
/** The underlying stale/conflict reason surfaced by the failed pend/commit phase. */
|
|
83
|
+
public readonly reason?: string,
|
|
84
|
+
) {
|
|
85
|
+
super(
|
|
86
|
+
`Multi-collection commit failed on a clean stale loss (no collection durably committed) ` +
|
|
87
|
+
`for [${failedCollections.join(', ')}]` + (reason ? ` — ${reason}` : '')
|
|
88
|
+
);
|
|
89
|
+
this.name = 'CoordinatorStaleLossError';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Thrown by {@link TransactionCoordinator.applyActions} and {@link TransactionCoordinator.commit}
|
|
95
|
+
* (and returned as a failure result by {@link TransactionCoordinator.execute}) when a second
|
|
96
|
+
* transaction stamp is opened while the coordinator already tracks an open stamp.
|
|
97
|
+
*
|
|
98
|
+
* ## Why the coordinator refuses instead of isolating
|
|
99
|
+
*
|
|
100
|
+
* The coordinator's registered collections hold exactly ONE staged state — one set of tracker
|
|
101
|
+
* transforms and one pending action queue per collection, shared by every open stamp. Two stamps
|
|
102
|
+
* staging through one coordinator therefore read each other's uncommitted rows, and committing
|
|
103
|
+
* either stamp writes the OTHER's staged actions into its own durable log entry (a commit builds
|
|
104
|
+
* each log entry from the collection's whole pending queue, and the pended transforms carry no
|
|
105
|
+
* stamp tag to filter by). There is no correct commit for that configuration, so the coordinator
|
|
106
|
+
* enforces at most one open stamp at a time.
|
|
107
|
+
*
|
|
108
|
+
* ## Recovery
|
|
109
|
+
*
|
|
110
|
+
* Commit or roll back the open stamp first. A CLEAN commit failure deliberately keeps its stamp
|
|
111
|
+
* open so `rollback(stampId)` remains a complete recovery — which means an abandoned failed
|
|
112
|
+
* commit holds the coordinator against new stamps until someone rolls it back.
|
|
113
|
+
*
|
|
114
|
+
* Callers that genuinely need concurrent writers must give each writer its own `Collection`
|
|
115
|
+
* instances — its own bridge and coordinator (see docs/transactions.md, "One writer at a time on
|
|
116
|
+
* the shared TransactionBridge"). Two configurations stay the caller's contract to avoid because
|
|
117
|
+
* this guard cannot see them: two coordinators sharing the same collection instances, and two
|
|
118
|
+
* writers that stage only via `Tree.stage` / `Collection.act` (which open no stamp at all).
|
|
119
|
+
*
|
|
120
|
+
* A stamp opens at its first {@link TransactionCoordinator.applyActions} call. On the Quereus
|
|
121
|
+
* path that is the pre-stage-barrier call the bridge makes at the first STATEMENT — so a second
|
|
122
|
+
* bridge sharing this coordinator fails at its first statement, not at BEGIN.
|
|
123
|
+
*/
|
|
124
|
+
export class CoordinatorConcurrentStampError extends Error {
|
|
125
|
+
constructor(
|
|
126
|
+
/** The stamp already open on this coordinator. */
|
|
127
|
+
public readonly openStampId: string,
|
|
128
|
+
/** The stamp that was refused. */
|
|
129
|
+
public readonly rejectedStampId: string,
|
|
130
|
+
) {
|
|
131
|
+
super(
|
|
132
|
+
`Transaction stamp ${rejectedStampId} refused: this coordinator already tracks open stamp ` +
|
|
133
|
+
`${openStampId}, and its collections hold only one staged state, so two concurrent stamps ` +
|
|
134
|
+
`cannot be kept apart. Commit or roll back stamp ${openStampId} first — a failed commit ` +
|
|
135
|
+
`keeps its stamp open until it is rolled back. Concurrent writers each need their own ` +
|
|
136
|
+
`Collection instances (own bridge/coordinator per writer). A stamp opens at its first ` +
|
|
137
|
+
`applied statement (the pre-stage applyActions barrier), not at BEGIN.`
|
|
138
|
+
);
|
|
139
|
+
this.name = 'CoordinatorConcurrentStampError';
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/transaction/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export {
|
|
|
34
34
|
|
|
35
35
|
export { ReadDependencyCollector } from './read-dependency-collector.js';
|
|
36
36
|
export { TransactionCoordinator } from './coordinator.js';
|
|
37
|
-
export { CoordinatorPartialCommitError, CoordinatorStaleLossError } from './errors.js';
|
|
37
|
+
export { CoordinatorConcurrentStampError, CoordinatorPartialCommitError, CoordinatorStaleLossError } from './errors.js';
|
|
38
38
|
export { TransactionSession } from './session.js';
|
|
39
39
|
export { TransactionValidator } from './validator.js';
|
|
40
40
|
export type { EngineRegistration, ValidationCoordinatorFactory, BlockStateProvider } from './validator.js';
|
|
@@ -177,9 +177,13 @@ export class TransactionSession {
|
|
|
177
177
|
/**
|
|
178
178
|
* Rollback the transaction (undo this session's applied actions).
|
|
179
179
|
*
|
|
180
|
-
* Delegates to coordinator.rollback(stampId) which restores collection
|
|
181
|
-
*
|
|
182
|
-
*
|
|
180
|
+
* Delegates to coordinator.rollback(stampId), which restores each collection's staged
|
|
181
|
+
* state — tracker transforms AND the pending action queue — to the pre-session snapshot.
|
|
182
|
+
* That restore is coordinator-wide rather than session-scoped, which is safe because the
|
|
183
|
+
* coordinator permits at most one open stamp: there is no concurrent session whose staged
|
|
184
|
+
* state it could discard. Restoring both halves is what keeps this session's actions out
|
|
185
|
+
* of the next transaction's durable log entry; see the snapshot contract on
|
|
186
|
+
* TransactionCoordinator.stampData.
|
|
183
187
|
*/
|
|
184
188
|
async rollback(): Promise<void> {
|
|
185
189
|
if (this.committed) {
|
|
@@ -342,8 +342,18 @@ export type ExecutionResult = {
|
|
|
342
342
|
* On a PARTIAL multi-collection commit failure, the collections that DID durably
|
|
343
343
|
* commit through consensus before the failure (and thus CANNOT be rolled back —
|
|
344
344
|
* reconciliation is required). Absent/empty means nothing durably committed, so
|
|
345
|
-
* the caller may treat the failure as a clean abort
|
|
346
|
-
*
|
|
345
|
+
* the caller may treat the failure as a clean abort — including calling
|
|
346
|
+
* `coordinator.rollback(stampId)`, which is still a valid and complete recovery there.
|
|
347
|
+
*
|
|
348
|
+
* When this IS present, the coordinator has already given the committed collections
|
|
349
|
+
* the success-path local treatment and restored the failed ones to their pre-staging
|
|
350
|
+
* state, and has DROPPED the transaction's rollback tracking — so
|
|
351
|
+
* `coordinator.rollback(stampId)` is a deliberate no-op rather than a rewind that
|
|
352
|
+
* would corrupt the collections that did commit. Reconciliation means driving a NEW
|
|
353
|
+
* transaction naming only the failed collections; re-driving THIS transaction through
|
|
354
|
+
* `execute()` would re-stage and re-apply the committed half a second time.
|
|
355
|
+
*
|
|
356
|
+
* See {@link CoordinatorPartialCommitError} for the session-mode (commit) analog.
|
|
347
357
|
*/
|
|
348
358
|
committedCollections?: CollectionId[];
|
|
349
359
|
/** On a partial multi-collection commit failure, the collections that failed to commit. */
|