Skip to content

Fix deep-chain recheck panic in the MAX_EPOCHS fallback#4188

Open
kinto0 wants to merge 2 commits into
facebook:mainfrom
kinto0:export-D112591162
Open

Fix deep-chain recheck panic in the MAX_EPOCHS fallback#4188
kinto0 wants to merge 2 commits into
facebook:mainfrom
kinto0:export-D112591162

Conversation

@kinto0

@kinto0 kinto0 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary:
An export change propagating through a dependency chain deeper than the
100-epoch cap panicked at commit with Transaction has uncommitted changes
(#4171). The fine-grained recheck
path advances one dependency edge per epoch, so a long chain exhausts the cap
before the change reaches the top importer.

The coarse fallback was meant to invalidate the whole affected closure and
recompute it, but it seeded invalidate_rdeps from changed — which the final
loop iteration had already drained — making it a no-op. A single run_step
then left changed/dirty populated, tripping the commit_transaction
invariant.

Seed the coarse invalidation from the pending dirty frontier instead, then
recompute to a fixpoint via stabilize_after_invalidate. This converges
quickly because lookups pull their dependencies: once the closure is
invalidated, a couple of run_steps recompute it with correct values and
drain the bookkeeping the commit invariant requires. A bounded loop with a
final residual-clear keeps a pathological oscillation from hanging or
committing inconsistent state.

Differential Revision: D112591162

kinto0 added 2 commits July 17, 2026 09:53
…oad the correct commit data.

Differential Revision: D112566493
Summary:
An export change propagating through a dependency chain deeper than the
100-epoch cap panicked at commit with `Transaction has uncommitted changes`
(facebook#4171). The fine-grained recheck
path advances one dependency edge per epoch, so a long chain exhausts the cap
before the change reaches the top importer.

The coarse fallback was meant to invalidate the whole affected closure and
recompute it, but it seeded `invalidate_rdeps` from `changed` — which the final
loop iteration had already drained — making it a no-op. A single `run_step`
then left `changed`/`dirty` populated, tripping the `commit_transaction`
invariant.

Seed the coarse invalidation from the pending `dirty` frontier instead, then
recompute to a fixpoint via `stabilize_after_invalidate`. This converges
quickly because lookups pull their dependencies: once the closure is
invalidated, a couple of `run_step`s recompute it with correct values and
drain the bookkeeping the commit invariant requires. A bounded loop with a
final residual-clear keeps a pathological oscillation from hanging or
committing inconsistent state.

Differential Revision: D112591162
@meta-codesync

meta-codesync Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@kinto0 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D112591162.

Comment on lines +2162 to +2171
// Even coarse invalidation did not stabilize (a pathological oscillation). Clear the
// residual `changed`/`dirty` so the transaction commits in a consistent state
// (last-computed values) rather than tripping a `commit_transaction` assertion.
tracing::warn!(
"Exceeded maximum epochs ({MAX_EPOCHS}) without stabilizing. \
This may indicate an unexpected dependency pattern. Forcing invalidation."
"Recheck did not stabilize within {} epochs; clearing residual state to commit consistently.",
2 * MAX_EPOCHS
);
let changed = mem::take(&mut *self.data.changed.lock());
self.invalidate_rdeps(changed.into_map(|(m, _)| m));
self.run_step(handles, require, custom_thread_pool)
self.data.changed.lock().clear();
self.data.dirty.lock().clear();
Ok(())

@kinto0 kinto0 Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this will hide bugs - we can't have this fallback

@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant