@optimystic/db-p2p 1.0.0-beta.2 → 1.0.0-beta.3

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.
Files changed (34) hide show
  1. package/dist/src/cluster/cluster-repo.d.ts +46 -11
  2. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  3. package/dist/src/cluster/cluster-repo.js +177 -101
  4. package/dist/src/cluster/cluster-repo.js.map +1 -1
  5. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  6. package/dist/src/libp2p-key-network.js +7 -0
  7. package/dist/src/libp2p-key-network.js.map +1 -1
  8. package/dist/src/logger.d.ts.map +1 -1
  9. package/dist/src/logger.js +13 -6
  10. package/dist/src/logger.js.map +1 -1
  11. package/dist/src/repo/cluster-coordinator.d.ts +42 -0
  12. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  13. package/dist/src/repo/cluster-coordinator.js +50 -9
  14. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  15. package/dist/src/repo/coordinator-repo.d.ts +74 -7
  16. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  17. package/dist/src/repo/coordinator-repo.js +290 -55
  18. package/dist/src/repo/coordinator-repo.js.map +1 -1
  19. package/dist/src/storage/storage-repo.d.ts +15 -0
  20. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  21. package/dist/src/storage/storage-repo.js +28 -0
  22. package/dist/src/storage/storage-repo.js.map +1 -1
  23. package/dist/src/testing/mesh-harness.d.ts +15 -0
  24. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  25. package/dist/src/testing/mesh-harness.js +21 -4
  26. package/dist/src/testing/mesh-harness.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/cluster/cluster-repo.ts +2749 -2671
  29. package/src/libp2p-key-network.ts +7 -0
  30. package/src/logger.ts +14 -6
  31. package/src/repo/cluster-coordinator.ts +1170 -1113
  32. package/src/repo/coordinator-repo.ts +2940 -2687
  33. package/src/storage/storage-repo.ts +30 -0
  34. package/src/testing/mesh-harness.ts +37 -4
