Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 0 additions & 65 deletions .github/workflows/c2-reconciler-canary.yml

This file was deleted.

72 changes: 0 additions & 72 deletions .github/workflows/c6-engine-wiring-canary.yml

This file was deleted.

34 changes: 12 additions & 22 deletions .github/workflows/default-install-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,16 @@
# - cacheindex/cluster-default.status.observedServer is populated (proves the
# controller's CacheIndex poller is talking to the server's /snapshot)
# - gRPC LookupRoute returns reason_code=NO_HINT (fail-open default)
# - paired sample (config/samples/cachebackend-with-engine.yaml) wires the
# CacheBackend ↔ engine-pod binding: status.matchedEnginePods=1, the
# injected-by annotation is stamped on the engine pod, and an
# InjectedByCacheBackend Event lands on the persisted pod's UID; then
# scaling the engine to 0 drives status.matchedEnginePods=0 via the
# reconciler's RequeueAfter cadence (no CR-side change).
# - External CacheBackend end-to-end: type=External renders no
# Deployment/Service, status.endpoint mirrors spec.endpoint, Ready=True,
# a matching engine pod is admitted with LMCACHE_REMOTE_URL injected
# from the operator-supplied endpoint, and admission rejects the
# known-bad shapes (non-lm:// scheme, empty host, non-External + endpoint)
# - server /readyz and /metrics are reachable through the installed Service
# - current samples pass live server-side admission, and the generic
# CachePolicy/CacheTenant/PromptTemplate/PDTopology APIs remain usable
# - the served CacheBackend CRD contains only the MP schema
# - typed PodLocal MP admission injects the vLLM connector and native sidecar
# - managed Redis renders independently and publishes remote-storage status
# - an idempotent bundle re-apply preserves typed CacheBackend objects
#
# Lightweight (two distroless ~30 MB images + cert-manager + a busybox
# stand-in for the engine container + a pause-image pod, no real engine
# pull), so it runs on every PR. Sister-canaries
# (c2-reconciler-canary, c6-engine-wiring-canary, cpu-substrate-canary)
# cover real engine pods + cross-pod cache reuse with multi-GB images;
# they stay schedule-only.
# Lightweight: no real inference engine, LMCache server, GPU, or model is
# started, so it runs on every PR.
name: default-install-smoke

on:
Expand All @@ -49,11 +41,9 @@ concurrency:
jobs:
install-smoke:
runs-on: ubuntu-latest
# Script targets ~6 min end-to-end (incl. the Calico CNI install the smoke
# needs to enforce NetworkPolicy). The timeout is a circuit breaker for a
# wedged kind cluster / image pull, not the expected runtime; it sits above
# the worst-case Calico readiness budget (~510s) plus the smoke so a wedged
# CNI still leaves room for the exit trap to collect diagnostics.
# The timeout is a circuit breaker for a wedged kind cluster, image build,
# cert-manager rollout, or admission check; the exit trap still has time to
# collect diagnostics.
timeout-minutes: 20
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,8 @@ install-hooks: ## Install git hooks (vendor-neutral naming guard) via core.hooks

.PHONY: verify-naming
verify-naming: ## Fail if core-identity files reference OCI/Oracle (see CONTRIBUTING.md).
@bad=$$(grep -rniEI '\boci\b|oci\.com|oraclecloud|\boracle\b' \
api proto gen pkg config/crd config/rbac config/default config/manager config/observability config/samples config/server config/webhook config/certmanager config/overlays docs/observability internal PROJECT go.mod 2>/dev/null || true); \
@bad=$$(grep -rniEI '(^|[^[:alnum:]_])(oci|oracle)([^[:alnum:]_]|$$)|ocir\.io|oci\.com|oraclecloud' \
api proto gen pkg config/crd config/rbac config/default config/manager config/observability config/samples config/server config/webhook config/certmanager config/overlays docs/design docs/observability docs/reference-stack internal PROJECT go.mod 2>/dev/null || true); \
if [ -n "$$bad" ]; then \
echo "✗ OCI/Oracle reference in core-identity files (banned per CONTRIBUTING.md):"; \
echo "$$bad" | sed 's/^/ /'; \
Expand Down
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A **vendor-neutral, Kubernetes-native cache-policy control plane for LLM inferen
inference-cache makes routing **cache-aware**: it tracks which replica already holds a
prompt's prefix warm and returns that as a routing *hint*, so a gateway can reuse
KV/prefill instead of recomputing it — cutting time-to-first-token and cost. It
**orchestrates** existing KV-cache technology (LMCache, Mooncake); it is **not** a new
**orchestrates** existing KV-cache technology (LMCache); it is **not** a new
distributed cache and **not** the data-plane gateway. Guiding principle — **"we decide
routing; the gateway follows"**: all routing intelligence lives in the server, and the
gateway simply tokenizes → calls `LookupRoute` → routes to the returned replica →
Expand All @@ -27,16 +27,17 @@ The API separates three choices: `spec.runtime` selects the inference runtime,
provider. Supporting another combination is an adapter addition; the core gRPC
contract stays stable.

