Skip to content

Conversation

@MichaReiser
Copy link
Contributor

@MichaReiser MichaReiser commented Oct 24, 2025

This PR fixes a assertion left == right failed: Can't merge cycle heads, but panicked with ClassLiteral < 'db >::is_typed_dict_(Id(3007)): execute: too many cycle iterations instead.` panic in ty.

This panic is normally due to:

  1. It's a multithreading issue where one thread returns its result too early, before the outer cycle completes.
  2. Salsa doesn't re-execute a query when it should (validate_same_iteration incorrectly returns false)

I can rule out 1. because I can reproduce the issue when running ty single-threaded. So it's two.

The smallest example I've been able to reduce it to is https://github.com/astral-sh/ruff/pull/21059/files#diff-601b44c1045125b03854a01be732f5bbda7b9b70673cbdc983bf9acc952c0a0d which still produced ~300MB of log files. Now, I didn't trace through the log file entirely (I also asked claude and it was very confused and always wanted to remove the assertion because that's obviously the correct fix to make the panic go away...), but my understanding of what's happening (and the fix confirms) is.:

  • There's a nested cycle with the outermost query A
  • B participates in the cycle and is a cycle head in the first few iterations
  • B becomes a non-cycle head in a later iteration
  • There's a query C that has B as its cycle head

The crucial point is that B switches from being a cycle head to being a regular cycle participant. The issue with that is that A doesn't update B's iteration_count when the iteration completes because it only does that for cycle heads (and collecting all queries participating in a query would be sort of expensive?).

When we now pull C in a later iteration, validate_same_iteration iterates over all its cycle heads (B), and check if the iteration count still matches. Which is the case because A didn't update B's iteration count.

The solution in this PR is that validate_same_iteration should also check whether the most recent memo for a query's cycle head is still a cycle head. If it isn't, then the query is obviously outdated and needs to be re-run.

Testing

I tried very hard to write a test, I also employed Claude (with different models) and, while we all produced a lot of test code, none of us managed to write a test that triggers this condition successfully.

That's why I opted to write a regression test in ty instead astral-sh/ruff#21059

@netlify
Copy link

netlify bot commented Oct 24, 2025

Deploy Preview for salsa-rs canceled.

Name Link
🔨 Latest commit d0a8113
🔍 Latest deploy log https://app.netlify.com/projects/salsa-rs/deploys/68fb7e38007dcb00083580d3

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 24, 2025

CodSpeed Performance Report

Merging #1014 will degrade performances by 6.01%

Comparing MichaReiser:fix-head-non-head (d0a8113) with master (d38145c)

Summary

❌ 2 (👁 2) regressions
✅ 11 untouched

Benchmarks breakdown

Benchmark BASE HEAD Change
👁 amortized[Input] 2.1 µs 2.2 µs -5.28%
👁 amortized[SupertypeInput] 2.7 µs 2.9 µs -6.01%

@MichaReiser MichaReiser requested review from carljm and ibraheemdev and removed request for carljm October 24, 2025 13:21
@MichaReiser MichaReiser added bikeshed 🚴‍♀️ Debating API details and the like bug Something isn't working and removed bikeshed 🚴‍♀️ Debating API details and the like labels Oct 24, 2025
@MichaReiser MichaReiser marked this pull request as ready for review October 24, 2025 13:22
Provisional {
iteration: IterationCount,
verified_at: Revision,
cycle_heads: &'db CycleHeads,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided to merge Ingredient::cycle_heads and Ingredient::provisional_status into provisional_status so that validate_same_iteration doesn't require an additional method call

@MichaReiser MichaReiser added this pull request to the merge queue Oct 24, 2025
Merged via the queue into salsa-rs:master with commit 25b3ef1 Oct 24, 2025
19 checks passed
@MichaReiser MichaReiser deleted the fix-head-non-head branch October 24, 2025 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants