Skip to content
Sponsor

Inspect cache behavior

Witness stored entries with explain() and route operational events through telemetry sinks.

Astilba Cache exposes two complementary observability surfaces. cache.explain() takes a point-in-time witness of one public key, while telemetry reports operational events as they occur. Neither surface turns uncertainty into a stronger correctness claim.

Call explain(key) with the same application-facing key used by getOrSet():

const explanation = await cache.explain(`product:${productId}`)
if (!explanation.present) {
console.log("not present", explanation.tier, explanation.reader)
} else {
console.log({
tier: explanation.tier,
identity: explanation.identity,
verdict: explanation.verdict,
reader: explanation.reader,
})
}

A missing, undecodable, or codec-incompatible entry is a reportable { present: false, tier: “miss” } result, not an exception.

Result field What it witnesses
key The application-facing key passed to explain().
tier The first decodable copy observed in L1 or L2, or miss.
identity.tags The stored user and reserved tags.
identity.scope The stored scope literal, or an explicit unreadable marker.
identity.bornEpoch The invalidation epoch at which the entry was born.
identity.ttl Stored TTL evidence. Current entries report { kind: “not-stored” }.
identity.kind A value or negative entry.
verdict Current fresh, stale, dead, or unknown classification and the soft and hard epochs behind it.
reader The local reader’s applied epoch, suspicion state, and terminal recovery state.

Without a coordinated invalidation reader, a present entry receives the codec-only fresh verdict and zero epochs. That says no local invalidation authority exists to classify it otherwise; it is not evidence from a Registry check.

explain() reports what this Cache instance already knows. It deliberately does not:

  • perform a live Registry check;
  • trigger mirror recovery or resynchronization;
  • hydrate an L2 result into L1;
  • preserve a historical dependency graph;
  • accept a scope argument.

The method canonicalizes the key at the default public scope. It cannot directly address tenant or principal-derived variants. Within a React Router request, requestDependencies can still show the dependencies the current render has recorded, including their scope evidence and any l3 flag.

The L1 and L2 probes call each Store’s ordinary get(). Cache performs no write while explaining, but a Store may have read behavior of its own; for example, memory() updates LRU recency on a successful read.

Pass a sink directly for local use, or use TelemetryConfig when you need hosted pseudonymization or sink-failure reporting:

const cache = createCache({
namespace: "storefront",
clock,
rng,
l2,
telemetry: {
sink: (event) => logger.info(event),
onSinkError: (error) => logger.warn({ error }, "cache telemetry failed"),
},
})

Built-in event delivery swallows both synchronous sink throws and asynchronous sink rejections so observability code cannot fail a read, fill, or response. onSinkError is called for a swallowed failure; failures from that hook are swallowed too.

For a hosted sink, set hosted: true and provide a project salt. String fields other than the event type are HMAC-pseudonymized before delivery. Hosted mode without a salt suppresses the event instead of forwarding raw strings.

TELEMETRY_EVENTS is the public catalog, and TelemetryEventName is the union of its values.

Event Current source behavior
writeback_throttled A retryable throttled L2 write was suppressed and the origin result remained non-durable.
l2_write_error Another retryable L2 write failure was suppressed.
singleflight_option_mismatch Production mode ran incompatible same-key work separately.
private_evicted A configured memory() sink observed a principal-scoped entry evicted by an LRU bound.
poll_tick_failed React Router’s request-piggyback recovery tick rejected.
l3_ineligible React Router demoted a managed response for budget, late_tag, scope, or scope_unreadable.
regid_divergence The Coordinator writes a Workers log when a derived Registry identity differs from its journaled identity.
registry_degraded Reserved in the catalog; no current emit site.
state_stale Reserved in the catalog; no current emit site.

The catalog closes the event-name vocabulary, not every event payload. Treat fields other than type according to the specific event you consume.

The React Router middleware accepts its own telemetry and onSinkError options for poll_tick_failed and l3_ineligible. Pass the same sink explicitly if you want those events beside kernel events:

cacheMiddleware({ cache, telemetry: sink, onSinkError, waitUntil })

The memory() driver also accepts telemetry and onSinkError. It emits private_evicted only when memory pressure removes a usr:-scoped entry, with count and byte information but no key, hash, or tag. TTL expiry, explicit deletion, and replacement do not emit that event.

createWorkersCache() currently fixes its internal L1 construction and exposes no telemetry option. To receive private_evicted from a memory L1, compose createCache() and memory({ telemetry }) directly. Kernel telemetry and React Router middleware telemetry likewise remain separate options in the current preview.

Continue with Cache HTTP responses for the response safety gate, Control cache sharing for private storage rules, or API reference for the complete types.