Detailed reference for the response cache subsystem powering ApiResponseCache.
- 1. Goals and Constraints
- 2. Data Flow
- 3. Dynamic Tagging Model
- 4. Configuration Surface
- 5. Failure Modes and Fallbacks
- 6. Validation Plan
- 7. Implementation Notes
- Serve idempotent requests (GET/HEAD) from cache without mutating payloads.
- Invalidate cached entries deterministically on mutations (POST/PUT/PATCH/DELETE) that target equivalent resource segments.
- Support per-user segmentation for authenticated APIs.
- Avoid cache stampede and stale data by leveraging tag indices rather than brute-force clears.
- Remain driver-agnostic: Redis and Memcached fully supported; file/array driver limited (no tagging).
-
Request qualification
- Skips cache unless
cache.enabledistrue. - Evaluates HTTP method (
cacheabledefaults to GET/HEAD) and content type (cacheable_content_types). - Optionally checks authentication when
cache_authenticatedisfalse.
- Skips cache unless
-
Key derivation
- Base key: SHA-1 hash of method + normalized URI + per-user token (optional) + vary headers.
- Per-request metadata stored alongside payload: status code, headers, ETag, compression flag.
-
Tag assignment
- Tags derived from path segments (see Dynamic Tagging Model).
- Custom tags can be appended via request attribute
page_speed.cache.tags.
-
Response storage
- Cache entry stores serialized body + metadata + tag list.
- TTL defaults to
cache.ttlseconds.
-
Invalidation
- Mutation verbs gather matching tags and flush them from the tag index.
- Tag index resides in cache as
pagespeed:tag:{tag}containing key IDs.
-
Segment parsing
- Routes are split on
/, filtered bydynamic_tagging.ignore_segments(default["api"]). - Numeric segments optionally normalized to a placeholder when
normalize_ids=true.
- Routes are split on
-
Depth control
- Only the first
max_depthsegments are considered (default5). - Example:
/api/v1/customers/42/invoices/3yields tagsapi:v1api:v1:customersapi:v1:customers:{id}api:v1:customers:{id}:invoices
- Only the first
-
Custom augmentation
- Controllers may push tags via
app('pagespeed.cache')->tag('tenant:'.$tenantId);
- Controllers may push tags via
-
Mutation invalidation
- A
POST /api/v1/customers/42/invoicesclears all tags prefixed with the normalized path, ensuring collection endpoints (e.g.,/api/v1/customers/42/invoices?page=2) are refreshed.
- A
Key options from config/laravel-page-speed.php (see docs/CONFIGURATION.md for full context):
'api' => [
'cache' => [
'enabled' => env('API_CACHE_ENABLED', false),
'driver' => env('API_CACHE_DRIVER', 'redis'),
'ttl' => env('API_CACHE_TTL', 300),
'per_user' => env('API_CACHE_PER_USER', false),
'cache_authenticated' => env('API_CACHE_AUTHENTICATED', false),
'track_metrics' => env('API_CACHE_TRACK_METRICS', true),
'vary_headers' => [],
'cacheable_content_types' => ['application/json', 'application/xml', 'application/vnd.api+json'],
'purge_methods' => ['POST', 'PUT', 'PATCH', 'DELETE'],
'dynamic_tagging' => [
'enabled' => env('API_CACHE_DYNAMIC_TAGS', true),
'ignore_segments' => ['api'],
'normalize_ids' => true,
'max_depth' => 5,
],
],
];| Scenario | Behaviour | Mitigation |
|---|---|---|
| Cache backend unavailable | Middleware bypasses cache and continues request lifecycle | Use monitoring on cache pool; configure retry/backoff in driver. |
| Tag index rebuild required | Mutation clears only known keys; orphaned keys may remain temporarily | Schedule periodic background sweep using Laravel command pagespeed:cache:prune. |
| Large payload serialization overhead | Response stored as UTF-8 string (binary-safe). Compression still applied downstream | Increase TTL to reduce churn; consider upstream pagination. |
| Per-user cache explosion | per_user=true multiplies key cardinality |
Combine with lower TTL and targeted tagging to control footprint. |
- Unit tests –
tests/Middleware/ApiResponseCacheTest.phpcovers hits, misses, purge verbs, nested paths, and per-user tagging. - Integration tests – create end-to-end scenarios using Laravel HTTP tests with
Cache::store('redis')configured. - Load test – measure miss vs hit latency using k6: ensure p95 latency stays <30 ms on hits.
- Chaos drill – disable Redis for a short window; verify app serves uncached responses without fatal errors.
- The cache wrapper uses Laravel's Cache tagging API; drivers without tagging automatically degrade to key-based storage but lose purge precision.
X-Cache-Statusheader advertisesMISS,HIT, orBYPASSfor observability. Pair withX-Cache-Storeto confirm driver.- Metrics (if enabled) track hit/miss counts in memory; integrate with Prometheus via custom collectors if needed.
- TTL should align with business SLAs—set shorter durations for mutable data, longer for catalogs/reference lists.
Use this document alongside docs/API-OPTIMIZATION.md to design cache rollouts that meet correctness and performance targets.