Repository navigation
Conversation
When Meilisearch fails, a full-text name search silently falls back to the SQL name filter, which ignores effect texts. That partial response was then cached by browsers for an hour (max-age=3600). - Log Meilisearch search/count failures at warning level. - Mark degraded responses on the request and override Cache-Control with no-store in a kernel.response subscriber. - Expose the backend that answered via X-Search-Backend (meilisearch|sql). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Why
GET /api/cards?name=aérolithe&locale=frnormally searches card names and effect texts through Meilisearch and returns 22 cards. On 2026-10-06, while Meilisearch was being re-indexed, a user got only the 4 cards whose name contains « aérolithe », and kept getting them for a full hour:SearchAwareCollectionProvider::fetchIds()/fetchTotal()caught every\Throwableand returnednullwithout logging, so the provider silently fell back to the Doctrine provider, whereCardNameFilteronly does aLIKEon card names.timeout: 2.0, max_duration: 5.0) during the re-index.Cache-Control: public, max-age=3600, s-maxage=3600, so the browser reused it for an hour.How
SearchAwareCollectionProviderlogs Meilisearch failures (search and total count) at warning level with the query, the filter, the exception message and the exception._search_backend:meilisearch|sql) and flags the response as degraded (_search_degraded) when a name search fell back to SQL because Meilisearch failed. A filter-only search that falls back to SQL is not flagged: SQL applies the same filters, so the result is complete.SearchBackendResponseSubscriber(kernel.response, priority -10, after API Platform has set the operation's cache headers):X-Search-Backend: meilisearch|sql(also added to CORSexpose_headers);Cache-Controlwithno-store, privateon degraded responses only.ProviderInterface(still autowired toCardCollectionProvider) so it can be stubbed in unit tests.Outcomes
public, max-age=3600, s-maxage=3600.X-Search-Backend.Note: in prod, Monolog's
mainhandler isfingers_crossedwithaction_level: error, so these warnings are only written if an error occurs in the same request. Raising them toerror, or adding a handler for this channel, would make them always visible.Tests
tests/Unit/State/SearchAwareCollectionProviderTest.php: Meilisearch success (IDs used, no warning), search failure (warning logged with the query, request flagged degraded), count failure (warning, IDs kept), filter-only failure (not degraded), plain listing.tests/Unit/EventSubscriber/SearchBackendResponseSubscriberTest.php: cache headers unchanged for Meilisearch / non-degraded SQL responses,no-storefor degraded ones, sub-requests ignored.tests/Integration/Api/CardApiTest.php:/api/cardsstays cacheable;/api/cards?name[fr]=aérolithewith Meilisearch unavailable returnsno-storeandX-Search-Backend: sql.Ran locally on PHP 8.4 + PostgreSQL 16 + Redis: Unit 37/37, Integration 26/26.
🤖 Generated with Claude Code