fix(aso-overlay): add Surrogate-Key header for Fastly targeted purge (SITES-48140)#2826
Merged
Merged
Conversation
…nvalidates (SITES-48140)
Alina's live-repro on mystique#3381 caught that our Fastly purge is currently
a silent no-op:
- Fastly's `POST /service/{sid}/purge/<path>` endpoint is a SURROGATE-KEY
purge (the path after `/purge/` is the key name), not a URL purge.
- Our 200 response emitted no Surrogate-Key header.
- So mystique was purging a key nothing was tagged with -> HTTP 200 + zero
effect (verified by `x-cache=HIT` age incrementing instead of resetting).
The URL-purge alternative (`POST /purge/{host}{path}`) is blocked by the VCL
`recv 40` aso-overlay-route auth gate (purge requests carry no X-ASO-API-Key).
Fix: emit `Surrogate-Key: aso-overlay-<service>` on both 200 and 304
responses. Mystique#3381 then purges that exact key via the same endpoint
it's already calling, which now does something real.
Key format:
- `aso-overlay-<service>` where <service> is the CM identifier (cm-p<N>-e<N>)
- Origin route is `/config/:service/redirects.txt` (Fastly strips tier
prefix), so per-service uniqueness is sufficient — no tier segment needed.
- Namespace prefix `aso-overlay-` guards against key collisions with future
overlays under other routes.
Coordination with mystique#3381: Maksym updates `fastly_purge.py` to call
`POST /service/{sid}/purge/aso-overlay-<service>` (drop the full URL path;
use the same key format). Unit tests there still pass because they mock the
HTTP call — end-to-end verification requires a live Fastly service.
Also included on the 304 response so any stragglers still cached at that
status get purged in the same call (RFC 7232 requires cache-control + etag
on 304; Surrogate-Key is an operationally-linked companion).
Tests: 54 passing (added Surrogate-Key assertions to the 200 and 304 tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This PR will trigger a patch release when merged. |
miakobchuk
approved these changes
Jul 15, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Summary
Adds a
Surrogate-Key: aso-overlay-<service>header to the 200 and 304 responses ofGET /config/:service/redirects.txt, so that Mystique can targeted-purge Fastly's edge cache for a specific tenant's overlay via the existingPOST /service/{sid}/purge/{key}endpoint after a successful S3 write.Pairs with:
fastly_purge.pycalls the key-purge endpointWhy this is needed
Alina live-verified mystique#3381 against prod (
DlxppS2VoAizEqJ9bPasq6, dev tenantcm-p117653-e365853) and found the purge is a silent no-op as currently wired: Fastly'sPOST /service/{sid}/purge/<path>endpoint is a surrogate-key purge (the path after/purge/is the key name), not a URL purge. Because our 200 responses had noSurrogate-Keyheader, the purge invalidated a key nothing was tagged with -> HTTP 200 + zero effect (x-cache=HITage incrementing across polls, never resetting to 0).Two fix options were considered:
fastly/vcl/aso-prod/recv/40-aso-overlay-route.vcl(that VCL auth-gates all requests, including purge). Touches prod VCL.Alina recommended option 1; taking that path.
Key format
aso-overlay-<service>where<service>is the CM identifier (e.g.,aso-overlay-cm-p117653-e365853).aso-overlay-prefix so future overlays under other routes cannot collide with this key space/config/<tier>/...prefix before proxying to origin, so per-service uniqueness is sufficientCoordination
Once this PR lands:
fastly_purge.pyendpoint fromPOST /service/{sid}/purge{url_path}toPOST /service/{sid}/purge/aso-overlay-<tenant_id>_URL_TIER_BY_BUCKET_SEGMENTfromhandler.pysince the key is now tier-agnostic (also cleans up the stage-returns-None edge case)Test plan
test/controllers/redirects.test.js: addedsurrogate-keyheader assertions on both the 200 happy-path test and the 304 conditional-GET test (38 passing)Surrogate-Key: aso-overlay-<service>x-cacheflips from HIT to MISS andageresets to 0beresp.ttlcan bump from 10s -> 24h for the cost winSeparately: this change is deliberately scoped narrow (17 lines) and split out from the paired observability PR (#2822) so it can be reviewed and shipped on its own timeline.
🤖 Generated with Claude Code