Repository navigation
perf: Hot reload compares a sibling with its snapshot again only when the file's length or write time changed - #3241
Conversation
The cache will let hot reload skip re-reading a sibling source whose length and write time are unchanged. The stub never remembers a verdict, so the tests that expect a recorded verdict back fail.
The verdict is keyed by the snapshot path and the source path together, and a changed stamp reads as no verdict, so the caller always re-reads and records again instead of trusting a stale answer.
Two of them fail today because every scan reads the file again: a file rewritten while its length and write time stay the same should keep the earlier verdict, in both directions. The other three guard that a new length, a new write time, or another snapshot directory still compares again.
…e unchanged Every hot reload run read each source of the edited assembly and its snapshot to find drifted siblings, which takes hundreds of milliseconds on a large assembly. A file compared once in this domain is now read again only when its stamp changes; the memo is cleared when a compile starts.
A rewrite that keeps both the length and the write time is now noticed only after the next domain reload, so the limit is stated beside the baseline description.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (3)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review. 📝 WalkthroughWalkthroughThe hot-reload detector now caches source-to-snapshot comparison verdicts using source length and UTC write-time ticks. Compilation start clears the cache. Tests cover cache behavior and sibling scans, and reference text describes when comparisons are repeated. ChangesHot-reload sibling verdict caching
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Other Sequence Diagram(s)sequenceDiagram
participant Detector as HotReloadChangedSiblingSourceDetector
participant Source as Source file
participant Snapshot as Snapshot file
participant Cache as HotReloadSiblingVerdictCache
participant Compilation as CompilationPipeline
Detector->>Source: Read length and UTC write-time ticks
Detector->>Cache: TryGetVerdict for snapshot path, source path, and source stamp
alt Cached verdict
Cache-->>Detector: Return comparison result
else Missing or stale verdict
Detector->>Source: Read source bytes
Detector->>Snapshot: Read snapshot bytes
Detector->>Cache: Record comparison result with source stamp
Cache-->>Detector: Store verdict
end
Compilation-->>Cache: compilationStarted clears verdicts
Merge Risk: ⚪ Minimal · up to No merge-blocking issue is established in the reviewed change; it is ready for normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change is confined to hot-reload behavior within an Editor session. Timestamp-preserving rewrites can retain an earlier decision, and overlapping compilation remains partly unvalidated. No newly exposed capability or verified security issue was identified. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 70.59% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 34 functions across 5 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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 |
The rebind planner reaches the cache from a thread-pool continuation while compilationStarted clears it on the main thread.
With a later write time as well, a comparison that ignored the length still passed the test.
Selecting files when --files is omitted goes through the same comparison, so a same-stamp rewrite is not selected until the next compile while a later write time still selects it.
The reuse affects sibling drift, re-applying live patches after a skip, and the default file selection, not only the sibling scan.
084e1d1
into
feature/hot-reload-large-project-feedback-3
Summary
Why
On a large project (one edited assembly with about 1,300 sources, 26 MB), the
sibling_detectstep ofhot_reload_timing_detailtook 228–457 ms per run.statof the same files takes 16–19 ms.Design
CompilationPipeline.compilationStarted, the same as the call-site scanner's memo. A compile that fails keeps the domain alive, so without the clear it would keep verdicts for an old generation.compilationStartedclears the cache on the main thread.FileInfogives existence, length and write time from a single stat.HasBaseline/IsComplete.Behaviour change
A source rewritten while it keeps both its length and its write time (for example a copy that preserves timestamps) keeps its previous verdict, "matches" or "differs", until the next domain reload such as
uloop compile.This applies on every path that compares a source with its baseline:
--filesis omitted.Editor and IDE saves,
git checkoutandstash popall update the write time. The tests pin this limit in both directions, and the hot-reload reference now states it beside the baseline description.Changes
HotReloadSiblingVerdictCache: the verdict memo, guarded by a lock.HotReloadChangedSiblingSourceDetectorcompares through the memo in both comparison helpers:scope-and-limits.mdgains two sentences, naming all three paths; the generated.claude/.agentscopies are regenerated.Verification
Repository CI does not run on pull requests into this integration branch. Everything below was run locally against a running Editor.
Compile:
uloop compilereports 0 errors.Before the fix (Red):
HotReloadSiblingVerdictCacheTests, against a stub that never remembers, only the 2 tests that expect a recorded verdict failed.HotReloadChangedSiblingSourceDetectorTests, before the detector change, only the two same-stamp tests failed:SecondScanWithSameStamp_KeepsTheMatchVerdict(got 1 path, expected none);SecondScanWithSameStamp_KeepsTheDiffersVerdict(got none, expected 1).Tests (single-flight
run-tests, regex filters):HotReloadChangedSiblingSourceDetectorTests(5 new),HotReloadSiblingVerdictCacheTests(8 new),HotReloadChangedSourceDetectorTests(2 new, default file selection),HotReloadChangedFileAggregatorTests,HotReloadSiblingRebindWarningSelectorTests,HotReloadSiblingBaselineNoticesTests,HotReloadRunSiblingLedgerUpdatesTestsHotReloadSiblingCompanionE2ETests,HotReloadFieldOnlySiblingE2ETests,HotReloadStaleRowSiblingE2ETests,HotReloadIntroducedTypeNewFileSiblingE2ETestsMutations. Each was applied after the commit, compiled, run against the detector and cache tests, and reverted.
SecondScanAfterRewriteWithNewLength,SecondScanAfterRewriteWithNewWriteTime, and the cache'sDifferentLength/DifferentWriteTimeSecondScanWithSameStamp_*sibling tests, andDetectAllChangedFromSnapshotDirectory_SecondScanWithSameStamp_DoesNotSelectTheRewrite(run separately)SameSiblingAgainstAnotherSnapshotDirectory_ComparesAgainSecondScanWithSameStamp_KeepsTheDiffersVerdictSecondScanAfterRewriteWithNewLength(the rewrite keeps the write time, so only the length differs)After reverting, all 29 tests pass at head.
Static checks: both pass.
Not covered
CompilationPipeline.GetAssemblies()and the coldsnapshot_group_statecost are left for separate changes.