Skip to content

turbo-tasks-gc: Defer most aggregation graph management until after GC has settled - #98591

Open
lukesandberg wants to merge 1 commit into
lukesandberg/gc-07-gc-configfrom
gc-08-gc-fixes
Open

turbo-tasks-gc: Defer most aggregation graph management until after GC has settled#98591
lukesandberg wants to merge 1 commit into
lukesandberg/gc-07-gc-configfrom
gc-08-gc-fixes

Conversation

@lukesandberg

@lukesandberg lukesandberg commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Fix AggregationGraph updates during GC.

During GC we aremoving a lot of edges so this can trigger a lot of aggregation graph churn. Because of the highly parallel nature of GC this can end up with multiple queues fighting over updates which can exceed various retry limits and lead to things like trying to turn nodes into followers that are actively being deleted.

Instead we just don't rebalance the aggregation graph during GC, instead we buffer every rebalance request and set the optimize budget to zero. Then after GC settles we process all the rebalances at once, taking care to filter out any tasks that have been since deleted.

Because GC is only removing edges it can lead to inefficient graphs (too many interior nodes) but not violate graph invariants.

Alternatives considered:

The initial idea was to ensure GC had finished with all 'children' of the node before processing rebalance requests, but this was only a partial solution (and complex!), since it didn't handle the case of an aggregating node having multiple children become collectible in the initial scan, this could still trigger 'optimization contention' and turning dead nodes into followers.

Testing:

The test/benchmark are enhanced to produce non-trivial aggregation graphs which improves test coverage (though the issue of racing deletes was trivially found by CI also)

@lukesandberg
lukesandberg added this pull request to stack #98592 September 11, 2026 23:52
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Tests Passed

Commit: 9eae998

@lukesandberg
lukesandberg force-pushed the gc-08-gc-fixes branch 2 times, most recently from c40516c to 2ef1efe Compare September 12, 2026 17:29

@vercel vercel Bot left a comment

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.

Additional Suggestion:

GC-collected (soft-deleted) transient tasks are never physically removed from the resident map because the eviction retain loop's is_transient() early-return runs before the flags.deleted() removal branch, leaking tombstoned transient entries for the life of the session.

Fix on Vercel

@lukesandberg lukesandberg changed the title gc 08 gc fixes turbopack-gc: Defer most aggregation graph management until after GC has settled Sep 13, 2026
@lukesandberg lukesandberg changed the title turbopack-gc: Defer most aggregation graph management until after GC has settled turbo-tasks-gc: Defer most aggregation graph management until after GC has settled Sep 13, 2026
Comment thread turbopack/crates/turbo-tasks-backend/benches/gc.rs
Comment thread turbopack/crates/turbo-tasks-backend/benches/gc.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_children.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/gc.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/gc.rs
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/gc.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/gc.rs Outdated
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs
Comment thread turbopack/crates/turbo-tasks-backend/tests/gc_fixture.rs
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs Outdated
Enabling GC on CI surfaced ~14.5k panics from the aggregation graph. Three
related fixes:

- `followers` is no longer a collectibility pin. A follower is an *outgoing*
  edge, so it says nothing about whether anything still reaches this task;
  only `upper` (incoming) does. Treating it as a pin made any task that had
  grown a follower during aggregation-graph reorganization permanently
  uncollectible.
- Defer rebalance work until the parallel collect phase is quiescent. Running
  `balance_edge` while other workers were still collecting could add aggregation
  edges to a task that had just been soft-deleted. The queues GC collects with
  also refuse optimizations outright: optimizing only raises an aggregation
  number to shrink fan-out, which is regenerable via `optimization_pending`,
  unlike `balance_edge`, which restores an invariant.
- Guard the aggregation-edge add sites against deleted tasks, so a sibling
  cascade cannot resurrect edges on a tombstone.

Also deepens the GC test fixture: the previous graph was too shallow to build
interior aggregation nodes, which is why none of this was caught by existing
coverage. The fixture now recurses to a configurable branching factor with a
retained top layer, so collection happens under a live root -- the common case.

Includes a rename of `GcStats` to `GcPassOutcome` and a revert of incidental
formatting churn in the napi bindings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
/// remains rebalances the graph rather than tearing edges out of it. GC uses this to stop at
/// that boundary and defer the rest until the parallel collect is quiescent.
pub fn only_rebalance_remains(&self) -> bool {
self.jobs.is_empty() && self.aggregation_number_updates.is_empty()

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.

GC's "stop at rebalance boundary" logic silently drops pending find_and_schedule / scheduled_tasks work, leaving dirty tasks unscheduled after a GC collect.

Fix on Vercel

@lukesandberg
lukesandberg marked this pull request as ready for review September 14, 2026 06:54
@lukesandberg
lukesandberg requested a review from a team September 14, 2026 06:55
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