Skip to content

Cache --only / --only-suffix runs under a rule-scoped key#8075

Open
SanderMuller wants to merge 1 commit into
rectorphp:mainfrom
SanderMuller:perf/only-rule-cache-scope
Open

Cache --only / --only-suffix runs under a rule-scoped key#8075
SanderMuller wants to merge 1 commit into
rectorphp:mainfrom
SanderMuller:perf/only-rule-cache-scope

Conversation

@SanderMuller

Copy link
Copy Markdown
Contributor

Why

#8029 fixed --only / --only-suffix runs poisoning the full-run cache by not caching them at all.
As @samsonasik noted #8029 (comment), that removed the cache on repeated selective runs, and #7641 tried to bring it back but hit cache thrash.

This scopes the per-file cache key by the active rule selection, so selective and full runs use
separate, coexisting cache entries:

A full run keys exactly as before, existing caches aren't invalidated and
full-run behaviour is unchanged. That also makes it trivially revertable.

Verify

The existing clean-file caching test is unchanged (full-run caching still works). A new test covers the selective case: after an --only run the file is cached under its own scope (a repeat --only is served from cache), while a full run treats it as changed, so a pending change can't be masked. Checked in parallel too.

Warm repeated --only on rector's own rules/ (~790 files, one rule): ~14s reprocess on main
~0.3s served from cache. That's the warm-repeat best case — the win is skipping the reprocess on repeats.

rectorphp#8029 stopped caching selective runs to fix full-run poisoning (a file clean
under one rule was cached as clean for all). That also removed the cache on
repeated --only / --only-suffix runs — a drawback raised in that thread, and
what rectorphp#7641 set out to solve.

Scope the per-file cache key by the active rule selection instead. Selective and
full runs then use distinct, coexisting cache entries: a repeated --only run is
served from cache, a full run is never poisoned by it, and nothing is cleared, so
there is no back-and-forth thrash (the behaviour that closed rectorphp#7641).

The scope is set in both run() and processFiles() because parallel workers invoke
processFiles() directly via WorkerCommand, bypassing run(), and must write entries
under the same scope the main process reads.
@SanderMuller

Copy link
Copy Markdown
Contributor Author

Benchmark for the repeated --only case this restores — hyperfine, 5 runs after a warmup, on this repo's own rules/ (790 files), sequential, --dry-run, a single rule:

  • main: 15.50 s ± 0.43 s — no --only cache, so every run reprocesses
  • this branch: 0.45 s ± 0.03 s — served from the scoped cache

~34× on the warm repeat. Cold/first runs are unchanged, and full runs key exactly as before, so existing caches aren't touched — the gain is purely the avoided reprocess on repeated selective runs.

@TomasVotruba TomasVotruba requested a review from samsonasik June 21, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant