Invalidate CDN cache on plain document edits - #439
Open
eyeseast wants to merge 5 commits into
Open
Conversation
Extends the existing (redaction-only) cache invalidation to plain edits,
deletion, and page modifications, and switches the API purge to a
`doc-{id}` Cache-Tag so one purge clears the bare URL and every
?expand=/Origin variant at once (available on the Business plan).
- DocumentViewSet.retrieve emits `Cache-Tag: doc-{id}` on 200 and 304
- perform_update batches a purge for every plainly-edited (non-processing)
instance, covering title/metadata edits and public<->private flips
- Document.destroy() and page modifications now trigger invalidation
- invalidate_cache task is variadic + batched, and clears cache_dirty via
a queryset update so it no longer bumps updated_at on every purge
- fix invalidate_cache always purging the wrong URL set on access flips
- move the purge helpers into documentcloud/documents/cache.py
- CLOUDFLARE_PURGE_LIMIT is now a setting (default 100)
Covered by test_cache.py, test_tasks.py, and new DocumentViewSet tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-implementation cleanup pass (reuse/simplification/efficiency/altitude): - load only pk/slug in the invalidate_cache task; invalidate_cache_batch never touches the heavy columns - bind pk eagerly in the _update_cache on_commit closure so it doesn't pin the whole Document until commit (matches _invalidate_edited_cache) - collapse the repeated requests.post patch in test_cache.py into a fixture Also records two deferred follow-ups in the plan: unifying the two staleness conventions on cache_dirty, and a CacheTaggedMixin so NoteViewSet can adopt Cache-Tag + purge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
eyeseast
requested review from
duckduckgrayduck and
mitchelljkotler
and removed request for
mitchelljkotler
July 30, 2026 23:54
Use @pytest.mark.usefixtures for the mock_post side-effect fixture in the CloudFront test, which asserts on boto3 rather than the requests mock. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Implements part 5 of the caching refactor (
plans/cache-refactor.md): extend CDN cache invalidation, which today only fires from redactions, to plain edits, access flips, deletion, and page modifications — and switch the API purge to adoc-{id}Cache-Tag so one purge clears the bare URL and every?expand=/Originvariant at once (purge-by-tag is available on the Business plan as of 2025-04-01). This is the prerequisite for the age-based TTL tiers in part 4.What changed
DocumentViewSet.retrieveemitsCache-Tag: doc-{id}on both the200and304branches, so cached API responses become purgeable by tag.perform_updatebatches a purge for every plainly-edited (non-processing) document — covers title/description/metadata/dataedits and, critically, public→private access flips (the privacy case). The processing-completion path keeps using the existingcache_dirtymechanism, so there's no double purge.Document.destroy()and page modifications now trigger invalidation (page mods ride the existingcache_dirtyprocessing-completion purge, the same way redaction does).invalidate_cachetask is now variadic (*document_pks) and batched: one task per request instead of one per document. It clearscache_dirtyvia a queryset.update()so a purge no longer bumpsupdated_at(which would reset theLast-Modifiedfreshness signal from part 3 / demote the doc to the shortest TTL tier in part 4).invalidate_cachepurging the wrong URL set on access flips (it branched on the already-flippedaccess, leaving the public copy cached).documentcloud/documents/cache.py;CLOUDFLARE_PURGE_LIMITis now a setting (default 100).filesandtagsare sent as separate Cloudflare requests (the zone purge API is aoneOf), each chunked to the per-request cap.Tests
New
test_cache.pyandtest_tasks.py, plusDocumentViewSettests for theCache-Tagheader (200 + 304), edit/access/bulk/destroy purge enqueue, and the page-modification flag. Full documents suite passes (442); pylint 10/10.Notes
plans/cache-refactor.mdis included and records the design, decisions, and two deferred follow-ups (unify the two staleness conventions oncache_dirty; aCacheTaggedMixinsoNoteViewSetcan adopt tagging + purge).🤖 Generated with Claude Code