CachePolicy

Opt-in, per-namespace tuning of cache lookup and eviction.

What is a CachePolicy?

A CachePolicy tunes how the server filters and ranks lookups, and how it evicts, for a single namespace. It is entirely opt-in: the server ships sane defaults, so most namespaces need no CachePolicy at all. It is purely declarative — the controller flattens all policies and pushes the resolved values to the server; the reconciler never writes CachePolicy.status.

CachePolicy is namespaced, short name cpol. At most one per namespace — a second is rejected at admission (best-effort), and the controller’s deterministic dedup (lexicographically-smallest name wins) is the authoritative backstop.

apiVersion: inferencecache.io/v1alpha1
kind: CachePolicy
metadata:
  name: default
  namespace: serving
spec:
  eviction: LRU
  evictionTTL: 30m
  minimumMatchedTokens: 64
  routingFloorScore: "0.1"
  strategy:
    enableChainMatching: true
    enableTenantHot: true
  affinityRouting: Enabled

The lookup-filter knobs

Three orthogonal filters act at different stages of a lookup:

FieldDefaultStageEffect
minimumPrefixTokensunset (no gate)Request-side, pre-lookupSkip the lookup entirely if the request’s prefix is shorter than this many tokens.
minimumMatchedTokens64 (= 4 KV blocks)Result-side, per-replicaA replica must have at least this many matched tokens to be offered as a PREFIX_MATCH. 0 opts out.
routingFloorScore"0.1"Result-side, per-responseThe top-ranked replica’s score must clear this floor, or the response degrades to no hint. Stringified float; "0" opts out; negatives clamp to 0.

The minimumMatchedTokens floor exists because chat templates frame every prompt with a shared system-prompt prefix; without a floor, that shared framing would match every replica and produce a useless hint. The routingFloorScore gate catches the mirror case — a prefix held by every replica has zero distinguishing power (see LookupRoute & ranking.

Eviction

FieldDefaultMeaning
evictionLRUCap-based eviction ordering — LRU or LFU.
evictionTTLserver default 30mAn entry ages out this long after it was last seen (reported). Lookups do not refresh lastSeen. Must be strictly positive when set.

Strategy gates

spec.strategy gates the ranking strategies:

FieldDefaultEffect
enableChainMatchingtrueEnable longest-prefix block-chain matching.
requireChainfalseIf true, a request without a valid block-hash chain returns POLICY_REQUIRES_CHAIN immediately (before touching the index).
enableTenantHottrueEnable the TENANT_HOT fallback on a prefix miss.

spec.affinityRouting (Enabled by default, Disabled to turn off) decides the final fallback: when the ranker would otherwise return NO_HINT, affinity routing returns a single stable replica chosen by a consistent hash — useful for diffuse single-turn workloads. See reason codes.

Status

status.conditions and status.observedGeneration exist but are reserved — the reconciler does not write them today. The policy takes effect by being pushed to the server, not through a status handshake.