Cache invalidation
Purge (exact URL)
# CLI
bouine purge https://example.com/products/123 --token <token>
# API
curl -X POST http://127.0.0.1:9000/v1/purge \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/products/123"}'In a cluster, the purge is forwarded to all live peers via HTTP fan-out (in strong mode) or gossiped via the memberlist broadcast queue (in eventual mode). Since v0.5.14, fan-out is batched: purge and refresh events coalesce into batch frames flushed on 256 events, a 10 ms interval, or shutdown — a 1000-key purge burst in a 3-peer cluster produces a handful of batched POSTs instead of 3000 — and receivers deduplicate by per-issuer sequence number. Events arriving on an idle queue still flush synchronously, preserving the purge API’s fan-out-before-return guarantee. Ban events stay unbatched (rare; immediacy dominates).
Cluster propagation
The delivery mechanism depends on cluster.mode:
| Mode | Purge delivery | Ban delivery | Refresh delivery |
|---|---|---|---|
strong | HTTP fan-out + gossip dual path | HTTP fan-out + gossip dual path | HTTP POST to owner node |
eventual | Gossip only (1–5 s convergence) | Gossip only (1–5 s convergence) | Gossip only |
See Clustering for details on choosing a mode.
Ban (predicate-based)
# CLI
bouine ban host_regex=example.com path_regex=^/api/ --token <token>
# API
curl -X POST http://127.0.0.1:9000/v1/ban \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"host_regex":"example.com","path_regex":"^/api/"}'Bans use a two-pronged invalidation strategy:
- Eager eviction — all entries currently in the hot store that match the predicate are deleted immediately. Since v0.5.16, surrogate-only bans skip the eager scan entirely: the O(1) lazy check below enforces them identically (a banned entry is never served), and memory reclaim moves to the TTL reaper — a ~1800× faster registration path for the dominant production invalidation workload.
POST /v1/bantherefore reportscount: 0for surrogate-only bans. Host/path and multi-condition bans keep the coalesced scan (which also deduplicates identical bans registered concurrently, v0.5.14). - Lazy check — newly-stored objects are checked against the active ban list on every lookup. Since v0.5.15 the ban list is compiled into an immutable snapshot (literal hosts, paths, and surrogate keys become set lookups; anchored prefixes become
HasPrefixchecks), so a full 1024-ban list costs ~22 ns per hit instead of ~10 µs.
Active bans are retained for 24 hours by default and then pruned automatically by the reaper. Since v0.5.20 the retention window is configurable via cluster.ban_ttl (must be ≥ 1s when set): RFC 9111 §4.4 exempts objects stored after the ban from matching, so cache-lifecycle surrogate invalidations are safe at minutes scale — lower it to bound the hit-ratio damage of an over-broad ban (a typo’d ban previously poisoned the hit ratio for the full 24 h).
Surrogate key ban
curl -X POST http://127.0.0.1:9000/v1/ban \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"surrogate_key":"product-456"}'Invalidates all objects tagged with the given surrogate key. Origins emit surrogate keys via response headers:
| Header | Used by |
|---|---|
Surrogate-Key: <tag> <tag> | Fastly, RFC 8607 draft |
Cache-Tag: <tag>, <tag> | Cloudflare |
X-Cache-Tags: <tag> <tag> | Varnish / Drupal |
bouine reads whichever header is present (first non-empty header wins) and stores the tags on the cached object.
Refresh (soft-purge)
# CLI
bouine refresh https://example.com/products/123 --token <token>
# API
curl -X POST http://127.0.0.1:9000/v1/refresh \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/products/123"}'Marks the entry stale — the next request triggers revalidation. If the origin returns 304, the cached body is reused; the TTL is refreshed from the updated headers.
| Scenario | Use |
|---|---|
| Content is wrong / security issue | Purge |
| Content updated, old is OK temporarily | Refresh |
| Bulk invalidation by pattern | Ban |
| Invalidate all pages for a product | Ban (surrogate key) |
Dashboard invalidation
The Invalidation view in the operator dashboard provides the same four operations through a browser UI — no curl required. The forms validate inputs before submitting:
- URLs must begin with
http://orhttps://and include a host - Regex fields must be valid RE2 expressions
- At least one ban field (host, path, or surrogate key) must be non-empty
The Recent invalidations list updates immediately after each successful operation, showing the operation type, argument, and relative timestamp.
Cloudflare CDN propagation
When bouine sits behind Cloudflare, invalidation operations can be forwarded to the Cloudflare Cache API so both caches are cleared together.
See Cloudflare CDN propagation for full setup instructions, mapping strategy (URL→PurgeSingleFile, surrogate-key→PurgeByTags, literal regex→PurgeByPrefixes/Hostnames), async mode, Kubernetes secret wiring, and monitoring metrics.