Skip to content

fix(aso-overlay): add Surrogate-Key header for Fastly targeted purge (SITES-48140)#2826

Merged
miakobchuk merged 1 commit into
mainfrom
feat/sites-48140-aso-overlay-surrogate-key
Jul 15, 2026
Merged

fix(aso-overlay): add Surrogate-Key header for Fastly targeted purge (SITES-48140)#2826
miakobchuk merged 1 commit into
mainfrom
feat/sites-48140-aso-overlay-surrogate-key

Conversation

@abhishekgarg18

Copy link
Copy Markdown
Member

Summary

Adds a Surrogate-Key: aso-overlay-<service> header to the 200 and 304 responses of GET /config/:service/redirects.txt, so that Mystique can targeted-purge Fastly's edge cache for a specific tenant's overlay via the existing POST /service/{sid}/purge/{key} endpoint after a successful S3 write.

Pairs with:

  • experience-platform/mystique#3381 - Mystique's fastly_purge.py calls the key-purge endpoint
  • experience-platform/mystique-deploy#568 - wires FASTLY_ASO_PURGE_TOKEN env var

Why this is needed

Alina live-verified mystique#3381 against prod (DlxppS2VoAizEqJ9bPasq6, dev tenant cm-p117653-e365853) and found the purge is a silent no-op as currently wired: 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. Because our 200 responses had no Surrogate-Key header, the purge invalidated a key nothing was tagged with -> HTTP 200 + zero effect (x-cache=HIT age incrementing across polls, never resetting to 0).

Two fix options were considered:

  1. Surrogate keys (this PR) - origin tags responses with a key, Mystique purges that key. No VCL change needed, works with the existing purge endpoint.
  2. URL purge + VCL exemption - would require adding a FASTLYPURGE method exemption in 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).

  • Namespaced with aso-overlay- prefix so future overlays under other routes cannot collide with this key space
  • Service-scoped only (no tier segment): Fastly VCL strips the public /config/<tier>/... prefix before proxying to origin, so per-service uniqueness is sufficient
  • Emitted on both 200 and 304 responses so any 304 stragglers still cached at that status get purged in the same call (RFC 7232 requires we carry cache-control + etag on 304; Surrogate-Key is an operationally-linked companion)

Coordination

Once this PR lands:

  • Maksym updates mystique#3381's fastly_purge.py endpoint from POST /service/{sid}/purge{url_path} to POST /service/{sid}/purge/aso-overlay-<tenant_id>
  • Drop _URL_TIER_BY_BUCKET_SEGMENT from handler.py since the key is now tier-agnostic (also cleans up the stage-returns-None edge case)

Test plan

  • test/controllers/redirects.test.js: added surrogate-key header assertions on both the 200 happy-path test and the 304 conditional-GET test (38 passing)
  • ESLint + type-check clean
  • Post-deploy on dev: hit the endpoint via curl, verify response includes Surrogate-Key: aso-overlay-<service>
  • After mystique#3381 lands + deploys, trigger a Deploy in ESS UI, verify x-cache flips from HIT to MISS and age resets to 0
  • Once verified end-to-end, spacecat-infrastructure Fastly VCL beresp.ttl can bump from 10s -> 24h for the cost win

Separately: 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

…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>
@github-actions

Copy link
Copy Markdown

This PR will trigger a patch release when merged.

@miakobchuk miakobchuk enabled auto-merge (squash) July 15, 2026 16:40
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@miakobchuk miakobchuk merged commit 9c1fa79 into main Jul 15, 2026
20 checks passed
@miakobchuk miakobchuk deleted the feat/sites-48140-aso-overlay-surrogate-key branch July 15, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants