fix: event-based cache invalidation for mid-request view clears#107
fix: event-based cache invalidation for mid-request view clears#107erhanurgun wants to merge 3 commits into
Conversation
Benchmark Results
Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 46.34s total |
|
@erhanurgun I don't think this would work, as the Command is run in a different PHP-Process, which uses their own static variables and event-handler. |
If you know how it shouldn't work, you should also know how it should work... How should we fix this problem? Do you have any suggestions? |
|
My recommendation is to use Zero-Downtime Deployments, so the files are not deleted during a request. The request can still finish in the old Directory, while new requests are handled in the new Directory. I wonder why this problem only occurs with blaze and not with a normal blade. 🤔 Though I don't have any idea for a performant approach. Process to Process Communcation would create a lot of different Problems in my opinion. |
|
Okay it seems I didn't catch the mid-request Part and assumed that another process would clear the files. |
|
@erhanurgun I'm not sure if we can reliably prevent this. This PR only fixes the issue when you run For now I'm leaning towards leaving it as it is. The It's probably not recommended to run You could hit this during deployment while running optimize and also serving requests, but as mentioned the proper solution there is zero downtime deployment. Let me know what do you think but unless there is a more performant solution or a more convincing reason that justifies 30% slowdown I don't think we'll go through with this one. |
Instead of adding a file_exists() check to the hot path (which caused ~40% performance regression), listen for the view:clear command via CommandFinished event and reset both BlazeRuntime's in-memory compilation cache and Laravel's Component view string cache when compiled files are actually deleted. This preserves zero-overhead in-memory caching during normal requests while correctly handling mid-request cache clears (e.g. optimize:clear). Fixes livewire#96
|
@juliuskiekbusch, Issue: |
b2fdeb8 to
203e093
Compare
|
@ganyicz Thanks for the detailed feedback -- really appreciate the thoughtful analysis. I think there might be a mix-up between this PR and the reverted #97, so I want to clarify the performance point first: This PR has zero overhead -- not 30%. The 30% regression came from #97, which added This is the "more performant solution" you mentioned -- it costs literally nothing during normal operation. On scope and use cases: You're absolutely right that calling
Cross-process invalidation (terminal Given this costs nothing at runtime and fixes a real, reproducible edge case, I believe it's worth including. That said, I don't have an alternative approach beyond what's already been explored (#97's |
|
I understand this PR is overhead-free but it doesn't fix the underlying issue, the I thought I understand the need for this in tests but as you pointed out we're also running A better solution here would be to fix the core issue, which is Blaze failing when a compiled file gets deleted mid-request. If we had that, we wouldn't need this workaround. I'll see if I can come up with an overhead-free approach. |
Summary
file_exists()hot-path check (fix: add file_exists check to ensureCompiled in-memory cache guard #97, reverted in Revert "fix: add file_exists check to ensureCompiled in-memory cache guard" #106 due to ~40% perf regression) with a zero-overhead event-based approachCommandFinishedwhenview:clearruns and resets bothBlazeRuntime::$compiledin-memory cache and Laravel'sComponentstatic view string cacheDetails
The original issue: calling
Artisan::call('optimize:clear')(orview:clear) mid-request deletes compiled view files, butBlazeRuntime::$compiledstill marks them as compiled. Subsequentresolve()calls skip recompilation andrequire_oncefails on the missing file.The previous fix added
file_exists()to theensureCompiled()guard, but this introduced astat()syscall on every component resolution -- benchmarks showed ~40% regression (33ms vs 20ms).New approach: Register a
CommandFinishedevent listener inBlazeServiceProvider::boot(). Whenview:clearfinishes:BlazeRuntime::clearCompiled()resets the$compiledarray soresolve()goes through the normal compilation flowComponent::flushCache()clears Laravel's static$bladeViewCachesocreateBladeViewFromString()recreates temporary source files thatview:cleardeletedScope: This fixes same-process
Artisan::call()scenarios (mid-request calls, test suites, queue workers). Cross-process invalidation (terminal commands during an active request) is an inherent race condition in any in-memory cache approach and requires zero-downtime deployment strategies instead.Rebased onto current main (post-#95 DI refactoring).
Test plan
FileNotFoundException)Fixes #96