Repository navigation
fix(api-cache): list cache keys cover the request path and session scope - #300
Merged
Merged
Conversation
List cache keys were built from the table, the session company, the query string and has_callback only. Query callbacks that filter on session values (storefront_store, storefront_network) or on route parameters, and internal and public endpoints that paginate and eager load differently, therefore shared one cache entry per company and query: one store's products, catalogs, reviews or categories were served to another store or network of the same company, and an internal console paginator could be served to the public API (or the reverse). The params hash now also covers the request path and the values of the session keys listed in api.cache.scope.session (default: api_credential, storefront_store, storefront_network). Both are hashed, so credentials do not appear in the key or the X-Cache-Key debug header.
This was referenced Oct 12, 2026
Merged
roncodes
added a commit
that referenced
this pull request
Oct 13, 2026
getSessionScope returns no scope when the request has no session. Every test request had one, so the release branch fell one statement short of the coverage floor after #300.
This was referenced Oct 13, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The list (query) cache key was built from the table, the session company, the query string and
has_callback. Two groups of requests that return different data produced the same key:SetStorefrontSessionresolves the store or network key intosession('storefront_store')/session('storefront_network'). Storefront's public list endpoints filter on those values inside the cached callback, for exampleProduct::queryWithRequestCached(... if (session('storefront_store')) $query->where('store_uuid', ...)). The value is not in the query string, so store A's products, catalogs, food trucks, reviews or categories were served to store B, or to a network of the same company, when both sent the same query.storefront/int/v1/catalogs(callbackonQueryRecordwith the editor eager loads; internal requests return afastPaginatepaginator) andstorefront/v1/catalogs(store filter; returns a collection) both sendhas_callback=true. The same company plus the same query gave the same key, so the console's paginator, which holds every catalog in the company, could be served to the public API, or the public collection to the console. The same applies to any core-api resource whose internal and public lists both pass a callback, or both pass none.Change
ApiModelCache::generateQueryCacheKey()now hashes three things together instead of the params alone:$request->path(). This separates internal from public endpoints, one endpoint from another, and endpoints whose callbacks read route parameters (/v1/customers/{id}/places).api.cache.scope.sessionconfig. The default isapi_credential,storefront_storeandstorefront_network. Missing values are left out, so console requests are unaffected. Extensions add any session key that a query callback reads but that does not appear in the query string.The key's visible shape is unchanged:
{api_query}:{table}:company_{uuid}:v{n}:{hash}. This builds on #299: the version segment and invalidation are untouched. Path and scope are inside the hash, so API and Storefront keys never appear in the key or in theX-Cache-Keydebug header.When this deploys, every list key changes once (one miss per key), and the old entries expire after their 5-minute TTL.
Tests
tests/Unit/Support/ApiModelCacheTest.phpadds four tests. All four failed against the previous code:store a product.Validation
vendor/bin/pest tests/Unit/Support/ApiModelCacheTest.php tests/Unit/Traits/HasApiModelBehaviorTest.php: 36 passed (297 assertions). Before the fix: 4 failed.php -d memory_limit=512M vendor/bin/pest(full suite): 2063 passed, 0 failed (12766 assertions). The deprecations come fromcknow/laravel-moneyunder PHP 8.4.php-cs-fixer fix -v --dry-run: 0 of 837 files to fix.Docs / API impact
fleetbase/postmanupdate is needed.fleetbase/fleetbase.ioupdate is needed. The docs and specs don't mention the API cache (API_CACHE_*,X-Cache-Keyor cache keys). The newapi.cache.scope.sessionoption is a config array with no env variable, and it is documented inconfig/api.php.Related: fleetbase/storefront, a separate PR for
NetworkController::addStores, found in the same audit.