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
Context. Issue #8 asks (among other criteria) that duplicate candidates, expired
indexes and two polling Workers never produce a second valid execution owner, and
that a Redis outage stops claiming without losing authoritative state. The merged #69 adds RedisLeasedProgressStore: a session lease (claim/renew/release) whose
owner+fencing token guards run/journal/outbox/checkpoint writes; every mutation is
one atomic Lua script that re-validates owner, fence, Redis server TIME and
revisions/entry-ids before its first write, with client-side script retries
disabled. C1 (outboxIndexRepairAfterCrash) belongs to the still-open #74
(cf8daf5) and is deliberately not claimed here.
What was run (all against real Valkey 8.1.2, AOF appendfsync always, and real
separate JVM processes — no in-process simulation of multiple JVMs):
S1 SIGKILL the owner JVM -> lease (ttl accelerated to 2500 ms via the public
claim ttl parameter) expires -> a second JVM takes over with fence 1->2 ->
the killed owner is resurrected in a third JVM from its recorded lease:
renew and commit throw LeaseLostException, release returns false, and the
authoritative hashes are byte-identical (no zombie writes). Takeover observed
2313-2506 ms after the kill across two independent executors (3+2 runs).
S2 SIGSTOP the owner (ps state T), let the lease expire, second JVM takes
over, SIGCONT: the resumed process keeps using its in-memory lease and every
write path is fenced; it exits via the fenced path. 5 runs total.
S3 six (and in an adversarial rerun, eight) independent JVMs busy-spin to one
epoch-ms barrier and claim simultaneously: exactly one winner, N-1
LeaseBusyException, winner fence=1, release then re-claim gives fence=2.
Duplicate candidates are dropped by the store: replaying a commit with a stale
expected revision -> ProgressConflictException "Run revision does not match";
correct revision but an existing append-only entry id -> "entry id already
exists"; a further claim while the lease is live -> BUSY.
S4 docker kill (SIGKILL) of the Valkey container: during the ~3.5 s outage
every claim/renew attempt failed with connection exceptions (0 successes, no
local degradation); after restarting the same container, AOF recovery
reproduced revision/state/checkpoint/journal/outbox field-for-field; the
takeover then proceeded (fence 2) and the old owner was fenced.
S5 with ttl=4000 ms and an idle owner: 23-25 poll attempts all BUSY before
expiry, first success 4003-4099 ms after the claim, fence monotonic.
Adversarial additions from the independent review (same store, public API only):
Self-expired owner with no takeover: renew -> LeaseLost, release -> false,
commit with the CORRECT expected revision -> LeaseLost (the time gate alone
rejects a matching owner+fence), fence retained, next claim fence+1.
Takeover during an in-flight operation: owner SIGSTOPped between claim and
first commit; new owner claims but does not commit; the resumed owner's
commit matches the stored revision exactly, so only the owner/fence gate can
reject it - and it does, with zero writes.
Three JVMs racing at the expiry boundary: exactly one winner (>= ttl), and
the killed owner's own later claim is BUSY (no second lease for anyone,
including the previous owner).
Honest mapping to #8's acceptance criteria on today's main:
Known non-blocking nits in our own harness reporting (not store defects), found during independent review: the S4
"connection-type" assertion matches any *Exception class name (actual logged
classes were all WriteRedisConnectionException), one timing figure in the
frozen report has a typo (2447/2436/2506 ms), and the S3 barrier-lag note
understates observed scheduler jitter (winner uniqueness is unaffected because
the server serializes CLAIM atomically).
Nothing is claimed about HA failover/RPO0, multi-primary deployments, or
exactly-once external effects; the store's Javadoc itself scopes these out.
This message reports local verification evidence only; no fix PR is attached.
Activity
Merge pull request #8 from GTyingzi/feature-mcp-nacos-refactor-distri…
PoisonGodv commentedon Nov 6, 2026
Context. Issue #8 asks (among other criteria) that duplicate candidates, expired
indexes and two polling Workers never produce a second valid execution owner, and
that a Redis outage stops claiming without losing authoritative state. The merged
#69 adds RedisLeasedProgressStore: a session lease (claim/renew/release) whose
owner+fencing token guards run/journal/outbox/checkpoint writes; every mutation is
one atomic Lua script that re-validates owner, fence, Redis server TIME and
revisions/entry-ids before its first write, with client-side script retries
disabled. C1 (outboxIndexRepairAfterCrash) belongs to the still-open #74
(cf8daf5) and is deliberately not claimed here.
What was run (all against real Valkey 8.1.2, AOF appendfsync always, and real
separate JVM processes — no in-process simulation of multiple JVMs):
claim ttl parameter) expires -> a second JVM takes over with fence 1->2 ->
the killed owner is resurrected in a third JVM from its recorded lease:
renew and commit throw LeaseLostException, release returns false, and the
authoritative hashes are byte-identical (no zombie writes). Takeover observed
2313-2506 ms after the kill across two independent executors (3+2 runs).
over, SIGCONT: the resumed process keeps using its in-memory lease and every
write path is fenced; it exits via the fenced path. 5 runs total.
epoch-ms barrier and claim simultaneously: exactly one winner, N-1
LeaseBusyException, winner fence=1, release then re-claim gives fence=2.
Duplicate candidates are dropped by the store: replaying a commit with a stale
expected revision -> ProgressConflictException "Run revision does not match";
correct revision but an existing append-only entry id -> "entry id already
exists"; a further claim while the lease is live -> BUSY.
every claim/renew attempt failed with connection exceptions (0 successes, no
local degradation); after restarting the same container, AOF recovery
reproduced revision/state/checkpoint/journal/outbox field-for-field; the
takeover then proceeded (fence 2) and the old owner was fenced.
expiry, first success 4003-4099 ms after the claim, fence monotonic.
Adversarial additions from the independent review (same store, public API only):
commit with the CORRECT expected revision -> LeaseLost (the time gate alone
rejects a matching owner+fence), fence retained, next claim fence+1.
first commit; new owner claims but does not commit; the resumed owner's
commit matches the stored revision exactly, so only the owner/fence gate can
reject it - and it does, with zero writes.
the killed owner's own later claim is BUSY (no second lease for anyone,
including the previous owner).
Honest mapping to #8's acceptance criteria on today's main:
rejection on every write path, single winner under concurrent claims,
revision/entry-id dedup). Advisory-index-level candidate dedup is feat(persistence): bounded directory discovery and advisory index repair #74 scope.
resumes"; polling batch/backoff were explicit fixture parameters.
due-retry index and scanner are feat(persistence): bounded directory discovery and advisory index repair #74 components and were not tested.
Known non-blocking nits in our own harness reporting (not store defects), found during independent review: the S4
"connection-type" assertion matches any *Exception class name (actual logged
classes were all WriteRedisConnectionException), one timing figure in the
frozen report has a typo (2447/2436/2506 ms), and the S3 barrier-lag note
understates observed scheduler jitter (winner uniqueness is unaffected because
the server serializes CLAIM atomically).
Nothing is claimed about HA failover/RPO0, multi-primary deployments, or
exactly-once external effects; the store's Javadoc itself scopes these out.
This message reports local verification evidence only; no fix PR is attached.