instar 1.3.337 → 1.3.339

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6uRtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgxRtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
@@ -9654,10 +9654,39 @@ export async function startServer(options) {
9654
9654
  // Shared-cluster writes stay blocked on a standby. (bug #9: the moved session's
9655
9655
  // saveSession was blocked by the standby read-only guard.)
9656
9656
  state.setSessionPoolActive(true);
9657
+ // Per-peer suspect breaker (P19) — the markOwnerSuspect hook's missing
9658
+ // half. A peer whose deliveries exhaust retries is short-circuited for
9659
+ // a half-open 30s window: every session it owns goes straight to the
9660
+ // EXISTING failover re-place path instead of re-paying the ~4.5s retry
9661
+ // tax per message. Any successful delivery closes the window; sustained
9662
+ // suspicion (10min of consecutive windows) raises ONE degradation
9663
+ // signal per episode.
9664
+ const ownerSuspectBreaker = new (await import('../core/OwnerSuspectBreaker.js')).OwnerSuspectBreaker({
9665
+ logger: (m) => console.log(pc.dim(m)),
9666
+ reportSustained: ({ machineId, suspectForMs, marks }) => {
9667
+ DegradationReporter.getInstance().report({
9668
+ feature: 'SessionPool.ownerDelivery',
9669
+ primary: 'Messages forward to the machine that owns their session',
9670
+ fallback: `Owner ${machineId} unresponsive to deliveries for ~${Math.round(suspectForMs / 60_000)}min (${marks} suspect windows); its sessions re-place on dispatch; half-open re-probes continue`,
9671
+ reason: 'deliverMessage retries to the owning machine keep exhausting',
9672
+ impact: 'Sessions owned by that machine fail over to other machines on their next message instead of being delivered in place.',
9673
+ });
9674
+ },
9675
+ });
9657
9676
  _sessionRouter = new routerMod.SessionRouter({
9658
9677
  selfMachineId: meshSelfId,
9659
9678
  placement: new placeMod.PlacementExecutor(),
9660
- machineRegistry: () => machinePoolRegistry?.getCapacities() ?? [],
9679
+ // Placement must consult the breaker too: a message re-placed OFF a
9680
+ // suspect owner must not be placed right back ONTO it. Suspect
9681
+ // machines are filtered from the candidate set UNLESS that would
9682
+ // empty it — with every machine suspect, placement proceeds on the
9683
+ // full set (mirrors the all-machines-quota-blocked precedent:
9684
+ // degraded placement beats no placement).
9685
+ machineRegistry: () => {
9686
+ const all = machinePoolRegistry?.getCapacities() ?? [];
9687
+ const ok = all.filter((c) => !ownerSuspectBreaker.isSuspect(c.machineId));
9688
+ return ok.length > 0 ? ok : all;
9689
+ },
9661
9690
  resolveOwnership: (sk) => {
9662
9691
  const r = ownReg.read(sk);
9663
9692
  if (!r)
@@ -9665,7 +9694,13 @@ export async function startServer(options) {
9665
9694
  const status = r.status === 'released' ? null : r.status;
9666
9695
  return { owner: ownReg.ownerOf(sk), epoch: r.ownershipEpoch, status, target: ownReg.placementTargetOf(sk) ?? undefined };
9667
9696
  },
9668
- isMachineAlive: (m) => m === meshSelfId || (machinePoolRegistry?.getCapacity(m)?.online ?? false),
9697
+ // Capacity-online AND not currently suspect: a slow-but-heartbeating
9698
+ // peer that keeps failing deliveries reads as not-alive for routing,
9699
+ // sending its sessions down the existing failover re-place path
9700
+ // without each message re-paying the retry tax.
9701
+ isMachineAlive: (m) => m === meshSelfId || ((machinePoolRegistry?.getCapacity(m)?.online ?? false) && !ownerSuspectBreaker.isSuspect(m)),
9702
+ markOwnerSuspect: (m) => ownerSuspectBreaker.markSuspect(m),
9703
+ onOwnerResponsive: (m) => ownerSuspectBreaker.recordSuccess(m),
9669
9704
  casClaimOwnership: (sk, machineId) => {
9670
9705
  const r = ownReg.cas({ type: 'place', machineId }, { sessionKey: sk, sender: meshSelfId, nonce: `${meshSelfId}:c:${++routerNonce}` });
9671
9706
  return { ok: r.ok, epoch: ownReg.read(sk)?.ownershipEpoch ?? 0 };