Skip to content

feat: added purger_http module - #39

Merged
vincent-gao merged 6 commits into
mainfrom
feature/purger_http
Sep 16, 2026
Merged

vincent-gao merged 6 commits into
mainfrom
feature/purger_http

Conversation

@nicksantamaria

@nicksantamaria nicksantamaria commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

…io modules. (#40)

* feat: added purger_http module and retire the section.io related module and settings.

* Fix CloudFront cache tag invalidation tokens

Prefix bare tag invalidations with tag: so the invalidation API treats them as CloudFront cache tags. Support comma, pipe and tab separators, preserve existing markers and deduplicate expressions.

* Empty the purge queue when switching to the Marina CloudFront purger

Items queued for the Section.io purger (URL and wildcard invalidations)
can never be handled by the tag-only Marina purger. Purge marks them
NOT_SUPPORTED and returns them to the queue, so they recirculate on every
run and, because the queue is ordered by creation time, always occupy the
first slot of every claimed batch. purge_tokens_tokens() only builds token
replacements when the batch offered to a purger starts at index 0, so one
stale item is enough to send every real invalidation with the literal
[invalidations:separated_comma] token as its request body.

bay_platform_dependencies_update_10007() empties the queue once so
environments migrated from Section.io start clean.

* Hash cache tags for CloudFront and fix the tags header format

CloudFront's tag invalidation indexes the x-amz-meta-cache-tag response
header as a comma-separated list, rejects tags containing spaces, keeps at
most 50 tags per object and caps the header at 1,783 characters. The
header this module emitted was space-separated and unbounded, so a
"#tag" invalidation could never match anything.

The header now filters tags against the purge_queuer_coretags blacklist,
orders entity tags first, hashes each tag with xxHash3 truncated to six
hex characters, de-duplicates and caps the list at 50, and joins it with
commas. The invalidation side uses the same hash service and prefixes
each value with "tag:", which the cache invalidation API rewrites to
"#<hash>". Both ends therefore always agree.

The token replacement is also rebuilt unconditionally in
hook_tokens_alter(): purge_tokens_tokens() checks
$data['invalidations'][0] and silently produces nothing when the batch
offered by PurgersService does not start at index 0, which happens
whenever an unsupported item sits ahead in the queue.

The hash, filter and prioritizer are services that sites can override.
Adapted from the cloudfront_purger_tags submodule of
drupal/cloudfront_purger (GPL-2.0-or-later).

* Patch Purge invalidation token replacement

Apply the patch for Drupal issue #3484260 to prevent token replacement from being skipped when invalidation sets lack index 0.
@vincent-gao
vincent-gao marked this pull request as ready for review September 16, 2026 00:55
vincent-gao
vincent-gao previously approved these changes Sep 16, 2026
public function filter(array $tags): array {
// cspell:ignore-word blacklist -- Purge's config key.
$blocklist = $this->configFactory
->get('purge_queuer_coretags.settings')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using this yet? Any examples of entities where updating would not need an invalidation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @nicksantamaria, Thanks for the review.
It is not about skipping invalidations. the purge_queuer_coretags blacklist already stops those tags being queued, so they're never invalidated regardless. The filter is only about the response header: it keeps x-amz-meta-cache-tag aligned with what can actually be invalidated, so blacklisted tags don't consume slots against CloudFront's 50-tags-per-object limit (a Drupal page easily emits 30+ tags). It's a service so a site can override it, and it's a no-op when the blacklist is empty. Happy to drop it if you'd rather keep the module lean - it's an optimisation, not required for correctness.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK thanks for the clarification - so this is glue code to ensure the response header only includes cache tag types that would trigger invalidations.

I think this is a good thing to implement - off the top of my head media and paragraph entities should not create invalidations so excluding them would be a great optimisation.

Comment on lines +20 to +22
* - `tag:<x>` becomes `#<x>` (a CloudFront cache tag)
* - starts with `/`, `#`, or contains `:`: passes through unchanged
* - anything else gets a leading `/` (a CloudFront path)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalidation API actually handles these transforms automatically - just need the raw tags (although it will need the hashed version).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @nicksantamaria ,
Good to know that's the intent, but I don't think the currently deployed API does it yet. In publisher.ts the VTL only produces a #-prefixed cache tag from the tag: branch; anything else with no /, # or : gets a leading /, so a bare hash like b46795 normalises to /b46795, and a raw node:203 stays node:203. The tag:<hash> form is the one branch that yields #<hash>, and it's what actually purged end to end on PR-675 (verified: edit → age reset → new title for anonymous users)

If you're planning to have the API normalise raw tags itself, happy to drop the prefix once that's in. Which side should own the # conversion?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey if you look at the VTL script here, it converts tag: to # https://github.com/dpc-sdp/marina/blob/main/src/infra/constructs/cache-invalidation/publisher.ts#L149

I've also tested it just now and can confirm its working as intended. Ditch the transforms here :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @nicksantamaria ,
the change has been applied, the transform is removed, it now sends the bare hashed tags!

The cache invalidation API's VTL normalises the tag itself, so the module
no longer prefixes invalidations with `tag:`. Each tag invalidation is sent
as the bare xxHash3 hash — the same value emitted in the x-amz-meta-cache-tag
response header — and the API converts it to a CloudFront cache tag.
@vincent-gao
vincent-gao merged commit b314f98 into main Sep 16, 2026
2 checks passed
@vincent-gao
vincent-gao deleted the feature/purger_http branch September 16, 2026 05:05
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.

3 participants