Skip to content

fix: requests never wait on the relay settings or session-pin locks - #411

Merged
ualtinok merged 4 commits into
mainfrom
fix/request-path-locks
Oct 20, 2026
Merged

ualtinok merged 4 commits into
mainfrom
fix/request-path-locks

Conversation

@antauth-alfonso

@antauth-alfonso antauth-alfonso Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

A user request in OpenCode failed with Timed out acquiring native-relay lock for …/anthropic-auth-vault-settings.json under heavy CPU load: every request read the relay settings under the exclusive lock their writer uses, and a lock-wait timeout failed the request. Rule: a request never waits on or fails from bookkeeping I/O.

  • Relay settings are read without a lock. The writer still serializes and now stamps the settings and the runtime token with one generation. Readers re-read (four tries, 10 ms apart) until both agree; both unstamped counts as legacy and is accepted. A pair that never agrees refuses that request locally (fixed non-retryable 400) and no token is sent. A racing reader/writer test proves an old URL never pairs with a new token.
  • Session pins never hold a request. Requests waited up to 5 s for the routing-file lock and failed without it. Routes are now decided from the atomically published file without a lock; the pin change is published in the background under the lock (re-read, merge only that session, ownership fence, retry on failure, a queued assignment superseded by a clear is dropped, drained on dispose). A session reset deletes in memory at once and still reaches disk after contention.
  • Proof that a healthy request takes no lock. On OpenCode 1, OpenCode 2 and Pi, a request with a valid token, known identity and ready roster records zero foreground lock acquisitions before dispatch, through an observer threaded into the lock calls (no global patching). A control per host shows an expired token still takes the real refresh lock. Credential-refresh and custody locks are unchanged.

Mutation rows: lock restored in the relay read, generation check disabled, route decided under a lease, superseded assignment published; all CAUGHT.


Summary by cubic

Fixes user requests that could time out or fail while waiting on relay settings or session-pin bookkeeping locks. Requests now complete without acquiring those locks; any bookkeeping is done in the background.

  • Relay settings are read without a lock and validated via a shared generation stamp; readers retry briefly and refuse locally if a pair never agrees.
  • Session-pin assignment and removal are queued and published in the background; routes are decided from the atomically published file without a lock. Tests drain queued publications before asserting on disk to remove timing flakiness.
  • A lock observer distinguishes foreground and background-origin acquisitions, and the exported NativeLockEvent type adds an acquiring event; tests prove healthy requests take no foreground locks on OpenCode 1, OpenCode 2, and Pi.
  • Mutation controls catch any reintroduced reader locking or missing generation checks.

Written for commit 04eaec3. Summary will update on new commits.

View guided diff Turn on auto-fix

Read atomically published relay settings and secrets without the writer lock. Stamp every relay update with a shared generation, retry crossed reads briefly, and return a terminal local refusal rather than dispatching an unmatched URL/token pair. Keep legacy unstamped pairs readable and leave all credential-refresh and custody locks unchanged.

Make session-pin assignment, removal and legacy seeding nonblocking best-effort bookkeeping. Two existing tests deliberately change contract: pin-seed rename failures and malformed pin files no longer veto an independently authorized request. They still assert no partial publication, preservation of malformed bytes, and successful later seeding.

Add lifetime-owned real held-lock coverage on OpenCode 1, OpenCode 2 and Pi, writer serialization and generation-race/refusal tests. Mutation controls catch restored reader locking, missing generation checks, missing writer locking and each blocking pin path.
Decide routes from atomically published pins without a writer lease. Queue session-only merges, touches, clears and legacy imports under the normal background lease; retain failed changes for retry, discard superseded changes after contention, and drain owned publications during Core, OpenCode and Pi disposal.

Keep credential-refresh and custody coordination unchanged. Extend the injected non-controlling lock observer, distinguish background-origin and post-dispatch writes, and prove zero foreground acquisitions for healthy local and vault requests on each host, with an expired-token provider-lock control per host.

Add the three requested catalogue controls with broad command/report/ID fields and prove each through the strict mutation runner. Preserve existing persistence, ownership and UUID/account assertions by explicitly draining publications before inspecting disk. Legacy recovery contention now serves requests while publication waits, then completes after release instead of being dropped.

Root typecheck/build, scoped regressions, catalogue self-test and all nine host lock probes pass. The last broad Core run had two unchanged five-second shared-runner timeouts; detailed failures, mutation evidence and the parent-deferred shared-refresh joining caveat are retained in the delivery audit.
Core's exported NativeLockEvent type gained acquiring. Deliberately include acquiring in both pinned unions of the generated consumer.ts fixture, retaining the closed event-kind contract, older producer compatibility, and rejection of narrow callbacks.

Verified with pinned Bun 1.3.14 on linux,4c,net: Core build and test:scripts pass (9 tests, 46 assertions); the fresh consumer typechecks 421 files and passes 18 Node runtime assertions.
The CI byte comparison captured routing before a queued reset finished, then compared it after the deletion. Other repetitions read before live-pin creation or before the background legacy import. Drain the actual routing publishers at those persistence boundaries; no production merger change is needed.

Keep every original assertion, add live-retention and legacy-only-removal checks after reset, and join fixture-owned publishers before removing their root. Reproduced 5/5 failures before the fix and 20/20 passing independent Linux runs afterward. A deliberate occupied-key overwrite still reddens the original live-pin equality through the strict mutation runner.

Core build and OpenCode typecheck pass; focused OC2/migration coverage passes 65 tests. Ran the full OpenCode suite once with built packed exports and network: all OC2 and real-host cases pass, with three unrelated existing test/hook deadline failures recorded in the delivery audit.
@ualtinok
ualtinok merged commit 04eaec3 into main Oct 20, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant