diff --git a/app/Listeners/CacheListener.php b/app/Listeners/CacheListener.php index 476821aec10..f685792d06e 100644 --- a/app/Listeners/CacheListener.php +++ b/app/Listeners/CacheListener.php @@ -38,7 +38,9 @@ public function handle(CacheHit|CacheMissed|KeyForgotten|KeyWritten $event): voi CacheHit::class => Log::debug('CacheListener: Hit for ' . $event->key), KeyForgotten::class => Log::info('CacheListener: Forgetting key ' . $event->key), KeyWritten::class => $this->keyWritten($event), + // @codeCoverageIgnoreStart default => '', + // @codeCoverageIgnoreEnd }; } diff --git a/app/Metadata/Cache/RouteCacher.php b/app/Metadata/Cache/RouteCacher.php index ddca4dc78a2..3e52a6a0e4e 100644 --- a/app/Metadata/Cache/RouteCacher.php +++ b/app/Metadata/Cache/RouteCacher.php @@ -11,6 +11,7 @@ use App\Exceptions\Internal\LycheeLogicException; use Closure; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Log; /** * RouteCacher also associate the route data with the cache key. @@ -51,13 +52,20 @@ public function remember( } $value = $callback(); - Cache::put($key, $value, $ttl); - - // Update the list of keys for the given route. - $this->rememberRoute($route, $key); - - // Update the tags for the given key. - $this->rememberTags($tags, $key); + try { + Cache::put($key, $value, $ttl); + + // Update the list of keys for the given route. + $this->rememberRoute($route, $key); + + // Update the tags for the given key. + $this->rememberTags($tags, $key); + // @codeCoverageIgnoreStart + } catch (\Exception $e) { + // If we can't cache the value, we will just return the value. + Log::error(__METHOD__ . ':' . __LINE__ . ' Could not cache the value.', ['exception' => $e]); + } + // @codeCoverageIgnoreEnd return $value; } diff --git a/database/migrations/2025_02_15_090409_bump_version060301.php b/database/migrations/2025_02_15_090409_bump_version060301.php new file mode 100644 index 00000000000..db2fdbf4e85 --- /dev/null +++ b/database/migrations/2025_02_15_090409_bump_version060301.php @@ -0,0 +1,34 @@ +where('key', 'cache_ttl')->update(['value' => '86400']); + DB::table('configs')->where('key', 'version')->update(['value' => '060301']); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + DB::table('configs')->where('key', 'cache_ttl')->update(['value' => '300']); + DB::table('configs')->where('key', 'version')->update(['value' => '060300']); + } +}; diff --git a/tests/Unit/Caching/CacheListenerTest.php b/tests/Unit/Caching/CacheListenerTest.php new file mode 100644 index 00000000000..cb14a7cb96e --- /dev/null +++ b/tests/Unit/Caching/CacheListenerTest.php @@ -0,0 +1,111 @@ +update(['value' => '0']); + Configs::invalidateCache(); + parent::tearDown(); + } + + public function testCacheListenerNever(): void + { + Log::shouldReceive('debug')->never(); + Log::shouldReceive('info')->never(); + + Configs::where('key', 'cache_event_logging')->update(['value' => '0']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new CacheMissed('store', 'key')); + $listener->handle(new CacheMissed('store', 'lv:dev-lycheeOrg')); + } + + public function testCacheListenerMissed(): void + { + Log::shouldReceive('debug')->once()->with('CacheListener: Miss for key'); + Log::shouldReceive('info')->never(); + + Configs::where('key', 'cache_event_logging')->update(['value' => '1']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new CacheMissed('store', 'key')); + } + + public function testCacheListenerHit(): void + { + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for key'); + Log::shouldReceive('info')->never(); + + Configs::where('key', 'cache_event_logging')->update(['value' => '1']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new CacheHit('store', 'key', 'value')); + } + + public function testCacheListenerKeyForgotten(): void + { + Log::shouldReceive('debug')->never(); + Log::shouldReceive('info')->once()->with('CacheListener: Forgetting key key'); + + Configs::where('key', 'cache_event_logging')->update(['value' => '1']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new KeyForgotten('store', 'key')); + } + + public function testCacheListenerKeyWritten(): void + { + Log::shouldReceive('debug')->never(); + Log::shouldReceive('info')->once()->with('CacheListener: Writing key key'); + + Configs::where('key', 'cache_event_logging')->update(['value' => '1']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new KeyWritten('store', 'key', 'value')); + } + + public function testCacheListenerKeyWrittenApi(): void + { + Log::shouldReceive('info')->never(); + Log::shouldReceive('debug')->once()->with('CacheListener: Writing key api/key with value: \'value\''); + + Configs::where('key', 'cache_event_logging')->update(['value' => '1']); + Configs::invalidateCache(); + + $listener = new CacheListener(); + $listener->handle(new KeyWritten('store', 'api/key', 'value')); + } +} diff --git a/tests/Unit/Caching/RouteCacherTest.php b/tests/Unit/Caching/RouteCacherTest.php new file mode 100644 index 00000000000..2485b51bf0e --- /dev/null +++ b/tests/Unit/Caching/RouteCacherTest.php @@ -0,0 +1,128 @@ +update(['value' => '1']); + Configs::invalidateCache(); + } + + public function tearDown(): void + { + Configs::where('key', 'cache_event_logging')->update(['value' => '0']); + Configs::invalidateCache(); + parent::tearDown(); + } + + public function testRouteCacherHit(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('info')->once(); + Cache::put('key', 60); + + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for key'); + Log::shouldReceive('info')->never(); + + $routeCacher->remember('key', 'route', 60, function () { + return 60; + }, ['tags']); + } + + public function testRouteCacherMiss(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('debug')->once()->with('CacheListener: Miss for key'); + Log::shouldReceive('debug')->once()->with('CacheListener: Miss for route'); + Log::shouldReceive('debug')->once()->with('CacheListener: Miss for T:tags'); + Log::shouldReceive('info')->once()->with('CacheListener: Writing key key'); + Log::shouldReceive('info')->once()->with('CacheListener: Writing key route'); + Log::shouldReceive('info')->once()->with('CacheListener: Writing key T:tags'); + + $routeCacher->remember('key', 'route', 60, function () { + return 60; + }, ['tags']); + } + + public function testRouteCacherForgetRouteException(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('info')->once(); + Cache::put('route', [60]); + + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for route'); + + $this->expectException(LycheeLogicException::class); + $routeCacher->forgetRoute('route'); + Cache::forget('route'); + } + + public function testRouteCacherForgetRoute(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('info')->twice(); + Cache::put('route', ['forgetMe' => 'value']); + Cache::put('forgetMe', 'value'); + + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for route'); + Log::shouldReceive('info')->once()->with('CacheListener: Forgetting key forgetMe'); + Log::shouldReceive('info')->once()->with('CacheListener: Forgetting key route'); + + $routeCacher->forgetRoute('route'); + } + + public function testRouteCacherForgetTagException(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('info')->once(); + Cache::put('T:tag', [60]); + + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for T:tag'); + + $this->expectException(LycheeLogicException::class); + $routeCacher->forgetTag('tag'); + Cache::forget('T:tag'); + } + + public function testRouteCacherForgetTag(): void + { + $routeCacher = new RouteCacher(); + Log::shouldReceive('info')->twice(); + Cache::put('T:tag', ['forgetMe' => 'value']); + Cache::put('forgetMe', 'value'); + + Log::shouldReceive('debug')->once()->with('CacheListener: Hit for T:tag'); + Log::shouldReceive('info')->once()->with('CacheListener: Forgetting key forgetMe'); + Log::shouldReceive('info')->once()->with('CacheListener: Forgetting key T:tag'); + + $routeCacher->forgetTag('tag'); + } +} diff --git a/version.md b/version.md index e7e42a4b585..39ee137ba59 100644 --- a/version.md +++ b/version.md @@ -1 +1 @@ -6.3.0 \ No newline at end of file +6.3.1 \ No newline at end of file