Cache control
On this page
Advertising a result as reusable by the client.
class Builder
Section titled “class Builder”public static final class BuilderDescription
Builder for CacheControl; see the class javadoc for field semantics.
Members
Builder ttl(int seconds)Freshness lifetime in whole seconds, relative to full-result receipt.
Builder expires(String rfc3339Utc)Absolute RFC 3339 UTC deadline; the lifetime is expires - now.
Builder scope(String scope)Reuse scope.
Builder noStore(boolean noStore)Explicit “never cache”; overrides any freshness key.
Builder etag(String etag)Strong validator for conditional revalidation.
Builder lastModified(String rfc3339Utc)Weaker RFC 3339 UTC validator; fallback when no ETag is advertised.
Builder revalidatable(boolean revalidatable)Whether the worker can confirm freshness cheaply without recomputing; gates whether the client ever sends a conditional request.
Builder staleWhileRevalidate(int seconds)Grace window to serve stale while revalidating in the background.
Builder staleIfError(int seconds)Grace window to serve stale if a revalidation RPC fails.
Builder notModified(boolean notModified)304-equivalent: set on a 0-row batch replying to a conditional request to assert the client’s stored payload is still fresh.
Builder partitionScope(boolean partitionScope)Opt in to per-partition caching: on a SINGLE_VALUE_PARTITIONS
function the client ADDITIONALLY stores the result split by partition
value, so a later =/IN scan on the partition column(s)
serves the requested partitions without calling the worker. Additive —
the whole-scan entry is still stored and served.
Builder perValue(boolean perValue)Opt in to per-value memoization: the client ADDITIONALLY
memoizes each distinct worker-input tuple’s output, keyed on that
tuple, so the same value is served without calling the worker on a
later chunk or a later query. Only meaningful for an exchange-mode
map — a scalar, or a blended table-in-out invoked through a
correlated LATERAL.
Defaults off, and should stay off unless one call is genuinely expensive. A per-value serve is not free: it costs a key probe, a decode of the stored value and a row-assembly step, for every distinct value. That only pays back when it is cheaper than simply asking the worker. For a cheap arithmetic map it is a large net loss — the probe-and-assemble path measures roughly 50x the cost of the worker call it replaces. The engine cannot tell an expensive call from a cheap one, which is why this is an explicit advertisement rather than a heuristic: only the function author knows. Turn it on for a model inference, a geocode, or a rate-limited remote fetch, where one call dwarfs a cache probe.
Independent of whole-result cacheability: a function can be worth
caching by ttl without per-value memoization paying off, and
vice versa.
CacheControl build()Validate and freeze.
class CacheControl
Section titled “class CacheControl”public final class CacheControlDescription
Result-cache control metadata (vgi.cache.*) advertised by a table
function on the first data batch it emits.
The vocabulary mirrors HTTP caching (RFC 9111/9110): a freshness lifetime
(Builder#ttl/Builder#expires), a reuse Builder#scope,
validators (Builder#etag / Builder#lastModified) for
conditional revalidation, and stale-serving grace windows. Presence of
ttl or expires is what makes a result cacheable;
Builder#noStore overrides any freshness key.
The key strings are the single source of truth shared with the C++
extension, which reads them by string off each batch’s custom_metadata.
Render them with #toMetadata() and hand the map to
OutputCollector.emit(root, metadata):
`BatchUtil.emit(schema, rows, out, CacheControl.ttl(300).toMetadata(), filler);`Booleans render as "1" and are omitted when false; unset optional
fields are omitted entirely. scope is always emitted. Mirrors
vgi-python’s vgi/cache_control.py.
Members
String TTL_KEY = “vgi.cache.ttl”Freshness lifetime in whole seconds, relative to full-result receipt.
String EXPIRES_KEY = “vgi.cache.expires”Absolute RFC 3339 UTC freshness deadline.
String NO_STORE_KEY = “vgi.cache.no_store”Explicit “never cache”; overrides any freshness key.
String SCOPE_KEY = “vgi.cache.scope”Reuse scope — #SCOPE_CATALOG or #SCOPE_TRANSACTION.
String ETAG_KEY = “vgi.cache.etag”Strong validator (opaque quoted string) for conditional revalidation.
String LAST_MODIFIED_KEY = “vgi.cache.last_modified”Weaker RFC 3339 UTC validator; fallback when no ETag.
String REVALIDATABLE_KEY = “vgi.cache.revalidatable”The worker can check freshness cheaply without recomputing.
String STALE_WHILE_REVALIDATE_KEY = “vgi.cache.stale_while_revalidate”Grace window (seconds) to serve stale while revalidating in the background.
String STALE_IF_ERROR_KEY = “vgi.cache.stale_if_error”Grace window (seconds) to serve stale if a revalidation RPC fails.
String NOT_MODIFIED_KEY = “vgi.cache.not_modified”304-equivalent, set on a 0-row batch replying to a conditional request.
String PARTITION_SCOPE_KEY = “vgi.cache.partition_scope”Opt in to per-partition caching: the client ALSO stores the result split by
partition value. Only meaningful on a SINGLE_VALUE_PARTITIONS function.
String PER_VALUE_KEY = “vgi.cache.per_value”Opt in to per-value memoization: the client ALSO memoizes each
distinct input tuple’s output. Only meaningful for an exchange-mode map
(a scalar, or a blended table-in-out called via correlated LATERAL).
Default OFF — see Builder#perValue(boolean) for why.
String IF_NONE_MATCH_KEY = “vgi.cache.if_none_match”Request-side key: the client’s stored ETag, delivered on the first tick’s
input custom_metadata when it asks the worker to confirm freshness.
String IF_MODIFIED_SINCE_KEY = “vgi.cache.if_modified_since”Request-side key: the client’s stored Last-Modified validator.
String SCOPE_CATALOG = “catalog”Reusable across transactions within the calling catalog identity (default).
String SCOPE_TRANSACTION = “transaction”Reusable only within the transaction that produced it.
Builder builder()A fresh builder with no freshness keys and #SCOPE_CATALOG.
CacheControl ttl(int seconds)Shorthand for the common case: a catalog-scoped result with a freshness lifetime and nothing else.
CacheControl noStore()Shorthand for a result the client must never store.
Map<String, String> toMetadata()Render to the vgi.cache.* batch-metadata keys.
Map<String, String> merge(Map<String, String> metadata, CacheControl cacheControl)Fold a cache control into an existing emit-metadata map. The rendered
cache keys win on collision (last write), matching vgi-python’s
_merge_cache_control.