- **vLLM + LMCache** supports host-only caching, a managed or external
`LMCacheServer`, and managed or external `Mooncake` storage.
- **SGLang + LMCache** supports host-only caching or a managed/external Redis
remote store.
- **vLLM + LMCache** supports typed PodLocal MP with host-only caching or an
optional managed/external Redis L3.
- **SGLang + LMCache** supports the same typed PodLocal MP and Redis profiles;
the engine launch surface remains SGLang-specific.
- **SGLang + SGLangHiCache** uses SGLang's native host tier and does not accept
a remote-storage binding.

See [`config/samples/`](config/samples/) for canonical manifests and
[`docs/design/cachebackend-api.md`](docs/design/cachebackend-api.md) for the
compatibility rules retained for older v1alpha1 resources.
current contract and clearly labeled legacy-compatibility sections retained
until Phase 7 of the migration.

## What's Inference Cache?

Expand Down Expand Up @@ -205,8 +206,7 @@ kubectl get cacheindex cluster-default -o yaml
Both binaries expose Prometheus metrics on their pod's `:8080/metrics`
(prefixed `inferencecache_*`) — the server binary's series cover the
in-memory index and gRPC handlers; the controller binary's series cover
the reconcilers (e.g. `inferencecache_backend_probe_result_total`,
`inferencecache_backend_server_restart_cascades_total`). A default
the reconcilers (for example, `inferencecache_backend_probe_result_total`). A default
alert bundle for the operational silent-failure patterns this code has
hit in production ships under
[`config/observability/`](config/observability/) and is **not** included
Expand All @@ -219,13 +219,13 @@ For prometheus-operator / kube-prometheus installs:
kubectl apply -k config/observability
```

This ships THREE resources: a `ServiceMonitor` (so Prometheus scrapes
`inference-cache-server:8080/metrics`), a `PodMonitor` (so Prometheus
scrapes the controller pod's `:8080/metrics` — required for the
controller-side alerts like `ServerProbeFail` to have a series to
evaluate), and the `PrometheusRule` carrying the alerts.
This ships FOUR resources: a `ServiceMonitor` for
`inference-cache-server:8080/metrics`, one `PodMonitor` for the controller
pod's `:8080/metrics`, one cross-namespace `PodMonitor` for successfully
injected PodLocal LMCache sidecars on their named `lmcache-http` port, and
the `PrometheusRule` carrying the alerts.

> **Caveat — Prometheus Operator selectors.** All three CRs carry
> **Caveat — Prometheus Operator selectors.** All four CRs carry
> example labels (`prometheus: k8s`, plus `role: alert-rules` on the
> PrometheusRule) that match the upstream kube-prometheus stack
> (default `Prometheus` named `k8s`). The `kube-prometheus-stack`
Expand All @@ -250,19 +250,19 @@ expressions) and only fire when the conditions are met.
> **The fifth alert needs a vLLM scrape this bundle does NOT ship.**
> [`LMCacheT2NoHits`](docs/observability/alerts.md#lmcachet2nohits) reads
> `vllm:external_prefix_cache_*` from vLLM engine pods directly. The
> shipped `ServiceMonitor` covers only `inference-cache-server`. To make
> shipped scrape configs do not collect vLLM's own metrics. To make
> that alert effective, add a separate `PodMonitor` for your vLLM
> Deployment (or `kubernetes_sd_configs: pod` for vanilla Prometheus)
> so engine `/metrics` is scraped with both `namespace` and `pod` labels
> attached. See alerts.md "How to enable" for the requirement.

For vanilla Prometheus, ConfigMap mounts, or Helm `prometheus.serverFiles`,
use the flat [`alerting-rules.yaml`](config/observability/alerting-rules.yaml).
**You must also configure scraping yourself, for BOTH the server AND
the controller pod.** The server's `:8080` exposes the index, lookup,
**You must also configure scraping yourself for the server, the controller
pod, and every injected PodLocal LMCache sidecar.** The server's `:8080` exposes the index, lookup,
and auth series; the controller pod's `:8080` exposes the per-stage
probe-result counter (`inferencecache_backend_probe_result_total`)
and the cache-server restart-cascade counter — the controller-side
probe-result counter (`inferencecache_backend_probe_result_total`); each LMCache sidecar exposes
its own `lmcache_mp_*` series on `:8080/metrics` — the controller-side
alerts (`ServerProbeFail` today) load against the controller's
series, so a server-only scrape leaves them inert.

Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/cachebackend_effective_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestEffectiveRemoteStorageUsesOnlyExplicitDeclaration(t *testing.T) {
}

want := &CacheBackendRemoteStorageSpec{
Provider: CacheBackendRemoteStorageProviderMooncake,
Provider: CacheBackendRemoteStorageProviderRedis,
Ownership: CacheBackendRemoteStorageOwnershipManaged,
}
spec.RemoteStorage = want
Expand Down
Loading
Loading