Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/Stache/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public function boot()
$this->registerStores($stache);

$this->resetMemoizedStateBetweenJobs($stache);

$this->resetMemoizedStateBetweenOctaneRequests($stache);
}

private function registerStores($stache)
Expand Down Expand Up @@ -88,6 +90,22 @@ private function resetMemoizedStateBetweenJobs($stache)
});
}

/**
* The stores are made when the app boots, so under Octane a worker uses the same ones
* for every request it handles. What they remember (their paths, for one) has to be
* read again for each request, or the worker never sees what another process saves.
*/
private function resetMemoizedStateBetweenOctaneRequests($stache)
{
// Listened for by name, since Octane isn't necessarily installed.
Event::listen('Laravel\Octane\Events\RequestReceived', function () use ($stache) {
$stache->stores()->each->resetMemoizedState();

// Octane handles each request in its own copy of the container.
app('stache.indexes')->each->resetMemoizedState();
});
}

private function locks()
{
if (config('statamic.stache.lock.enabled', true)) {
Expand Down
30 changes: 30 additions & 0 deletions tests/Stache/ServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ public function it_resets_memoized_store_and_index_state_between_jobs_when_runni
$this->assertEquals(2, $hits->filter(fn ($key) => $key === $idIndexCacheKey)->count());
}

#[Test]
public function it_resets_memoized_store_and_index_state_when_an_octane_request_arrives()
{
$store = (new ServiceProviderTestStore)->directory('/path/to/directory');
Stache::registerStore($store);

$pathsCacheKey = "stache::indexes::{$store->key()}::path";
$idIndexCacheKey = "stache::indexes::{$store->key()}::id";

Cache::put($pathsCacheKey, ['alfa' => 'alfa.md']);
Cache::put($idIndexCacheKey, ['alfa' => 'alfa']);

// A request to a worker uses the store, which is the same one for every request it handles.
$this->assertEquals(['alfa'], $store->paths()->keys()->all());
$this->assertEquals(['alfa'], $store->index('id')->keys());

// Another process saves an item.
Cache::put($pathsCacheKey, ['alfa' => 'alfa.md', 'bravo' => 'bravo.md']);
Cache::put($idIndexCacheKey, ['alfa' => 'alfa', 'bravo' => 'bravo']);

$this->assertEquals(['alfa'], $store->paths()->keys()->all());
$this->assertEquals(['alfa'], $store->index('id')->keys());

// Octane isn't a dependency, so its event is fired by name, as the listener is registered.
Event::dispatch('Laravel\Octane\Events\RequestReceived');

$this->assertEquals(['alfa', 'bravo'], $store->paths()->keys()->all());
$this->assertEquals(['alfa', 'bravo'], $store->index('id')->keys());
}

#[Test]
public function it_does_not_reset_memoized_state_for_jobs_processed_outside_a_worker()
{
Expand Down
Loading