fix(keycloak): readiness could not fail on the database, and the measured budget

Two corrections found while verifying the first CI run, not from the plan.

THE DEFAULT THAT MADE THE READINESS PROBE TOOTHLESS. Keycloak's DATABASE health
check is the one row of upstream's "Available Checks" table whose "Requires
Metrics" column says yes; Cluster, Graceful shutdown and Keycloak Initialized
say no. So with metrics-enabled=false - this chart's default until now -
/health/ready answers 200 while the database is gone, and the pod keeps taking
logins it cannot serve. The probe path was right and the aggregate behind it
contained nothing external, which is the urlaubsverwaltung defect (#218) in a
new hiding place: not a health group this time, but an option named for
observability, where nobody looks when reviewing a probe.

keycloak.metrics.enabled therefore defaults to TRUE, and its comment argues
about readiness rather than about scraping. The price is stated at the value:
/metrics then answers on the management port, unauthenticated, carrying realm
names, client ids and login counters - on a port that is ClusterIP-only and in
no Ingress. Switching it off is now documented as a decision about whether
readiness can fail at all. The CI fixture keeps it on and says why, so a green
run really does touch the database.

THE STARTUP BUDGET NOW CARRIES ITS MEASUREMENT. From the install-test of PR #87
on the Gitea docker runner (k3s v1.35.5, two replicas, empty database, image
pulled during the run): 71.8 seconds from `helm install` to BOTH pods Ready -
first at ~70.8s, second one second later, 0 restarts. The 600s budget stays,
with the reason for the headroom written down: everything in that run was the
cheap case, and a first install against a real database serializes the second
replica behind the first's Liquibase migration lock instead of letting it
arrive a second later.

AND AN OVERCLAIM OF MY OWN, REMOVED. ci/keycloak/test-values.yaml said a green
run proves jdbc-ping found the second replica. It does not: readiness reports
one node's own health, so a node that never found its peer is Ready, healthy
and alone - and two such nodes are two islands that each believe they are the
cluster. What the test does rule out is the louder half, a replica that cannot
start or cannot share the database. The Infinispan view has to be read out of
the log, which is what step 2 of NOTES.txt tells the operator to do, now with
the note that no probe and no install-test replaces that step. Measurement
point 4 of the plan therefore stays open.

Part of #83
This commit is contained in:
jklein authored and Justin Klein committed 2026-10-04 18:00:20 +00:00
1 parent 4c1cff1744
commit 320257c742
6 files changed
+113 -40

No files matched your search

+1 -1
View File
@@ -76,7 +76,7 @@
}
},
"metrics": {
"description": "A Keycloak BUILD option (metrics-enabled). Off, because the management port is unauthenticated in this chart's configuration and metrics add realm and client names to that surface.",
"description": "A Keycloak BUILD option (metrics-enabled). ON, and not for the metrics: upstream's table of health checks has a 'Requires Metrics' column, and the DATABASE check is the row that says yes. With this false, /health/ready still answers 200 while the database is gone - the urlaubsverwaltung shape (#218), with the dependency hidden behind an option whose name says observability. The price is that /metrics then answers on the management port, unauthenticated, carrying realm and client names - that port is ClusterIP-only and in no Ingress. Switching this off is a decision about whether readiness can fail.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {