Cache integration API reads and refresh them on a schedule
Deploy HickLab3D Dashboard / Deploy to production (push) Successful in 28s
Deploy HickLab3D Dashboard / Deploy to production (push) Successful in 28s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
8bee8d42c7
commit
2fe4fbd91e
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Read-through cache for slow third-party API calls.
|
||||
*
|
||||
* Fresh entries are served instantly, expired entries fall back to the last good value if the
|
||||
* upstream call fails, and a short failure cooldown stops every page load waiting on a dead API.
|
||||
* The scheduled `integrations:refresh` command runs with $forceRefresh enabled to keep it warm.
|
||||
*/
|
||||
class IntegrationCache
|
||||
{
|
||||
private const RETAIN_SECONDS = 86400;
|
||||
|
||||
private const FAILURE_COOLDOWN_SECONDS = 30;
|
||||
|
||||
public bool $forceRefresh = false;
|
||||
|
||||
/**
|
||||
* @template TValue
|
||||
*
|
||||
* @param Closure(): TValue $fetch
|
||||
* @return TValue
|
||||
*/
|
||||
public function remember(string $key, int $freshSeconds, Closure $fetch, bool $fresh = false): mixed
|
||||
{
|
||||
$cacheKey = 'integration:'.$key;
|
||||
$entry = Cache::get($cacheKey);
|
||||
$bypass = $fresh || $this->forceRefresh;
|
||||
|
||||
if (! $bypass && is_array($entry) && time() < $entry['fetched_at'] + $freshSeconds) {
|
||||
return $entry['value'];
|
||||
}
|
||||
if (! $bypass && ! is_array($entry) && ($failure = Cache::get($cacheKey.':failed'))) {
|
||||
throw new RuntimeException((string) $failure);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = $fetch();
|
||||
} catch (Throwable $exception) {
|
||||
if (is_array($entry)) {
|
||||
report($exception);
|
||||
|
||||
return $entry['value'];
|
||||
}
|
||||
Cache::put($cacheKey.':failed', $exception->getMessage(), self::FAILURE_COOLDOWN_SECONDS);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
Cache::put($cacheKey, ['value' => $value, 'fetched_at' => time()], self::RETAIN_SECONDS);
|
||||
Cache::forget($cacheKey.':failed');
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function forget(string $key): void
|
||||
{
|
||||
Cache::forget('integration:'.$key);
|
||||
Cache::forget('integration:'.$key.':failed');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user