test(signals): assert retention on bookkeeping, not a GC heap count - #4128
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 4 included reviews per hour; 0 remain after this review. WalkthroughThe tests remove the Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to The updated tests check listener and task-set cleanup without changing runtime behavior. No merge-blocking issue is established. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
MERGE Positive improvement, complexity justified, approach looks right. The flaky ~8192 retained Promise counts were a bad test, not a leak: Concrete strengths:
Spying on This is an automated review, not the maintainer's decision |
|
Auto-merge enabled: CI green, Codex and CodeRabbit clean. Unblocks #4095, whose Test job failed on the old heap-count assertion. (Written by Claude Opus 5.5) |
Problem
spawn retention > an effect that never reruns drops settled tasks(added in #4085) fails CI's Test job intermittently with ~8191-8194 retained promises (runs 36104628091, 36104280755). It blocks #4095.The implementation is correct:
spawndeletes each task from its set once it settles. The test is not. It countedPromisecells afterBun.gc(true), called from inside the test's own async stack. JSC scans that stack conservatively, so a stale pointer can keep dead cells alive. The ~8192 is most likely a discarded storage table of the task set (it grows to 10000 promises before any are dropped), pinned with every entry it still holds. Whether such a pointer survives depends on stack/register reuse, which differs by arch (CI is arm64) and load; bun is 1.3.13 in both CI and the local dev shell.The sibling
race > many races against a long-lived promise keep the heap flat(same PR,objectCount-based) has the same flaw. It reproduces locally under parallel load: ~1900 extraCell Butterflyand ~95Structurecells after the GC, gone once the stack unwinds.Approach
Assert on the bookkeeping itself instead of observing the GC. Both tests spy on
Set.prototype.addto find the set in question, then check itssize:closed's listener set received all 1000 listeners, and every set touched is empty afterwards.Both fail deterministically with the respective
deleteremoved, and passed 160 parallel runs locally.Impact
Alternatives
setTimeoutso the stack unwinds, or spawn one task at a time so the set never grows: less flaky, still GC-dependent.Follow-ups
js/binary,js/json,js/watch(sync.test.ts,retention.test.ts) have end-to-end heap-count tests from fix(js): stop retaining a listener, reaction, or task per frame #4085 with the same exposure. They have not failed in CI yet; left for a separate change since they have no single set to inspect.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code