Skip to content

Remove Django server-side response cache#605

Closed
skaphan wants to merge 5 commits intoartoonie:mainfrom
skaphan:remove-django-cache
Closed

Remove Django server-side response cache#605
skaphan wants to merge 5 commits intoartoonie:mainfrom
skaphan:remove-django-cache

Conversation

@skaphan
Copy link

@skaphan skaphan commented Mar 5, 2026

Why remove Django's server-side response cache

The Django file-based cache middleware (UpdateCacheMiddleware / FetchFromCacheMiddleware) was the original mechanism for avoiding redundant graph computation on repeated page loads. It served that purpose, but it is now redundant and counterproductive given the current architecture:

1. Cloudflare already handles edge caching

Dynamic pages are cached at Cloudflare's CDN, which handles the high-traffic case (millions of requests during bursts). The ConditionalGetMixin returns 304 when content has not changed, so even cache misses at Cloudflare skip expensive graph computation. The Django middleware is a second cache layer that does not add meaningful value.

2. The middleware requires workarounds that add complexity

  • vary_on_headers('increment') -- decorates several views with a Vary header for a fake HTTP header that no client ever sends. This is a no-op (every request has the same empty value, so it never affects caching) and can be removed as dead code.
  • cache.clear() in cloudflare.py and Movie.save() -- every CDN purge and movie save also had to clear the local Django cache. These become dead code.
  • DISABLE_CACHE environment variable -- needed for development/testing to work around the cache. Goes away entirely.

3. It causes confusing behavior during development and testing

Stale cached responses make it hard to tell whether code changes are taking effect. We hit this directly while testing the friendly-embed-404 change -- the cache was serving old responses and masking the actual view behavior.

4. What we keep

  • Cloudflare CDN purging (unchanged)
  • ConditionalGetMixin for 304 responses (unchanged, with linter fixes in Fix linter issues in ConditionalGetMixin #604)
  • LocMemCache for upload rate limiting (the only actual use of django.core.cache in views.py)

Summary of changes

  • Remove UpdateCacheMiddleware and FetchFromCacheMiddleware from MIDDLEWARE
  • Replace file-based CACHES with LocMemCache (still needed for rate limiting)
  • Remove vary_on_headers('increment') from Visualize and VisualizeBallotpedia (was a no-op)
  • Remove cache.clear() from cloudflare.py and Movie.save()
  • Remove test_cache_speed and test_cache_works (tested the removed middleware behavior)
  • Remove unused imports (cache, patch, get_data_for_view)

Net effect: 5 files changed, ~120 lines removed, no new code.

Test plan

  • Existing tests pass (cache tests removed since they tested removed behavior)
  • Rate limiting still works via LocMemCache
  • Cloudflare CDN purging unaffected

skaphan and others added 5 commits February 24, 2026 13:29
Add updated_at field to JsonConfig model, use ConditionalGetMixin for
all visualization views, and short-circuit 304 responses in
VisualizeEmbedded before expensive computation.
Add proper cache control for embedded visualizations
- Add pylint disable for too-few-public-methods (it is a mixin)
- Add docstring to get() method
- Rename last_modified/if_modified_since to camelCase

Co-Authored-By: Claude Opus 4.6
Full HTTP response caching is handled by Cloudflare at the edge.
The Django file-based cache middleware was redundant and added
complexity (vary_on_headers hack, cache.clear() on every save).

- Remove UpdateCacheMiddleware and FetchFromCacheMiddleware
- Replace file-based CACHES with LocMemCache (still needed for rate limiting)
- Remove vary_on_headers('increment') from Visualize and VisualizeBallotpedia
- Remove cache.clear() from cloudflare.py and Movie.save()
- Remove test_cache_speed and test_cache_works (tested removed behavior)

Co-Authored-By: Claude Opus 4.6
@artoonie
Copy link
Owner

artoonie commented Mar 5, 2026

It seems there's some tension between supporting RCV123, which has frequent updates, and supporting real elections, which are usually static. The increase in PATCH() calls (and subsequent cache clearing) have already doubled RCVis' server costs, so that caching is important.

To break this down, take a look at RCVis server response times:
image

At just 750 requests per minute, response times starts to go up.

Cloudflare isn't a catch-all here: we need to populate the Cloudflare cache for each Point of Presence (each "exit node"). Given the global interest in US elections, that often means populating 100+ PoPs on election night with dozens of elections. Our local cache is protection against that

You mentioned "even cache misses at Cloudflare skip expensive graph computation" -- I'm not sure I see how without this. If neither the browser nor cloudflare has the page in cache, how are we skipping the expensive graph computation?

@skaphan
Copy link
Author

skaphan commented Mar 6, 2026

Superseded by #608 which fixes the server-side cache instead of removing it.

@skaphan skaphan closed this Mar 6, 2026
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