Skip to content
Sponsor

Invalidating data

Mark dependent values stale, make them unreadable, or invalidate one contextless public key.

In Astilba Cache, tags describe what a value depends on. Invalidation advances tag watermarks, so readers can reject matching entries without scanning or deleting every stored value.

The application changes its source of truth; Cache only changes whether stored representations may be served. See Core concepts for keys, tags, Registry, and Bus.

Operation Effect
expire() — soft Values born before the new soft watermark become stale. An eventual read may return the old value after attempting a refresh for a later read.
delete() — hard Values born before the new hard watermark become unreadable wherever that invalidation is visible. Grace cannot resurrect them.
invalidation.ts
const productTag = compound("product", productId)
// Mark every entry carrying the tag stale.
await cache.expire({ tag: productTag })
// Make every entry carrying the tag unreadable.
await cache.delete({ tag: productTag })

compound() escapes percent signs and delimiters, then prefixes the vector’s arity. Delimiter-like values, empty strings, and vectors of different lengths therefore remain distinct.

const productTag = compound("product", productId)
const categoryListingTag = compound("category", categoryId, "listing")

Caller-supplied tags on getOrSet() and getOrSetEntry() are rejected when they begin with __. The kernel reserves that prefix for its per-key and per-namespace tags. The Tag brand prevents ordinary raw-string selectors; do not bypass it with a type assertion.

The key selector maps a user key to Cache’s reserved per-key tag:

await cache.expire({ key: `product:${productId}` })
await cache.delete({ key: `product:${productId}` })

In the current kernel, that selector resolves the contextless public canonical key: it has no request or scope input. It does not target principal-derived or tenant-scoped variants of the same user key. Use a dependency tag when data can exist in more than one scope.

The selector types also expose scope on tag invalidation, but the current implementation does not apply it when resolving the Registry tag. Treat a tag purge as affecting every cached entry carrying that tag; do not rely on scope-qualified tag invalidation yet.

clear() bumps the calling instance’s namespace version and issues a hard invalidation for the reserved namespace tag. The version makes old keys unreachable on that instance; the hard watermark makes other readers reject pre-clear entries as the invalidation reaches them.

expire(), delete(), and clear() return a PurgeResult with an epoch, matchedHint, flushed(), and edgePurged().

In the current preview:

  • matchedHint is always “unknown”;
  • flushed() resolves immediately without measuring mirror acceptance;
  • edgePurged() resolves immediately without invoking a CDN queue;
  • the cdn option on delete() is not wired.

Do not use those completion fields as rollout or takedown guarantees yet.

The purge verbs require a Registry. For reads to observe coordinated invalidation, configure Registry and Bus together. L2 remains required for fills and lets suspect readers replay durable delta batches. The production Bus, CDN path, and supported adapter exports are not implemented.