Skip to content

fix(RcMap): preserve replacement entries during invalidated resource cleanup - #7516

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
kitlangton:rcmap-release
Aug 30, 2026
Merged

fix(RcMap): preserve replacement entries during invalidated resource cleanup#7516
tim-smart merged 2 commits into
Effect-TS:mainfrom
kitlangton:rcmap-release

Conversation

@kitlangton

@kitlangton kitlangton commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Why

Invalidating a borrowed resource and reacquiring its key lets old cleanup affect the replacement. Zero TTL removes the replacement mapping; infinite TTL leaves the detached old resource unfinalized. An existing finite idle timer can independently remove the replacement.

Closes #7515.
Closes EFF-974.

What Changes

Cleanup event New behavior
Invalidated entry still has borrowers Keep the old resource alive
Last borrower of an invalidated entry closes Finalize only that resource, regardless of idle TTL
Last borrower of the current entry closes Preserve existing TTL behavior
Old idle timer expires after replacement Leave the replacement untouched

Release and idle expiry check that the key still maps to the entry being cleaned up before removing it.

Scope

A focused RcMap lifecycle fix, with regression coverage for zero, finite, and infinite TTL and a surviving old idle timer. Includes an effect: patch changeset covering the consumer-visible RcMap and LayerMap behavior. No public API changes.

Verification

pnpm test --run packages/effect/test/RcMap.test.ts packages/effect/test/LayerMap.test.ts
pnpm check
pnpm exec oxlint -f unix packages/effect/src/RcMap.ts packages/effect/test/RcMap.test.ts
pnpm exec dprint check packages/effect/src/RcMap.ts packages/effect/test/RcMap.test.ts .changeset/rcmap-invalidation-release.md
pnpm exec changeset status
git diff --check

All four new regression cases failed before the fix. Fixing release alone passed the three TTL cases but left the stale-timer regression failing; the expiry identity check resolved that remaining failure.

Final result: 25 tests pass, comprising 19 RcMap and 6 LayerMap tests. Type checking, lint, formatting, and changeset validation pass. The standalone reproducer produces the expected results with the fix and no manual resource cleanup.

The full test suite and OpenCode integration were not run.

@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 76ecd2d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

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

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
arbitrary-combinators.ts 33.76 KB 33.76 KB 0.00 KB (0.00%)
basic.ts 7.00 KB 7.00 KB 0.00 KB (0.00%)
batching.ts 9.87 KB 9.87 KB 0.00 KB (0.00%)
brand.ts 6.56 KB 6.56 KB 0.00 KB (0.00%)
cache.ts 10.74 KB 10.74 KB 0.00 KB (0.00%)
config.ts 21.45 KB 21.45 KB 0.00 KB (0.00%)
differ.ts 20.32 KB 20.32 KB 0.00 KB (0.00%)
http-client.ts 21.77 KB 21.77 KB 0.00 KB (0.00%)
logger.ts 10.94 KB 10.94 KB 0.00 KB (0.00%)
metric.ts 8.98 KB 8.98 KB 0.00 KB (0.00%)
optic.ts 6.73 KB 6.73 KB 0.00 KB (0.00%)
pubsub.ts 15.12 KB 15.12 KB 0.00 KB (0.00%)
queue.ts 11.84 KB 11.84 KB 0.00 KB (0.00%)
schedule.ts 10.86 KB 10.86 KB 0.00 KB (0.00%)
schema-binary.ts 39.42 KB 39.42 KB 0.00 KB (0.00%)
schema-class.ts 20.07 KB 20.07 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 31.02 KB 31.02 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 26.21 KB 26.21 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.66 KB 13.66 KB 0.00 KB (0.00%)
schema-string.ts 11.18 KB 11.18 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.47 KB 15.47 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 33.31 KB 33.31 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.51 KB 24.51 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.29 KB 19.29 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 19.42 KB 19.42 KB 0.00 KB (0.00%)
schema-toFormatter.ts 19.51 KB 19.51 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 23.45 KB 23.45 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.56 KB 19.56 KB 0.00 KB (0.00%)
schema.ts 19.28 KB 19.28 KB 0.00 KB (0.00%)
stm.ts 12.74 KB 12.74 KB 0.00 KB (0.00%)
stream.ts 9.83 KB 9.83 KB 0.00 KB (0.00%)

Comment thread packages/effect/src/RcMap.ts Outdated

const o = MutableHashMap.get(self.state.map, key)
if (o._tag === "None" || o.value !== entry) {
return Scope.close(entry.scope, Exit.void)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this leak any running fibers?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. A detached entry with an already-running finite-TTL timer could leave that fiber asleep until its old deadline after the resource scope was closed. Commit 76ecd2d routes external entry shutdown through closeEntry, which interrupts the idle fiber before closing the scope; the timer-expiry path still terminates naturally from inside that fiber. I also extended the stale-timer regression to assert immediate interruption. The assertion fails on the prior PR head and passes with this commit.

@tim-smart
tim-smart merged commit a29e05a into Effect-TS:main Aug 30, 2026
13 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RcMap active invalidation can remove replacement entries or leak resources (v4)

2 participants