@@ -223,13 +223,15 @@ export declare class ClusterMember implements ICluster {
223
223
  */
224
224
  getExecutedPendResult(messageHash: string): PendResult | undefined;
225
225
  /**
226
- * Commit-shaped sibling of {@link getExecutedPendResult}: local storage's verdict for a commit
227
- * operation applied during consensus, when this member retained one. `CoordinatorRepo.commit`
228
- * consults it after a locally-executed commit-consensus a retained refusal whose cause a local
229
- * re-read confirms as a rival holding the requested revision is returned to the writer as a
230
- * retryable conflict instead of the fabricated success the ahead-divergence tolerance would
231
- * otherwise imply. Same availability caveats as the pend accessor: in-memory only, absent for
232
- * pre-restart applies, pruned on the executed-transaction TTL.
226
+ * Commit-shaped sibling of {@link getExecutedPendResult}: this member's post-reconcile durable
227
+ * verdict for a commit operation applied during consensus, when it retained one `success: true`
228
+ * iff local storage holds the committed revision under the record's action for every block the
229
+ * commit named, after the apply and any behind-reconcile it triggered. `CoordinatorRepo.commit`
230
+ * reads it after a locally-executed commit-consensus, both as this node's own contribution to the
231
+ * durable-holder count its durability gate needs, and for a retained refusal whose cause a
232
+ * local re-read confirms as a rival holding the requested revision — as the trigger for a
233
+ * retryable conflict answer. Same availability caveats as the pend accessor: in-memory only,
234
+ * absent for pre-restart applies, pruned on the executed-transaction TTL.
233
235
  */
234
236
  getExecutedCommitResult(messageHash: string): CommitResult | undefined;
235
237
  /**
@@ -247,10 +249,12 @@ export declare class ClusterMember implements ICluster {
247
249
  update(record: ClusterRecord): Promise<ClusterRecord>;
248
250
  private processUpdate;
249
251
  /**
250
- * Add this member's own conflict-shaped pend verdict to a record's {@link ClusterRecord.applyOutcomes},
251
- * when storage produced one for this transaction. A success, a bare-reason fault, or no retained
252
- * verdict at all leaves the record untouched the coordinator's rule is "an entry means retry",
253
- * so an entry that does not mean retry must not exist.
252
+ * Add this member's own apply verdicts to a record's {@link ClusterRecord.applyOutcomes}, when
253
+ * storage produced any for this transaction. The two arms follow the rules on
254
+ * {@link MemberApplyOutcome}: the pend arm carries ONLY a conflict-shaped refusal (a success, a
255
+ * bare-reason fault, or no retained verdict leaves it off the coordinator's rule is "an entry
256
+ * means retry", so an entry that does not mean retry must not exist); the commit arm carries the
257
+ * retained durable verdict whatever it says, because the coordinator counts the successes.
254
258
  *
255
259
  * The member never signs this and never writes another peer's entry.
256
260
  */
@@ -585,8 +589,39 @@ export declare class ClusterMember implements ICluster {
585
589
  * mid-commit `internalCommit` fault (`success:false` with a bare `reason`, no
586
590
  * `missing`) is propagated so {@link handleConsensus} rolls back the executed marker
587
591
  * and rethrows — exactly like an unexpected thrown fault.
592
+ *
593
+ * Tolerated is not the same as acknowledged. Whatever shape a commit's apply took, this
594
+ * member retains one post-reconcile durable verdict for it ({@link durableCommitVerdict}) and
595
+ * reports it to the coordinator, whose durability gate (`CoordinatorRepo.commit`) acknowledges
596
+ * the writer only when a majority of the cohort reports holding the revision.
588
597
  */
589
598
  private applyConsensusOperation;
599
+ /**
600
+ * Apply one consensus commit to local storage, tolerating every divergence shape the way the
601
+ * doc comment on {@link applyConsensusOperation} describes and reconciling the behind ones.
602
+ * Returns storage's own result — or, for the thrown missing-pend shape, which produces no
603
+ * `CommitResult` at all, a refusal built from the throw — so {@link durableCommitVerdict} works
604
+ * from one uniform shape. Genuine faults (a bare-reason returned failure, an unrecognized throw)
605
+ * propagate so {@link handleConsensus} rolls back the executed marker and rethrows.
606
+ */
607
+ private applyCommitToStorage;
608
+ /**
609
+ * The durable verdict for a consensus commit: `applied` itself when storage landed it (a
610
+ * success from `StorageRepo.commit` means every block is committed, or already was, under this
611
+ * action), otherwise a fresh look at what local storage holds NOW — after the reconcile the
612
+ * refusal triggered. Durable means every block in `commit.blockIds` records `commit.rev` under
613
+ * `commit.actionId`. A refusal that is not durable is returned as-is, so the coordinating node
614
+ * can still classify its own member's ahead-shaped refusal against a rival. A read fault counts
615
+ * as not durable: the gate this feeds fails toward retry.
616
+ */
617
+ private durableCommitVerdict;
618
+ /**
619
+ * True when local storage records `commit.rev` of `blockId` under `commit.actionId`. Read from
620
+ * the revision index (the {@link IRevisionActionReader} capability) so a member whose `latest` has
621
+ * since moved past the revision still counts as holding it; a repo without the capability is
622
+ * judged on `latest` alone.
623
+ */
624
+ private holdsCommittedRevision;
590
625
  /**
591
626
  * Applies a consensus-ordered invalidation on this member: dedup → certificate verification →
592
627
  * capture the invalidation's commit cert (for reactivity reuse) → delegate the compensating write +
@@ -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":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAa,WAAW,EAAE,qBAAqB,EAAE,sBAAsB,EAA2B,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAiB,UAAU,EAAE,iBAAiB,EAAsB,MAAM,qBAAqB,CAAC;AACxS,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAiC,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAGxG,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAK7E,OAAO,EAA8C,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AA+CtG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAExH;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAEnF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAElF;;;;;;;;GAQG;AACH,MAAM,MAAM,gCAAgC,GAAG,sBAAsB,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IACjC,wGAAwG;IACxG,KAAK,EAAE,YAAY,CAAC;IACpB,yGAAyG;IACzG,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAyB/F,wGAAwG;AACxG,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE9E,UAAU,uBAAuB;IAChC,WAAW,EAAE,KAAK,CAAC;IACnB,WAAW,EAAE,YAAY,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,kGAAkG;IAClG,mBAAmB,CAAC,EAAE,qBAAqB,CAAC;IAC5C,oGAAoG;IACpG,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,oHAAoH;IACpH,sBAAsB,CAAC,EAAE,gCAAgC,CAAC;IAC1D,mHAAmH;IACnH,qBAAqB,CAAC,EAAE,6BAA6B,CAAC;IACtD;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,uBAAuB,GAAG,aAAa,CAoBhF;AAKD;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAkBhD;;;GAGG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAwE5C,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAGhC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAE5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IAtFxC,OAAO,CAAC,kBAAkB,CAA4C;IAEtE,OAAO,CAAC,oBAAoB,CAAkC;IAK9D,OAAO,CAAC,mBAAmB,CAAsC;IAYjE,OAAO,CAAC,qBAAqB,CAAwC;IAQrE,OAAO,CAAC,kBAAkB,CAAoC;IAK9D,OAAO,CAAC,oBAAoB,CAAkC;IAE9D,OAAO,CAAC,YAAY,CAAgB;IAEpC,OAAO,CAAC,cAAc,CAAkD;IAExE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAiB;IACpD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IAEjD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CAAO;IAE5D;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAGhD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAS;IACrD,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAU;IACvD;sGACkG;IAClG,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA0B;IAClE,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGjB,WAAW,EAAE,KAAK,EAClB,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,MAAM,YAAA,EAExC,kBAAkB,CAAC,EAAE,iBAAiB,EACrB,WAAW,CAAC,EAAE,WAAW,YAAA,EACzB,SAAS,CAAC,EAAE,qBAAqB,YAAA,EACjC,UAAU,CAAC,EAAE,eAAe,YAAA,EAC7C,eAAe,CAAC,EAAE,sBAAsB,EACvB,UAAU,CAAC,EAAE,sBAAsB,YAAA,EACnC,cAAc,CAAC,EAAE,sBAAsB,YAAA,EACvC,mBAAmB,CAAC,EAAE,qBAAqB,YAAA,EAC3C,YAAY,CAAC,EAAE,qBAAqB,YAAA,EACpC,sBAAsB,CAAC,EAAE,gCAAgC,YAAA,EACzD,qBAAqB,CAAC,EAAE,6BAA6B,YAAA,EACtE,GAAG,CAAC,EAAE,MAAM,MAAM;IA2BnB;;;;;OAKG;IACH,IAAI,+BAA+B,IAAI,MAAM,CAE5C;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI;IAcf;;;OAGG;IACH,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIpD;;;;;;;;;OASG;IACH,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIlE;;;;;;;;;;OAUG;IACH,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAItE;;;;;;OAMG;IACG,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI1E;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;YAyB7C,aAAa;IAgM3B;;;;;;;;;OASG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;;OAGG;YACW,YAAY;IAgE1B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;YAyCZ,cAAc;IAmC5B;;;;OAIG;YACW,kBAAkB;YAIlB,kBAAkB;IA4BhC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;YAQd,kBAAkB;YAIlB,iBAAiB;YAIjB,QAAQ;IAKtB;;;;;;;;;;;OAWG;YACW,eAAe;YAqCf,mBAAmB;IAkFjC,OAAO,CAAC,WAAW;YAIL,mBAAmB;IAgCjC;;;;;;;;OAQG;YACW,wBAAwB;IAkBtC;;;;;;;;OAQG;YACW,eAAe;IA4B7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;YACW,eAAe;IAgJ7B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,cAAc;IAKtB;;;;;;;;;;;;OAYG;YACW,yBAAyB;IAoEvC,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAStC;;;;;OAKG;YACW,sBAAsB;IAqHpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkDG;YACW,uBAAuB;IA0ErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;YACW,gCAAgC;IA0B9C;;;;OAIG;YACW,kCAAkC;IAyBhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;YACW,wBAAwB;YA0FxB,kBAAkB;IAoBhC;;;;;;;;;;;;;;;;;;OAkBG;YACW,eAAe;IA0D7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8CG;YACW,uBAAuB;IAkGrC;;;;;;;OAOG;YACW,oBAAoB;IA4ElC;;;;;;;;OAQG;YACW,oBAAoB;IAsBlC;;;;;OAKG;YACW,sBAAsB;IASpC;;;;;;;;;;OAUG;YACW,0BAA0B;IAwExC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;;;;;OAMG;YACW,wBAAwB;IAgBtC,uFAAuF;YACzE,iBAAiB;IAmB/B,0FAA0F;IAC1F,OAAO,CAAC,oBAAoB;YAcd,eAAe;IAI7B,OAAO,CAAC,aAAa;IAiBrB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;YAuEN,iBAAiB;YAejB,gBAAgB;YA+BhB,gBAAgB;IAM9B,OAAO,CAAC,wBAAwB;YAgClB,mBAAmB;IAqBjC,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,gBAAgB;IAqBxB,iEAAiE;IACjE,OAAO,CAAC,uBAAuB;IAS/B;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC1C;;;OAGG;IACG,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUxE"}
@@ -99,13 +99,17 @@ export class ClusterMember {
99
99
  // a pend that every member refused (rival pending action, or the revision already taken) must
100
100
  // reach the writer as a conflict, not a win. Pruned alongside executedTransactions (same TTL).
101
101
  executedPendResults = new Map();
102
- // Local storage's verdict for a COMMIT operation applied during consensus (messageHash ->
103
- // CommitResult). Retained so the coordinator can detect when the ahead-divergence tolerance in
102
+ // This member's POST-RECONCILE durable verdict for a COMMIT operation applied during consensus
103
+ // (messageHash -> CommitResult): whether local storage holds the committed revision under the
104
+ // record's action once the apply — and any behind-reconcile it triggered — has run. Two readers,
105
+ // which must see the same answer: the coordinating node reads its own member's directly
106
+ // (getExecutedCommitResult), every other member's reaches the coordinator on the response record
107
+ // (withOwnApplyOutcome → ClusterRecord.applyOutcomes[peer].commit). Together they feed the
108
+ // durability gate in CoordinatorRepo.commit, which acknowledges a commit only when a majority of
109
+ // the cohort reports holding it — consensus votes were never evidence of storage. A retained
110
+ // refusal also lets the coordinator detect when the ahead-divergence tolerance in
104
111
  // applyConsensusOperation swallowed a refusal whose real cause was a RIVAL action holding the
105
- // requested revision the commit-tier acknowledgement hole: a commit that assembled consensus
106
- // inside every member's signed-but-not-yet-applied window is refused by every member's storage
107
- // at apply, and without this verdict the coordinator fabricates a success no member durably
108
- // stored. Pruned alongside executedTransactions (same TTL).
112
+ // requested revision. Pruned alongside executedTransactions (same TTL).
109
113
  executedCommitResults = new Map();
110
114
  // Conflict-shaped pend refusals this member's storage produced at consensus-apply, keyed by the
111
115
  // refused action's id rather than by messageHash. The messageHash-keyed map above cannot serve the
@@ -243,13 +247,15 @@ export class ClusterMember {
243
247
  return this.executedPendResults.get(messageHash);
244
248
  }
245
249
  /**
246
- * Commit-shaped sibling of {@link getExecutedPendResult}: local storage's verdict for a commit
247
- * operation applied during consensus, when this member retained one. `CoordinatorRepo.commit`
248
- * consults it after a locally-executed commit-consensus a retained refusal whose cause a local
249
- * re-read confirms as a rival holding the requested revision is returned to the writer as a
250
- * retryable conflict instead of the fabricated success the ahead-divergence tolerance would
251
- * otherwise imply. Same availability caveats as the pend accessor: in-memory only, absent for
252
- * pre-restart applies, pruned on the executed-transaction TTL.
250
+ * Commit-shaped sibling of {@link getExecutedPendResult}: this member's post-reconcile durable
251
+ * verdict for a commit operation applied during consensus, when it retained one `success: true`
252
+ * iff local storage holds the committed revision under the record's action for every block the
253
+ * commit named, after the apply and any behind-reconcile it triggered. `CoordinatorRepo.commit`
254
+ * reads it after a locally-executed commit-consensus, both as this node's own contribution to the
255
+ * durable-holder count its durability gate needs, and for a retained refusal whose cause a
256
+ * local re-read confirms as a rival holding the requested revision — as the trigger for a
257
+ * retryable conflict answer. Same availability caveats as the pend accessor: in-memory only,
258
+ * absent for pre-restart applies, pruned on the executed-transaction TTL.
253
259
  */
254
260
  getExecutedCommitResult(messageHash) {
255
261
  return this.executedCommitResults.get(messageHash);
@@ -472,21 +478,28 @@ export class ClusterMember {
472
478
  return currentRecord;
473
479
  }
474
480
  /**
475
- * Add this member's own conflict-shaped pend verdict to a record's {@link ClusterRecord.applyOutcomes},
476
- * when storage produced one for this transaction. A success, a bare-reason fault, or no retained
477
- * verdict at all leaves the record untouched the coordinator's rule is "an entry means retry",
478
- * so an entry that does not mean retry must not exist.
481
+ * Add this member's own apply verdicts to a record's {@link ClusterRecord.applyOutcomes}, when
482
+ * storage produced any for this transaction. The two arms follow the rules on
483
+ * {@link MemberApplyOutcome}: the pend arm carries ONLY a conflict-shaped refusal (a success, a
484
+ * bare-reason fault, or no retained verdict leaves it off the coordinator's rule is "an entry
485
+ * means retry", so an entry that does not mean retry must not exist); the commit arm carries the
486
+ * retained durable verdict whatever it says, because the coordinator counts the successes.
479
487
  *
480
488
  * The member never signs this and never writes another peer's entry.
481
489
  */
482
490
  withOwnApplyOutcome(record) {
483
- const verdict = this.executedPendResults.get(record.messageHash);
484
- if (verdict === undefined || verdict.success || !isConflictFailure(verdict)) {
491
+ const pend = this.executedPendResults.get(record.messageHash);
492
+ const commit = this.executedCommitResults.get(record.messageHash);
493
+ const own = {
494
+ ...(pend !== undefined && !pend.success && isConflictFailure(pend) ? { pend } : {}),
495
+ ...(commit !== undefined ? { commit } : {})
496
+ };
497
+ if (Object.keys(own).length === 0) {
485
498
  return record;
486
499
  }
487
500
  return {
488
501
  ...record,
489
- applyOutcomes: { ...record.applyOutcomes, [this.peerId.toString()]: { pend: verdict } }
502
+ applyOutcomes: { ...record.applyOutcomes, [this.peerId.toString()]: own }
490
503
  };
491
504
  }
492
505
  /**
@@ -1750,6 +1763,11 @@ export class ClusterMember {
1750
1763
  * mid-commit `internalCommit` fault (`success:false` with a bare `reason`, no
1751
1764
  * `missing`) is propagated so {@link handleConsensus} rolls back the executed marker
1752
1765
  * and rethrows — exactly like an unexpected thrown fault.
1766
+ *
1767
+ * Tolerated is not the same as acknowledged. Whatever shape a commit's apply took, this
1768
+ * member retains one post-reconcile durable verdict for it ({@link durableCommitVerdict}) and
1769
+ * reports it to the coordinator, whose durability gate (`CoordinatorRepo.commit`) acknowledges
1770
+ * the writer only when a majority of the cohort reports holding the revision.
1753
1771
  */
1754
1772
  async applyConsensusOperation(record, operation) {
1755
1773
  const messageHash = record.messageHash;
@@ -1832,87 +1850,16 @@ export class ClusterMember {
1832
1850
  membershipVersion: record.membershipVersion
1833
1851
  });
1834
1852
  }
1835
- let result;
1836
- try {
1837
- result = await this.storageRepo.commit(commit, undefined, proof);
1838
- }
1839
- catch (err) {
1840
- // `StorageRepo.commit` throws (rather than returning success:false) when
1841
- // the pending action is missing the canonical "behind" signal: this
1842
- // member reached commit-consensus without the matching pend (cohort drift).
1843
- if (isMissingPendingActionError(err)) {
1844
- log('cluster-member:consensus-commit-diverged', {
1845
- messageHash,
1846
- actionId: commit.actionId,
1847
- divergence: 'behind',
1848
- reason: err.message
1849
- });
1850
- // We hold no revision of these blocks; pull the committed revision from a
1851
- // cohort peer so the block is no longer under-replicated. Best-effort:
1852
- // failures are logged inside, never thrown (a throw would reset the stream).
1853
- await this.reconcileDivergentCommit(record, commit);
1854
- return;
1855
- }
1856
- throw err;
1857
- }
1858
- // Retain the verdict either way (see getExecutedCommitResult): a success confirms local
1859
- // durability, and an ahead-shaped refusal is the only evidence the coordinator has that
1860
- // the tolerance below swallowed a rival's win at the requested revision. The
1861
- // missing-pending throw path above retains nothing — no CommitResult exists there, and
1862
- // the coordinator's fabricated-success fallback plus cohort reconcile is the right shape
1863
- // for a member that is genuinely behind.
1864
- this.executedCommitResults.set(messageHash, result);
1865
- if (!result.success) {
1866
- // success:false is a StaleFailure. `missing` ⇒ ahead/stale divergence
1867
- // (we already hold ≥ this rev): tolerate, do NOT reconcile downward. A
1868
- // missing-base reason ⇒ behind divergence, reconcile (below). Any other bare
1869
- // `reason` with no `missing` ⇒ a genuine internalCommit fault: propagate so
1870
- // handleConsensus rolls back the executed marker and rethrows.
1871
- //
1872
- // NOTE: this 'ahead' tolerance is what turns a rival's commit that somehow reaches
1873
- // consensus into a reported success no member durably stored (consensus without
1874
- // durability — the commit-tier acknowledgement hole). It must stay: a member
1875
- // genuinely ahead of a redelivered/lagging commit is the common, correct case. The
1876
- // guards live UPSTREAM: `validateCommitRevisions` rejects the rival at the promise
1877
- // round, `CoordinatorRepo.commit` returns lost races as retryable conflicts instead
1878
- // of re-driving them, and the verdict retained just above (getExecutedCommitResult)
1879
- // lets the coordinating node convert its OWN member's rival-confirmed refusal into a
1880
- // conflict answer — that last guard is what closes the signed-but-not-yet-applied
1881
- // window, where two commits for one revision both assemble consensus because signing
1882
- // drops each member's reservation before applying advances its storage —
1883
- // `validateCommitAgainstRefusedPend` now refuses to sign that commit in the first
1884
- // place on any member that refused its pend. If consensus-without-durability is ever
1885
- // observed again, look at those guards' remaining abstain residuals (capability-less
1886
- // or history-truncated storage; a member whose refusal has aged out of retention),
1887
- // not at this branch.
1888
- if (result.missing?.length) {
1889
- log('cluster-member:consensus-commit-diverged', {
1890
- messageHash,
1891
- actionId: commit.actionId,
1892
- divergence: 'ahead',
1893
- reason: result.reason,
1894
- hasMissing: true
1895
- });
1896
- return;
1897
- }
1898
- // This member holds no materializable base for one of the blocks, so
1899
- // `StorageRepo.commit` REFUSED rather than record a revision it could never serve.
1900
- // Same "behind" divergence as a missing pend — and the same cure: pull the committed
1901
- // revision from a cohort peer. Reconciling here (after commit released its per-block
1902
- // latches) is what makes refusing safe; fetching inside the commit path would deadlock
1903
- // against the latch `saveReplicatedBlock` needs to persist what it fetched.
1904
- if (isMissingBaseRevisionFailure(result)) {
1905
- log('cluster-member:consensus-commit-diverged', {
1906
- messageHash,
1907
- actionId: commit.actionId,
1908
- divergence: 'behind',
1909
- reason: result.reason
1910
- });
1911
- await this.reconcileDivergentCommit(record, commit);
1912
- return;
1913
- }
1914
- throw new Error(`Consensus commit for action ${commit.actionId} failed: ${result.reason ?? 'unknown reason'}`);
1915
- }
1853
+ const applied = await this.applyCommitToStorage(record, commit, proof);
1854
+ // Retain the POST-RECONCILE durable verdict (see getExecutedCommitResult and
1855
+ // withOwnApplyOutcome): what this member's storage holds NOW, after any behind-reconcile the
1856
+ // apply triggered — not what the first apply attempt said. A member that pulled the
1857
+ // revision from a cohort peer is a durable holder and reports one; a member that could not
1858
+ // is not, whichever shape its refusal took. The coordinator's durability gate counts these
1859
+ // verdicts its own member's through getExecutedCommitResult, every other member's off the
1860
+ // response record and the two readers must see the same answer, hence one verdict,
1861
+ // computed once, retained here for both.
1862
+ this.executedCommitResults.set(messageHash, await this.durableCommitVerdict(commit, applied));
1916
1863
  return;
1917
1864
  }
1918
1865
  if ('invalidate' in operation) {
@@ -1920,6 +1867,135 @@ export class ClusterMember {
1920
1867
  return;
1921
1868
  }
1922
1869
  }
1870
+ /**
1871
+ * Apply one consensus commit to local storage, tolerating every divergence shape the way the
1872
+ * doc comment on {@link applyConsensusOperation} describes and reconciling the behind ones.
1873
+ * Returns storage's own result — or, for the thrown missing-pend shape, which produces no
1874
+ * `CommitResult` at all, a refusal built from the throw — so {@link durableCommitVerdict} works
1875
+ * from one uniform shape. Genuine faults (a bare-reason returned failure, an unrecognized throw)
1876
+ * propagate so {@link handleConsensus} rolls back the executed marker and rethrows.
1877
+ */
1878
+ async applyCommitToStorage(record, commit, proof) {
1879
+ const messageHash = record.messageHash;
1880
+ let result;
1881
+ try {
1882
+ result = await this.storageRepo.commit(commit, undefined, proof);
1883
+ }
1884
+ catch (err) {
1885
+ // `StorageRepo.commit` throws (rather than returning success:false) when
1886
+ // the pending action is missing — the canonical "behind" signal: this
1887
+ // member reached commit-consensus without the matching pend (cohort drift).
1888
+ if (isMissingPendingActionError(err)) {
1889
+ log('cluster-member:consensus-commit-diverged', {
1890
+ messageHash,
1891
+ actionId: commit.actionId,
1892
+ divergence: 'behind',
1893
+ reason: err.message
1894
+ });
1895
+ // We hold no revision of these blocks; pull the committed revision from a
1896
+ // cohort peer so the block is no longer under-replicated. Best-effort:
1897
+ // failures are logged inside, never thrown (a throw would reset the stream).
1898
+ await this.reconcileDivergentCommit(record, commit);
1899
+ return { success: false, reason: err.message };
1900
+ }
1901
+ throw err;
1902
+ }
1903
+ if (result.success) {
1904
+ return result;
1905
+ }
1906
+ // success:false is a StaleFailure. `missing` ⇒ ahead/stale divergence
1907
+ // (we already hold ≥ this rev): tolerate, do NOT reconcile downward. A
1908
+ // missing-base reason ⇒ behind divergence, reconcile (below). Any other bare
1909
+ // `reason` with no `missing` ⇒ a genuine internalCommit fault: propagate so
1910
+ // handleConsensus rolls back the executed marker and rethrows.
1911
+ //
1912
+ // NOTE: this 'ahead' tolerance is what turns a rival's commit that somehow reaches
1913
+ // consensus into a member-side no-op rather than a stream reset. It must stay: a member
1914
+ // genuinely ahead of a redelivered/lagging commit is the common, correct case. What keeps
1915
+ // the tolerance from turning into an acknowledged write no member stored is downstream:
1916
+ // the durable verdict retained by the caller reports this member as NOT holding the
1917
+ // revision (unless the revision index names this very action — an ahead member that did
1918
+ // land it), and `CoordinatorRepo.commit`'s durability gate acknowledges only a majority of
1919
+ // such reports; a retained refusal whose cause a local re-read confirms as a rival at the
1920
+ // requested revision is answered as a retryable conflict there. Upstream,
1921
+ // `validateCommitRevisions` rejects the rival at the promise round and
1922
+ // `validateCommitAgainstRefusedPend` refuses to sign the commit of a pend this member
1923
+ // refused. If consensus-without-durability is ever observed again, look at the gate's
1924
+ // inputs (a member reporting a durable success it does not hold, or a majority that is
1925
+ // genuinely durable on a forked lineage), not at this branch.
1926
+ if (result.missing?.length) {
1927
+ log('cluster-member:consensus-commit-diverged', {
1928
+ messageHash,
1929
+ actionId: commit.actionId,
1930
+ divergence: 'ahead',
1931
+ reason: result.reason,
1932
+ hasMissing: true
1933
+ });
1934
+ return result;
1935
+ }
1936
+ // This member holds no materializable base for one of the blocks, so
1937
+ // `StorageRepo.commit` REFUSED rather than record a revision it could never serve.
1938
+ // Same "behind" divergence as a missing pend — and the same cure: pull the committed
1939
+ // revision from a cohort peer. Reconciling here (after commit released its per-block
1940
+ // latches) is what makes refusing safe; fetching inside the commit path would deadlock
1941
+ // against the latch `saveReplicatedBlock` needs to persist what it fetched.
1942
+ if (isMissingBaseRevisionFailure(result)) {
1943
+ log('cluster-member:consensus-commit-diverged', {
1944
+ messageHash,
1945
+ actionId: commit.actionId,
1946
+ divergence: 'behind',
1947
+ reason: result.reason
1948
+ });
1949
+ await this.reconcileDivergentCommit(record, commit);
1950
+ return result;
1951
+ }
1952
+ throw new Error(`Consensus commit for action ${commit.actionId} failed: ${result.reason ?? 'unknown reason'}`);
1953
+ }
1954
+ /**
1955
+ * The durable verdict for a consensus commit: `applied` itself when storage landed it (a
1956
+ * success from `StorageRepo.commit` means every block is committed, or already was, under this
1957
+ * action), otherwise a fresh look at what local storage holds NOW — after the reconcile the
1958
+ * refusal triggered. Durable means every block in `commit.blockIds` records `commit.rev` under
1959
+ * `commit.actionId`. A refusal that is not durable is returned as-is, so the coordinating node
1960
+ * can still classify its own member's ahead-shaped refusal against a rival. A read fault counts
1961
+ * as not durable: the gate this feeds fails toward retry.
1962
+ */
1963
+ async durableCommitVerdict(commit, applied) {
1964
+ if (applied.success) {
1965
+ return applied;
1966
+ }
1967
+ try {
1968
+ for (const blockId of commit.blockIds) {
1969
+ if (!(await this.holdsCommittedRevision(blockId, commit))) {
1970
+ return applied;
1971
+ }
1972
+ }
1973
+ }
1974
+ catch (err) {
1975
+ log('cluster-member:consensus-commit-durability-read-error', {
1976
+ actionId: commit.actionId,
1977
+ rev: commit.rev,
1978
+ error: err.message
1979
+ });
1980
+ return applied;
1981
+ }
1982
+ log('cluster-member:consensus-commit-durable-after-reconcile', { actionId: commit.actionId, rev: commit.rev });
1983
+ return { success: true };
1984
+ }
1985
+ /**
1986
+ * True when local storage records `commit.rev` of `blockId` under `commit.actionId`. Read from
1987
+ * the revision index (the {@link IRevisionActionReader} capability) so a member whose `latest` has
1988
+ * since moved past the revision still counts as holding it; a repo without the capability is
1989
+ * judged on `latest` alone.
1990
+ */
1991
+ async holdsCommittedRevision(blockId, commit) {
1992
+ const reader = this.storageRepo;
1993
+ if (typeof reader.getRevisionAction === 'function') {
1994
+ return await reader.getRevisionAction(blockId, commit.rev) === commit.actionId;
1995
+ }
1996
+ const latest = (await this.storageRepo.get({ blockIds: [blockId] }))[blockId]?.state?.latest;
1997
+ return latest?.rev === commit.rev && latest.actionId === commit.actionId;
1998
+ }
1923
1999
  /**
1924
2000
  * Applies a consensus-ordered invalidation on this member: dedup → certificate verification →
1925
2001
  * capture the invalidation's commit cert (for reactivity reuse) → delegate the compensating write +