@optimystic/db-p2p 1.0.0 → 1.2.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/cluster/cluster-repo.d.ts +58 -9
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +113 -21
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +100 -31
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +288 -192
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +2 -0
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +7 -5
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +24 -6
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/package.json +2 -2
- package/src/cluster/cluster-repo.ts +116 -21
- package/src/repo/cluster-coordinator.ts +1409 -1296
- package/src/repo/coordinator-repo.ts +9 -5
- package/src/storage/storage-repo.ts +1781 -1762
|
@@ -182,6 +182,7 @@ export declare class ClusterMember implements ICluster {
|
|
|
182
182
|
private executedTransactions;
|
|
183
183
|
private executedPendResults;
|
|
184
184
|
private executedCommitResults;
|
|
185
|
+
private behindCommitRefusals;
|
|
185
186
|
private refusedPendActions;
|
|
186
187
|
private appliedInvalidations;
|
|
187
188
|
/**
|
|
@@ -276,12 +277,13 @@ export declare class ClusterMember implements ICluster {
|
|
|
276
277
|
update(record: ClusterRecord): Promise<ClusterRecord>;
|
|
277
278
|
private processUpdate;
|
|
278
279
|
/**
|
|
279
|
-
* Add this member's own apply
|
|
280
|
-
*
|
|
281
|
-
* {@link MemberApplyOutcome}:
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
280
|
+
* Add this member's own apply report to a record's {@link ClusterRecord.applyOutcomes}, when it
|
|
281
|
+
* has anything to report for this transaction. The fields follow the rules on
|
|
282
|
+
* {@link MemberApplyOutcome}: `executed` is set once this member has run the consensus apply; the
|
|
283
|
+
* pend arm carries ONLY a conflict-shaped refusal (a success, a bare-reason fault, or no retained
|
|
284
|
+
* verdict leaves it off — the coordinator's rule is "an entry means retry", so an entry that does
|
|
285
|
+
* not mean retry must not exist); the commit arm carries the retained durable verdict whatever it
|
|
286
|
+
* says, because the coordinator counts the successes.
|
|
285
287
|
*
|
|
286
288
|
* The member never signs this and never writes another peer's entry.
|
|
287
289
|
*/
|
|
@@ -658,6 +660,16 @@ export declare class ClusterMember implements ICluster {
|
|
|
658
660
|
* @see docs/internals.md "Check-Then-Act Race in Consensus" and "Independent Node Storage" pitfalls
|
|
659
661
|
*/
|
|
660
662
|
private handleConsensus;
|
|
663
|
+
/**
|
|
664
|
+
* A delivery of a record this member already applied. Nothing is applied again, but a retained
|
|
665
|
+
* behind-shaped commit refusal gets one more reconcile ({@link reconcileRefusedCommit}, a no-op for
|
|
666
|
+
* every other verdict). This is how a behind remote member heals when it applied on receipt of the
|
|
667
|
+
* commit round, before the coordinating member held the revision: the coordinator re-sends it the
|
|
668
|
+
* merged record once its own member has applied, and this delivery's response carries the
|
|
669
|
+
* refreshed verdict (`withOwnApplyOutcome`), which the durability gate reads. A scheduled commit
|
|
670
|
+
* retry reaching such a member heals it the same way.
|
|
671
|
+
*/
|
|
672
|
+
private handleAlreadyExecuted;
|
|
661
673
|
/**
|
|
662
674
|
* Applies one consensus-approved operation to local storage.
|
|
663
675
|
*
|
|
@@ -709,12 +721,39 @@ export declare class ClusterMember implements ICluster {
|
|
|
709
721
|
/**
|
|
710
722
|
* Apply one consensus commit to local storage, tolerating every divergence shape the way the
|
|
711
723
|
* doc comment on {@link applyConsensusOperation} describes and reconciling the behind ones.
|
|
712
|
-
*
|
|
724
|
+
* `applied` is storage's own result — or, for the thrown missing-pend shape, which produces no
|
|
713
725
|
* `CommitResult` at all, a refusal built from the throw — so {@link durableCommitVerdict} works
|
|
714
|
-
* from one uniform shape.
|
|
715
|
-
*
|
|
726
|
+
* from one uniform shape. `behind` is true exactly when one of the two behind branches ran, which
|
|
727
|
+
* is what makes a refusal eligible for {@link reconcileRefusedCommit}. Genuine faults (a
|
|
728
|
+
* bare-reason returned failure, an unrecognized throw) propagate so {@link handleConsensus} rolls
|
|
729
|
+
* back the executed marker and rethrows.
|
|
716
730
|
*/
|
|
717
731
|
private applyCommitToStorage;
|
|
732
|
+
/**
|
|
733
|
+
* Retain `verdict` as this member's durable verdict for `record`'s commit (see
|
|
734
|
+
* {@link executedCommitResults}), remember whether a refusal was the behind shape, and — once the
|
|
735
|
+
* verdict is a success — report the committed holders. Shared by the consensus apply and
|
|
736
|
+
* {@link reconcileRefusedCommit} so both leave the same state behind.
|
|
737
|
+
*/
|
|
738
|
+
private retainCommitVerdict;
|
|
739
|
+
/**
|
|
740
|
+
* One more reconcile for a commit this member already applied at consensus but could not hold:
|
|
741
|
+
* its retained verdict is a BEHIND-shaped refusal (missing pend or missing base), and the
|
|
742
|
+
* reconcile that ran during the apply found no cohort peer holding the revision yet. Two callers.
|
|
743
|
+
* The coordinating node calls it for its own member once a remote member reports holding the
|
|
744
|
+
* revision (`ClusterCoordinator.broadcastMergedRecord`), for a cohort where the remote members
|
|
745
|
+
* applied after it did. And every member runs it on a redelivery of a record it already applied
|
|
746
|
+
* ({@link handleAlreadyExecuted}), which is how a remote member that applied on receipt of the
|
|
747
|
+
* commit round, before anyone held the revision, heals once the coordinator re-sends it the merged
|
|
748
|
+
* record. The holder's copy carries the cohort's commit proof, so the existing certified
|
|
749
|
+
* single-holder path adopts it; no new trust is involved.
|
|
750
|
+
*
|
|
751
|
+
* Does nothing unless the retained verdict for `record.messageHash` is a behind refusal; in
|
|
752
|
+
* particular an ahead-shaped refusal (`missing`) is never reconciled downward. Recomputes and
|
|
753
|
+
* re-retains the durable verdict afterwards, so `getExecutedCommitResult` then answers a success
|
|
754
|
+
* when the revision landed. Bounded by the same per-block reconcile timeout and never throws.
|
|
755
|
+
*/
|
|
756
|
+
reconcileRefusedCommit(record: ClusterRecord): Promise<void>;
|
|
718
757
|
/**
|
|
719
758
|
* The durable verdict for a consensus commit: `applied` itself when storage landed it (a
|
|
720
759
|
* success from `StorageRepo.commit` means every block is committed, or already was, under this
|
|
@@ -754,6 +793,16 @@ export declare class ClusterMember implements ICluster {
|
|
|
754
793
|
* **synchronous** (no `await` here) so the cert is retained before `StorageRepo.commit` emits its
|
|
755
794
|
* change event. No-op (zero cost) when no sink is configured; a throwing sink is isolated + logged so
|
|
756
795
|
* it can never break consensus.
|
|
796
|
+
*
|
|
797
|
+
* A record carrying fewer approving commit signatures than `minSigs` retains no cert, so the bridge
|
|
798
|
+
* skips origination on this member rather than forwarding a cert below its own threshold. Consensus
|
|
799
|
+
* needs only a simple majority of commits, so a member can apply with fewer.
|
|
800
|
+
*
|
|
801
|
+
* NOTE: in a three-member cohort that is the ordinary case for the two remote members. Each applies
|
|
802
|
+
* on receipt of the commit round holding its own commit and the coordinator's (2 of `minSigs` 3), and
|
|
803
|
+
* the coordinator does not broadcast the full set to a member that already applied, so only the
|
|
804
|
+
* coordinating member, which applies with every commit merged, originates. If origination from the
|
|
805
|
+
* other members is ever needed, have the coordinator deliver the merged record to them too.
|
|
757
806
|
*/
|
|
758
807
|
private captureCommitCert;
|
|
759
808
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster-repo.d.ts","sourceRoot":"","sources":["../../../src/cluster/cluster-repo.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cluster-repo.d.ts","sourceRoot":"","sources":["../../../src/cluster/cluster-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -134,6 +134,12 @@ export class ClusterMember {
|
|
|
134
134
|
// applyConsensusOperation swallowed a refusal whose real cause was a RIVAL action holding the
|
|
135
135
|
// requested revision. Pruned alongside executedTransactions (same TTL).
|
|
136
136
|
executedCommitResults = new Map();
|
|
137
|
+
// The messageHashes whose retained commit refusal above has the BEHIND shape (missing pend or
|
|
138
|
+
// missing base — this member held no usable revision), as opposed to the ahead shape, which must
|
|
139
|
+
// never be reconciled downward. Recorded from the apply branch that produced the refusal rather
|
|
140
|
+
// than read back from its prose. Only these are eligible for reconcileRefusedCommit. Same TTL,
|
|
141
|
+
// pruning and rollback as executedCommitResults.
|
|
142
|
+
behindCommitRefusals = new Set();
|
|
137
143
|
// Conflict-shaped pend refusals this member's storage produced at consensus-apply, keyed by the
|
|
138
144
|
// refused action's id rather than by messageHash. The messageHash-keyed map above cannot serve the
|
|
139
145
|
// commit-promise guard: a commit is a DIFFERENT message with a different hash, so a member holding
|
|
@@ -256,6 +262,7 @@ export class ClusterMember {
|
|
|
256
262
|
this.cleanupQueue.length = 0;
|
|
257
263
|
this.executedPendResults.clear();
|
|
258
264
|
this.executedCommitResults.clear();
|
|
265
|
+
this.behindCommitRefusals.clear();
|
|
259
266
|
this.refusedPendActions.clear();
|
|
260
267
|
}
|
|
261
268
|
/**
|
|
@@ -519,12 +526,13 @@ export class ClusterMember {
|
|
|
519
526
|
return currentRecord;
|
|
520
527
|
}
|
|
521
528
|
/**
|
|
522
|
-
* Add this member's own apply
|
|
523
|
-
*
|
|
524
|
-
* {@link MemberApplyOutcome}:
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
529
|
+
* Add this member's own apply report to a record's {@link ClusterRecord.applyOutcomes}, when it
|
|
530
|
+
* has anything to report for this transaction. The fields follow the rules on
|
|
531
|
+
* {@link MemberApplyOutcome}: `executed` is set once this member has run the consensus apply; the
|
|
532
|
+
* pend arm carries ONLY a conflict-shaped refusal (a success, a bare-reason fault, or no retained
|
|
533
|
+
* verdict leaves it off — the coordinator's rule is "an entry means retry", so an entry that does
|
|
534
|
+
* not mean retry must not exist); the commit arm carries the retained durable verdict whatever it
|
|
535
|
+
* says, because the coordinator counts the successes.
|
|
528
536
|
*
|
|
529
537
|
* The member never signs this and never writes another peer's entry.
|
|
530
538
|
*/
|
|
@@ -532,6 +540,7 @@ export class ClusterMember {
|
|
|
532
540
|
const pend = this.executedPendResults.get(record.messageHash);
|
|
533
541
|
const commit = this.executedCommitResults.get(record.messageHash);
|
|
534
542
|
const own = {
|
|
543
|
+
...(this.executedTransactions.has(record.messageHash) ? { executed: true } : {}),
|
|
535
544
|
...(pend !== undefined && !pend.success && isConflictFailure(pend) ? { pend } : {}),
|
|
536
545
|
...(commit !== undefined ? { commit } : {})
|
|
537
546
|
};
|
|
@@ -1968,14 +1977,14 @@ export class ClusterMember {
|
|
|
1968
1977
|
// Check persistent store first for post-recovery dedup (in-memory map is cleared on restart).
|
|
1969
1978
|
// wasTransactionExecutedAsync also checks the in-memory map as a fast path.
|
|
1970
1979
|
if (await this.wasTransactionExecutedAsync(record.messageHash)) {
|
|
1971
|
-
|
|
1980
|
+
await this.handleAlreadyExecuted(record);
|
|
1972
1981
|
return;
|
|
1973
1982
|
}
|
|
1974
1983
|
// Check-and-set ATOMICALLY to prevent race condition where multiple calls
|
|
1975
1984
|
// pass the async check before any completes. Since JavaScript is single-threaded,
|
|
1976
1985
|
// this synchronous check-and-set is atomic before any await.
|
|
1977
1986
|
if (this.executedTransactions.has(record.messageHash)) {
|
|
1978
|
-
|
|
1987
|
+
await this.handleAlreadyExecuted(record);
|
|
1979
1988
|
return;
|
|
1980
1989
|
}
|
|
1981
1990
|
// Set the in-memory guard IMMEDIATELY, before any async operations: its synchronous
|
|
@@ -2002,6 +2011,7 @@ export class ClusterMember {
|
|
|
2002
2011
|
this.executedTransactions.delete(record.messageHash);
|
|
2003
2012
|
this.executedPendResults.delete(record.messageHash);
|
|
2004
2013
|
this.executedCommitResults.delete(record.messageHash);
|
|
2014
|
+
this.behindCommitRefusals.delete(record.messageHash);
|
|
2005
2015
|
for (const operation of record.message.operations) {
|
|
2006
2016
|
if ('pend' in operation)
|
|
2007
2017
|
this.refusedPendActions.delete(operation.pend.actionId);
|
|
@@ -2021,6 +2031,19 @@ export class ClusterMember {
|
|
|
2021
2031
|
this.stateStore?.markExecuted(record.messageHash, executedAt)
|
|
2022
2032
|
.catch(err => log('cluster-member:persist-executed-error', { messageHash: record.messageHash, error: err.message }));
|
|
2023
2033
|
}
|
|
2034
|
+
/**
|
|
2035
|
+
* A delivery of a record this member already applied. Nothing is applied again, but a retained
|
|
2036
|
+
* behind-shaped commit refusal gets one more reconcile ({@link reconcileRefusedCommit}, a no-op for
|
|
2037
|
+
* every other verdict). This is how a behind remote member heals when it applied on receipt of the
|
|
2038
|
+
* commit round, before the coordinating member held the revision: the coordinator re-sends it the
|
|
2039
|
+
* merged record once its own member has applied, and this delivery's response carries the
|
|
2040
|
+
* refreshed verdict (`withOwnApplyOutcome`), which the durability gate reads. A scheduled commit
|
|
2041
|
+
* retry reaching such a member heals it the same way.
|
|
2042
|
+
*/
|
|
2043
|
+
async handleAlreadyExecuted(record) {
|
|
2044
|
+
log('cluster-member:consensus-already-executed', { messageHash: record.messageHash });
|
|
2045
|
+
await this.reconcileRefusedCommit(record);
|
|
2046
|
+
}
|
|
2024
2047
|
/**
|
|
2025
2048
|
* Applies one consensus-approved operation to local storage.
|
|
2026
2049
|
*
|
|
@@ -2149,7 +2172,7 @@ export class ClusterMember {
|
|
|
2149
2172
|
membershipVersion: record.membershipVersion
|
|
2150
2173
|
});
|
|
2151
2174
|
}
|
|
2152
|
-
const applied = await this.applyCommitToStorage(record, commit, proof);
|
|
2175
|
+
const { applied, behind } = await this.applyCommitToStorage(record, commit, proof);
|
|
2153
2176
|
// Retain the POST-RECONCILE durable verdict (see getExecutedCommitResult and
|
|
2154
2177
|
// withOwnApplyOutcome): what this member's storage holds NOW, after any behind-reconcile the
|
|
2155
2178
|
// apply triggered — not what the first apply attempt said. A member that pulled the
|
|
@@ -2158,10 +2181,7 @@ export class ClusterMember {
|
|
|
2158
2181
|
// verdicts — its own member's through getExecutedCommitResult, every other member's off the
|
|
2159
2182
|
// response record — and the two readers must see the same answer, hence one verdict,
|
|
2160
2183
|
// computed once, retained here for both.
|
|
2161
|
-
|
|
2162
|
-
this.executedCommitResults.set(messageHash, verdict);
|
|
2163
|
-
if (verdict.success)
|
|
2164
|
-
this.reportCommittedHolders(record, commit);
|
|
2184
|
+
this.retainCommitVerdict(record, commit, await this.durableCommitVerdict(commit, applied), behind);
|
|
2165
2185
|
return;
|
|
2166
2186
|
}
|
|
2167
2187
|
if ('invalidate' in operation) {
|
|
@@ -2172,10 +2192,12 @@ export class ClusterMember {
|
|
|
2172
2192
|
/**
|
|
2173
2193
|
* Apply one consensus commit to local storage, tolerating every divergence shape the way the
|
|
2174
2194
|
* doc comment on {@link applyConsensusOperation} describes and reconciling the behind ones.
|
|
2175
|
-
*
|
|
2195
|
+
* `applied` is storage's own result — or, for the thrown missing-pend shape, which produces no
|
|
2176
2196
|
* `CommitResult` at all, a refusal built from the throw — so {@link durableCommitVerdict} works
|
|
2177
|
-
* from one uniform shape.
|
|
2178
|
-
*
|
|
2197
|
+
* from one uniform shape. `behind` is true exactly when one of the two behind branches ran, which
|
|
2198
|
+
* is what makes a refusal eligible for {@link reconcileRefusedCommit}. Genuine faults (a
|
|
2199
|
+
* bare-reason returned failure, an unrecognized throw) propagate so {@link handleConsensus} rolls
|
|
2200
|
+
* back the executed marker and rethrows.
|
|
2179
2201
|
*/
|
|
2180
2202
|
async applyCommitToStorage(record, commit, proof) {
|
|
2181
2203
|
const messageHash = record.messageHash;
|
|
@@ -2198,12 +2220,12 @@ export class ClusterMember {
|
|
|
2198
2220
|
// cohort peer so the block is no longer under-replicated. Best-effort:
|
|
2199
2221
|
// failures are logged inside, never thrown (a throw would reset the stream).
|
|
2200
2222
|
await this.reconcileDivergentCommit(record, commit);
|
|
2201
|
-
return { success: false, reason: err.message };
|
|
2223
|
+
return { applied: { success: false, reason: err.message }, behind: true };
|
|
2202
2224
|
}
|
|
2203
2225
|
throw err;
|
|
2204
2226
|
}
|
|
2205
2227
|
if (result.success) {
|
|
2206
|
-
return result;
|
|
2228
|
+
return { applied: result, behind: false };
|
|
2207
2229
|
}
|
|
2208
2230
|
// success:false is a StaleFailure. `missing` ⇒ ahead/stale divergence
|
|
2209
2231
|
// (we already hold ≥ this rev): tolerate, do NOT reconcile downward. A
|
|
@@ -2233,7 +2255,7 @@ export class ClusterMember {
|
|
|
2233
2255
|
reason: result.reason,
|
|
2234
2256
|
hasMissing: true
|
|
2235
2257
|
});
|
|
2236
|
-
return result;
|
|
2258
|
+
return { applied: result, behind: false };
|
|
2237
2259
|
}
|
|
2238
2260
|
// This member holds no materializable base for one of the blocks, so
|
|
2239
2261
|
// `StorageRepo.commit` REFUSED rather than record a revision it could never serve.
|
|
@@ -2249,10 +2271,64 @@ export class ClusterMember {
|
|
|
2249
2271
|
reason: result.reason
|
|
2250
2272
|
});
|
|
2251
2273
|
await this.reconcileDivergentCommit(record, commit);
|
|
2252
|
-
return result;
|
|
2274
|
+
return { applied: result, behind: true };
|
|
2253
2275
|
}
|
|
2254
2276
|
throw new Error(`Consensus commit for action ${commit.actionId} failed: ${result.reason ?? 'unknown reason'}`);
|
|
2255
2277
|
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Retain `verdict` as this member's durable verdict for `record`'s commit (see
|
|
2280
|
+
* {@link executedCommitResults}), remember whether a refusal was the behind shape, and — once the
|
|
2281
|
+
* verdict is a success — report the committed holders. Shared by the consensus apply and
|
|
2282
|
+
* {@link reconcileRefusedCommit} so both leave the same state behind.
|
|
2283
|
+
*/
|
|
2284
|
+
retainCommitVerdict(record, commit, verdict, behind) {
|
|
2285
|
+
this.executedCommitResults.set(record.messageHash, verdict);
|
|
2286
|
+
if (verdict.success) {
|
|
2287
|
+
this.behindCommitRefusals.delete(record.messageHash);
|
|
2288
|
+
this.reportCommittedHolders(record, commit);
|
|
2289
|
+
}
|
|
2290
|
+
else if (behind) {
|
|
2291
|
+
this.behindCommitRefusals.add(record.messageHash);
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* One more reconcile for a commit this member already applied at consensus but could not hold:
|
|
2296
|
+
* its retained verdict is a BEHIND-shaped refusal (missing pend or missing base), and the
|
|
2297
|
+
* reconcile that ran during the apply found no cohort peer holding the revision yet. Two callers.
|
|
2298
|
+
* The coordinating node calls it for its own member once a remote member reports holding the
|
|
2299
|
+
* revision (`ClusterCoordinator.broadcastMergedRecord`), for a cohort where the remote members
|
|
2300
|
+
* applied after it did. And every member runs it on a redelivery of a record it already applied
|
|
2301
|
+
* ({@link handleAlreadyExecuted}), which is how a remote member that applied on receipt of the
|
|
2302
|
+
* commit round, before anyone held the revision, heals once the coordinator re-sends it the merged
|
|
2303
|
+
* record. The holder's copy carries the cohort's commit proof, so the existing certified
|
|
2304
|
+
* single-holder path adopts it; no new trust is involved.
|
|
2305
|
+
*
|
|
2306
|
+
* Does nothing unless the retained verdict for `record.messageHash` is a behind refusal; in
|
|
2307
|
+
* particular an ahead-shaped refusal (`missing`) is never reconciled downward. Recomputes and
|
|
2308
|
+
* re-retains the durable verdict afterwards, so `getExecutedCommitResult` then answers a success
|
|
2309
|
+
* when the revision landed. Bounded by the same per-block reconcile timeout and never throws.
|
|
2310
|
+
*/
|
|
2311
|
+
async reconcileRefusedCommit(record) {
|
|
2312
|
+
const messageHash = record.messageHash;
|
|
2313
|
+
const refused = this.executedCommitResults.get(messageHash);
|
|
2314
|
+
if (refused === undefined || refused.success || !this.behindCommitRefusals.has(messageHash)) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const operation = record.message.operations.find((op) => 'commit' in op);
|
|
2318
|
+
if (operation === undefined) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const commit = operation.commit;
|
|
2322
|
+
log('cluster-member:consensus-commit-reconcile-again', { messageHash, actionId: commit.actionId, rev: commit.rev });
|
|
2323
|
+
await this.reconcileDivergentCommit(record, commit);
|
|
2324
|
+
const verdict = await this.durableCommitVerdict(commit, refused);
|
|
2325
|
+
// The apply's retention may have been pruned or rolled back while the reconcile ran; never
|
|
2326
|
+
// resurrect it.
|
|
2327
|
+
if (this.executedCommitResults.get(messageHash) !== refused) {
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
this.retainCommitVerdict(record, commit, verdict, true);
|
|
2331
|
+
}
|
|
2256
2332
|
/**
|
|
2257
2333
|
* The durable verdict for a consensus commit: `applied` itself when storage landed it (a
|
|
2258
2334
|
* success from `StorageRepo.commit` means every block is committed, or already was, under this
|
|
@@ -2386,6 +2462,16 @@ export class ClusterMember {
|
|
|
2386
2462
|
* **synchronous** (no `await` here) so the cert is retained before `StorageRepo.commit` emits its
|
|
2387
2463
|
* change event. No-op (zero cost) when no sink is configured; a throwing sink is isolated + logged so
|
|
2388
2464
|
* it can never break consensus.
|
|
2465
|
+
*
|
|
2466
|
+
* A record carrying fewer approving commit signatures than `minSigs` retains no cert, so the bridge
|
|
2467
|
+
* skips origination on this member rather than forwarding a cert below its own threshold. Consensus
|
|
2468
|
+
* needs only a simple majority of commits, so a member can apply with fewer.
|
|
2469
|
+
*
|
|
2470
|
+
* NOTE: in a three-member cohort that is the ordinary case for the two remote members. Each applies
|
|
2471
|
+
* on receipt of the commit round holding its own commit and the coordinator's (2 of `minSigs` 3), and
|
|
2472
|
+
* the coordinator does not broadcast the full set to a member that already applied, so only the
|
|
2473
|
+
* coordinating member, which applies with every commit merged, originates. If origination from the
|
|
2474
|
+
* other members is ever needed, have the coordinator deliver the merged record to them too.
|
|
2389
2475
|
*/
|
|
2390
2476
|
captureCommitCert(record, actionId, signedPayload) {
|
|
2391
2477
|
if (!this.onCommitCertificate) {
|
|
@@ -2393,7 +2479,12 @@ export class ClusterMember {
|
|
|
2393
2479
|
}
|
|
2394
2480
|
const minSigs = Math.ceil(Object.keys(record.peers).length * this.superMajorityThreshold);
|
|
2395
2481
|
try {
|
|
2396
|
-
|
|
2482
|
+
const cert = buildCommitCert(record, minSigs, signedPayload);
|
|
2483
|
+
if (cert.signers.length < minSigs) {
|
|
2484
|
+
log('cluster-member:commit-cert-below-threshold', { actionId, signers: cert.signers.length, minSigs });
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
this.onCommitCertificate(actionId, cert);
|
|
2397
2488
|
}
|
|
2398
2489
|
catch (err) {
|
|
2399
2490
|
log('cluster-member:commit-cert-sink-error', { actionId, error: err.message });
|
|
@@ -2619,6 +2710,7 @@ export class ClusterMember {
|
|
|
2619
2710
|
this.executedTransactions.delete(messageHash);
|
|
2620
2711
|
this.executedPendResults.delete(messageHash);
|
|
2621
2712
|
this.executedCommitResults.delete(messageHash);
|
|
2713
|
+
this.behindCommitRefusals.delete(messageHash);
|
|
2622
2714
|
}
|
|
2623
2715
|
}
|
|
2624
2716
|
// Prune actionId-keyed pend refusals on the same TTL as the verdicts they were derived from.
|