-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/l1 memcached cache middleware #412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,5 +1,6 @@ | ||||||
| <?php namespace App\Http\Middleware; | ||||||
|
|
||||||
| use App\Utils\Cache\MemCache; | ||||||
| use Closure; | ||||||
| use Illuminate\Http\JsonResponse; | ||||||
| use Illuminate\Support\Facades\Cache; | ||||||
|
|
@@ -87,49 +88,82 @@ public function handle($request, Closure $next, $cache_lifetime, $cache_region = | |||||
| } | ||||||
| } | ||||||
| $status = 200; | ||||||
| $wasMemCacheHit = false; | ||||||
| $wasHit = false; | ||||||
| $data = null; | ||||||
|
|
||||||
| if ($regionTag) { | ||||||
| Log::debug("CacheMiddleware: using region tag {$regionTag} ip {$ip} agent {$agent}"); | ||||||
| $wasHit = Cache::tags($regionTag)->has($key); | ||||||
| Log::debug($wasHit ? "CacheMiddleware: cache HIT (tagged)" : "CacheMiddleware: cache MISS (tagged)", [ | ||||||
| 'tag' => $regionTag, | ||||||
| 'ip' => $ip, | ||||||
| 'agent' => $agent, | ||||||
| 'key' => $key, | ||||||
| ]); | ||||||
|
|
||||||
| $encoded = Cache::tags($regionTag) | ||||||
| ->remember($key, $cache_lifetime, function() use ($next, $request, $regionTag, $key, $cache_lifetime, &$status,$ip, $agent) { | ||||||
| // try L1 APC | ||||||
| $encoded = MemCache::get($key); | ||||||
| $wasMemCacheHit = $encoded !== null; | ||||||
| if($wasMemCacheHit){ | ||||||
| Log::debug("CacheMiddleware:: MemcCache Hit"); | ||||||
|
||||||
| Log::debug("CacheMiddleware:: MemcCache Hit"); | |
| Log::debug("CacheMiddleware:: Memcached Hit"); |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment incorrectly refers to 'APC' but the code is using memcached. Should be 'backfill memcached only if we actually have a value'.
| // backfill APC only if we actually have a value | |
| // backfill memcached only if we actually have a value |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment incorrectly refers to 'APC' but the code is using memcached. Should be 'try L1 memcached'.
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in log message - 'MemcCache' should be 'Memcached'.
| Log::debug("CacheMiddleware:: MemcCache Hit"); | |
| Log::debug("CacheMiddleware:: Memcached Hit"); |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment incorrectly refers to 'APC' but the code is using memcached. Should be 'store at memcached'.
| // store at APC | |
| // store at memcached |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in header value - 'MemcCache' should be 'Memcached'.
| 'X-Cache-Result' => $wasMemCacheHit ? 'HIT MemcCache' : ($wasHit ? 'HIT REDIS' : 'MISS'), | |
| 'X-Cache-Result' => $wasMemCacheHit ? 'HIT Memcached' : ($wasHit ? 'HIT REDIS' : 'MISS'), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| <?php namespace App\Utils\Cache; | ||
|
|
||
| use Illuminate\Support\Facades\Cache; | ||
| use Illuminate\Support\Facades\Log; | ||
|
|
||
| final class MemCache | ||
| { | ||
| private const CM_REGION = 'mem:region:'; | ||
|
|
||
| private static function store(): \Illuminate\Contracts\Cache\Repository | ||
| { | ||
| return Cache::store('memcached'); | ||
| } | ||
|
|
||
| public static function get(string $key) | ||
| { | ||
| try { | ||
| Log::debug("MemCacheService::get", ["key" => $key]); | ||
| return self::store()->get($key, null); | ||
| } catch (\Throwable $e) { | ||
| Log::warning($e); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private static function trackKey(string $regionTag, string $key, int $ttl): void | ||
| { | ||
| try { | ||
| $store = self::store(); | ||
| Log::debug("MemCache::trackKey", ["regionTag" => $regionTag, "key" => $key, "ttl" => $ttl]); | ||
| $setKey = self::CM_REGION.$regionTag; | ||
| $list = $store->get($setKey, []); | ||
| if (!\is_array($list)) $list = []; | ||
| $list[$key] = \time() + $ttl; | ||
| if (\count($list) > 10000) { array_shift($list); } | ||
| $store->put($setKey, $list, $ttl); | ||
| } catch (\Throwable $e) { | ||
| Log::warning($e); | ||
| } | ||
| } | ||
|
|
||
| public static function put(string $key, $value, int $ttl, ?string $regionTag = null): void | ||
| { | ||
| try { | ||
| Log::debug("MemCache::put", ["key" => $key, "value" => $value, "ttl" => $ttl]); | ||
| self::store()->put($key, $value, $ttl); | ||
| if ($regionTag) self::trackKey($regionTag, $key, $ttl); | ||
| } catch (\Throwable $e) { | ||
| Log::warning($e); | ||
| } | ||
| } | ||
|
|
||
| public static function apcClearRegion(string $regionTag): int | ||
| { | ||
| try { | ||
| $store = self::store(); | ||
| $setKey = self::CM_REGION.$regionTag; | ||
| $list = $store->get($setKey, []); | ||
| $n = 0; | ||
| Log::debug("MemCache::apcClearRegion", ["regionTag" => $regionTag, "list" => $list]); | ||
| if (\is_array($list)) { | ||
| foreach (array_keys($list) as $k) { | ||
| if ($store->forget($k)) $n++; | ||
| } | ||
| } | ||
| $store->forget($setKey); | ||
| return $n; | ||
| } catch (\Throwable $e) { | ||
| Log::warning($e); | ||
| return 0; | ||
| } | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -49,12 +49,17 @@ | |||||
|
|
||||||
| 'memcached' => [ | ||||||
| 'driver' => 'memcached', | ||||||
| 'servers' => [ | ||||||
| //'persistent_id' => 'host-cache', | ||||||
| 'sasl' => [null, null], | ||||||
| 'servers' => [ | ||||||
| // UNIX socket (fastest) | ||||||
| [ | ||||||
| 'host' => env('MEMCACHED_HOST', '127.0.0.1'), | ||||||
| 'port' => env('MEMCACHED_PORT', 11211), | ||||||
| 'weight' => 100, | ||||||
| 'host' => env('MEMCACHED_SERVER_HOST', '/var/run/memcached/memcached.sock'), | ||||||
| 'port' => env('MEMCACHED_SERVER_PORT',0), | ||||||
|
||||||
| 'port' => env('MEMCACHED_SERVER_PORT',0), | |
| 'port' => env('MEMCACHED_SERVER_PORT', 0), |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after comma in function call. Should be env('MEMCACHED_SERVER_WEIGHT', 100) for consistency with Laravel coding standards.
| 'weight' => env('MEMCACHED_SERVER_WEIGHT',100) | |
| 'weight' => env('MEMCACHED_SERVER_WEIGHT', 100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment incorrectly refers to 'APC' but the code is using memcached. Should be 'try L1 memcached'.