Repository navigation
fix(effect): treat interruption as abandonment in Effect.cached* and Cache - #8719
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: fda02af The changes in this PR will be included in the next version bump. This PR includes changesets to release 32 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
tim-smart
commented
Oct 4, 2026
Contributor
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
…rop interrupted RcMap entries
…hes its references
…publishes its references
- ScopedCache: drop the per-entry onInterrupt hook (which also leaked into the public Entry interface) and share one removeEntry helper between the awaiting side and the lookup's exit handler - RcRef and ResourceMap: assign the acquisition/lookup fiber unconditionally instead of guarding and resetting an optional field; count awaiters in one place - Effect.cached: skip the clock lookup on the hit path, drop the dead expiresAt reset on invalidate, hoist the infinite TTL constant - RcMap: fork the interrupted-entry cleanup from the lookup context instead of retaining the caller fiber - Cache.refresh: reuse removeEntry
juliusmarminge
added a commit
to pingdotgg/t3code
that referenced
this pull request
Oct 5, 2026
Effect 4.0.1 stops Effect.cached* from keeping an interrupted run (Effect-TS/effect#8719), so the seven sites that only needed that go back to Effect.cached. Two changes stay: - The mobile DPoP key no longer keeps a failed SecureStore read, so the next relay request loads it again. Effect.cached still keeps failures. - A fresh provider maintenance read starts a new resolution instead of joining one already running, so an update click never uses ownership resolved before it. invalidate did not detach the running one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Interruption is now treated as abandonment in
Effect.cached,Effect.cachedWithTTL,Effect.cachedInvalidateWithTTL,Cache,ScopedCache,RcRef,RcMapand the clusterResourceMap.Effect.cached*Previously the first caller ran the effect on its own fiber. Interrupting that caller cancelled the computation for everyone, and the interrupt exit was cached and replayed.
All three APIs now share one implementation that runs each computation on a detached fiber and counts the callers waiting on it:
CacheandScopedCacheCache.getalready shared cancellation between callers. The gap was after the last caller left: the abandoned entry stayed in the map until its lookup fiber exited, so a newgetcould join it and get the interrupt. Entries created bygetandrefreshnow remove themselves from the map (only if they're still the current entry for the key) before the lookup is interrupted.ScopedCachegets the same treatment. It also now counts waiters that arrive before the lookup fiber starts, which covers the capacity-eviction window.RcRefAcquisition used to run on the first caller's fiber under a semaphore. Interrupting that caller cancelled the acquisition, and the next queued caller started it again from scratch. Acquisition now runs on a detached fiber in a new
Acquiringstate, with a count of the callers waiting on it:Emptybefore that happens, so a newgetstarts a fresh acquisition.ResourceMap(cluster)Lookups used to run on the first caller's fiber, so interrupting that caller failed every other waiter. Lookups now run on a detached fiber and count their waiters, with the same abandonment rules as above. Removing a failed or abandoned entry now checks entry identity. Before this, a lookup abandoned after
remove(key)could delete the replacement entry. Closing the map also interrupts pending lookups.RcMapLookups were already detached and reference-counted. A lookup that ends in interruption is now dropped from the map, so with a non-zero
idleTimeToLivethe nextgetlooks up again instead of replaying the interrupt. If no borrower holds the entry, its scope is closed.Notes
cachedWithTTLused to say interruption exits were cached for the returned TTL. I updated it, along with thecacheddocs, to describe the new behaviour.14bb77f7cf. The oldreplays the owner's interrupted exittest was removed because it asserted the behaviour this change reverses.MemoMapis not in this PR. Sharing builds on a detached fiber breaks layers that capture their building fiber withFiber.getCurrent()and interrupt it to shut down.RpcServer.makeProtocolStdioandmakeProtocolWorkerRunnerdo this, and the MCP stdio conformance tests fail because of it.Validation
pnpm vitest --run --project effect: 324 files, 11053 tests passedpnpm vitest --run packages/platform packages/vitest packages/atom packages/sql/sqlite-node packages/opentelemetry: passedpnpm check,pnpm lint: cleanCloses EFF-1692