diff --git a/src/Lifecycle/Broker.php b/src/Lifecycle/Broker.php index 39445813..237c6c9e 100644 --- a/src/Lifecycle/Broker.php +++ b/src/Lifecycle/Broker.php @@ -81,12 +81,14 @@ public function replay(?callable $beforeEach = null, ?callable $afterEach = null { $this->is_replaying = true; + $state_manager = app(StateManager::class); + try { - app(StateManager::class)->reset(include_storage: true); + $state_manager->reset(include_storage: true); app(StoresEvents::class)->read() - ->each(function (Event $event) use ($beforeEach, $afterEach) { - app(StateManager::class)->setReplaying(true); + ->each(function (Event $event) use ($state_manager, $beforeEach, $afterEach) { + $state_manager->setReplaying(true); if ($beforeEach) { $beforeEach($event); @@ -99,10 +101,12 @@ public function replay(?callable $beforeEach = null, ?callable $afterEach = null $afterEach($event); } + $state_manager->prune(); + return $event; }); } finally { - app(StateManager::class)->setReplaying(false); + $state_manager->setReplaying(false); $this->is_replaying = false; } } diff --git a/src/Lifecycle/StateManager.php b/src/Lifecycle/StateManager.php index 8c7efa3a..2d65e667 100644 --- a/src/Lifecycle/StateManager.php +++ b/src/Lifecycle/StateManager.php @@ -11,7 +11,7 @@ use Thunk\Verbs\Exceptions\StateCacheSizeTooLow; use Thunk\Verbs\Facades\Id; use Thunk\Verbs\State; -use Thunk\Verbs\Support\LeastRecentlyUsedCache; +use Thunk\Verbs\Support\StateInstanceCache; use UnexpectedValueException; class StateManager @@ -22,7 +22,7 @@ public function __construct( protected Dispatcher $dispatcher, protected StoresSnapshots $snapshots, protected StoresEvents $events, - protected LeastRecentlyUsedCache $states, + protected StateInstanceCache $states, ) { $this->states->onDiscard(fn () => throw_unless($this->is_replaying, StateCacheSizeTooLow::class)); } @@ -102,6 +102,13 @@ public function reset(bool $include_storage = false): static return $this; } + public function prune(): static + { + $this->states->prune(); + + return $this; + } + protected function reconstitute(State $state, bool $singleton = false): static { // When we're replaying, the Broker is in charge of applying the correct events diff --git a/src/Support/LeastRecentlyUsedCache.php b/src/Support/StateInstanceCache.php similarity index 90% rename from src/Support/LeastRecentlyUsedCache.php rename to src/Support/StateInstanceCache.php index b329f4a7..fe1beeea 100644 --- a/src/Support/LeastRecentlyUsedCache.php +++ b/src/Support/StateInstanceCache.php @@ -4,7 +4,7 @@ use Closure; -class LeastRecentlyUsedCache +class StateInstanceCache { public function __construct( protected int $capacity = 100, @@ -43,11 +43,6 @@ public function put(string|int $key, mixed $value): static $this->cache[$key] = $value; - if (count($this->cache) > $this->capacity) { - reset($this->cache); - $this->forget(key($this->cache)); - } - return $this; } @@ -67,6 +62,13 @@ public function forget(string|int $key): static return $this; } + public function prune(): static + { + $this->cache = array_slice($this->cache, -1 * $this->capacity, null, true); + + return $this; + } + public function values(): array { return $this->cache; diff --git a/src/VerbsServiceProvider.php b/src/VerbsServiceProvider.php index d03fce69..8d8e4fa2 100644 --- a/src/VerbsServiceProvider.php +++ b/src/VerbsServiceProvider.php @@ -34,8 +34,8 @@ use Thunk\Verbs\Livewire\SupportVerbs; use Thunk\Verbs\Support\EventStateRegistry; use Thunk\Verbs\Support\IdManager; -use Thunk\Verbs\Support\LeastRecentlyUsedCache; use Thunk\Verbs\Support\Serializer; +use Thunk\Verbs\Support\StateInstanceCache; use Thunk\Verbs\Support\Wormhole; class VerbsServiceProvider extends PackageServiceProvider @@ -77,7 +77,7 @@ public function packageRegistered() dispatcher: $app->make(Dispatcher::class), snapshots: $app->make(StoresSnapshots::class), events: $app->make(StoresEvents::class), - states: new LeastRecentlyUsedCache( + states: new StateInstanceCache( capacity: $app->make(Repository::class)->get('verbs.state_cache_size', 100) ), ); @@ -156,7 +156,6 @@ public function boot() $this->app->terminating(function () { app(AutoCommitManager::class)->commitIfAutoCommitting(); - app(StateManager::class)->reset(include_storage: false); }); // Hook into Laravel event dispatcher @@ -175,7 +174,6 @@ protected function handleEvent($event = null) // Auto-commit after each job on the queue is processed if ($event instanceof JobProcessed) { app(AutoCommitManager::class)->commitIfAutoCommitting(); - app(StateManager::class)->reset(include_storage: false); } } } diff --git a/tests/Unit/LruCacheTest.php b/tests/Unit/LruCacheTest.php index 052c78fe..87bce3f9 100644 --- a/tests/Unit/LruCacheTest.php +++ b/tests/Unit/LruCacheTest.php @@ -1,9 +1,9 @@ put('a', 1)->put('b', 2)->put('c', 3)->put('d', 4)->put('e', 5); @@ -11,21 +11,13 @@ $lru->put('f', 6); - expect($lru->has('a'))->toBeFalse() - ->and($lru->has('f'))->toBeTrue() - ->and($lru->values())->toBe(['b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6]); + expect($lru->values())->toBe(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6]); $lru->get('b'); - expect($lru->values())->toBe(['c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'b' => 2]); - - $lru->put('a', 1); + expect($lru->values())->toBe(['a' => 1, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'b' => 2]); - expect($lru->has('c'))->toBeFalse() - ->and($lru->values())->toBe(['d' => 4, 'e' => 5, 'f' => 6, 'b' => 2, 'a' => 1]); + $lru->prune(); - $lru->forget('e'); - - expect($lru->has('e'))->toBeFalse() - ->and($lru->values())->toBe(['d' => 4, 'f' => 6, 'b' => 2, 'a' => 1]); + expect($lru->values())->toBe(['c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'b' => 2]); });