Performance
KubeAtlas serves its read API from a graph store that comes in two tiers — Tier 1 (in-memory) and Tier 2 (PostgreSQL + Apache AGE). Both are benchmarked against synthetic stress fixtures so a release can be checked for regressions.
Latency targets
The cluster and namespace views and the blast-radius walk are
the hot read paths. The v1.2 service-level targets, p95:
| Endpoint | Target (p95) |
|---|---|
cluster-view (/graph?level=cluster) | ≤ 1000 ms |
namespace-view (/graph?level=namespace) | ≤ 1000 ms |
| blast-radius | ≤ 500 ms |
These were briefly relaxed to 5–8 s during the v1.0 work, while every
cluster/namespace view materialised the whole graph in the API
process per request. The store-side pushdown aggregation moved
that work into the store (a GROUP BY on Tier 2, a single pass on
Tier 1), so v1.2 reclaims the original targets.
Baselines
Captured with test/perf/bench-v1.sh (100 samples per endpoint) on
Docker Desktop Kubernetes under WSL2. Tier 2 numbers include
kubectl port-forward + WSL2 round-trip overhead; Tier 1 is a local
memory-backed process. p95, milliseconds:
| Endpoint | 5K T1 | 5K T2 | 10K T1 | 10K T2 |
|---|---|---|---|---|
| cluster-view | 7.6 | 145.0 | 29.1 | 360.2 |
| namespace-view | 36.9 | 696.1 | 390.8 | 1433.9 ⚠ |
| blast-radius | 0.7 | 5.9 | 0.7 | 9.3 |
- 5K — the
stress-test-5kfixture (~7.2K resources), the v1.0 / v1.1 baseline (perf-baseline-v1.0.json,perf-baseline-v1.1.json). - 10K — the
stress-test-10kfixture (~14.4K resources), the v1.2 baseline (perf-baseline-v1.2.json). - Multi-cluster (v1.3) — two-cluster federation fixture
captured by
test/perf/start-multicluster-fixture.sh. The/api/v1/federation/graph?level=clusterpath returns in 6 ms p50 (vs. 2177 ms for the resource-level merge) — a 200× reduction from the store-side cluster-summary aggregator added in v1.3.0. Numbers inperf-baseline-v1.3.json.
cluster-view and blast-radius stay well inside target on both tiers at both scales. blast-radius is near-flat because a recursive-CTE traversal is bounded by the affected subgraph, not the total graph.
⚠ The one over-target cell — 10K Tier 2 namespace-view (1.43 s).
This is a known artefact of the synthetic fixture, not a product
regression. stress-test-10k puts all ~10K resources in a single
namespace; on Tier 2 the NamespaceSubgraph JOIN then costs more
than a full scan, because the namespace filter selects 100 % of the
rows. Real namespaces are a small slice of a cluster, where the
JOIN is a clear win — and the realistic whole-graph aggregation,
cluster-view, lands at 360 ms p95. Tier 1 (in-memory) is unaffected.
The synthetic single-namespace fixture is deliberately not optimised
for; see perf-baseline-v1.2.json for the recorded numbers.
Regression gate
test/verify/perf-regression.sh re-runs the benchmark and fails CI
if any metric, on any tier, against either the v1.0 or the v1.2
baseline, regresses by more than 20 %:
# capture a fresh run per fixture, then:
bash test/verify/perf-regression.sh
v1.6.0 validation scope and benchmark targets
The published v1.6.0 release used one-hour functional validation and a separate short public-install smoke check. It does not claim dedicated performance acceptance or endurance. See the release matrix for the exact source, artifacts, evidence, and limits.
The historical baselines above and the original benchmark protocol below are retained as regression tools, not final-release acceptance results. A future performance claim requires all three measurements on the exact tested commit:
| Evidence row | Kubernetes fixture | Resources | Namespace p95 |
|---|---|---|---|
| Default 5K | One synthetic namespace: 5,000 ConfigMaps, 1,000 Deployments, and 200 Services | Chart-default requests and limits; the 75% runtime boundary derives a 384MiB Go soft limit | Gated at 1 second |
| Production 10K | The same deterministic shape distributed over 10 namespaces | v160-production-10k-values.yaml; the same 75% boundary derives 1536MiB from 2Gi | Gated at 1 second |
| Pathological 10K | All 10K fixture objects in one namespace | The same production profile | Recorded, but not substituted for the representative gate |
All three rows gate cluster-view p95 at 1 second and blast-radius p95 at 500 milliseconds. Each endpoint receives at least 100 samples, and any HTTP failure fails the row even when it would fall below the p95 rank. Evidence also records the clean Git commit, immutable application and PostgreSQL image IDs, rendered resource settings and Go memory limit, exact fixture counts, Kubernetes and container-runtime versions, host capacity, RSS, restarts, and OOM state.
The reusable benchmark tools and resource profiles are present on main;
that is not a claim that final-release performance validation passed. Keep
workstation-specific orchestration and run evidence outside the source tree.
Retain all three measurements with their inputs, failures, and artifact
identities so another reviewer can independently assess the results.