Readiness Check

Readiness probe for Kubernetes (readinessProbe). NOT the liveness probe.

/health above stays the LIVENESS probe: static, no I/O, so a merely busy event loop never gets the pod killed. This endpoint answers the different question k8s asks for readiness - "should this pod receive traffic?" - and it can answer no. Before it existed, a pod with a dead database or an unapplied migration passed readiness, joined the Service endpoints, took live class traffic, and made kubectl rollout status report success.

Checks (see app/health/readiness.py for the full rationale):

  • database: a real SELECT 1 over its own short-lived connection, hard- bounded by libpq connect_timeout + statement_timeout (~2s), and deliberately NOT taken from the main pool so a load spike cannot fail readiness on every pod at once.
  • migrations: the DB's alembic revision vs the migration head shipped in this image. DB BEHIND code fails (the code will read columns that do not exist). DB AHEAD of code does NOT fail - during an expand/migrate deploy, and during a rollback, the old pods are intentionally running against a newer schema, and failing them would cause the outage the zero-downtime rollout is meant to avoid.

Contract: 200 when servable, 503 when not. k8s only reads the status code; the JSON body names the failing check for humans. Never raises - an unexpected exception is turned into a 503 with a reason, because a 500 stack trace from the probe looks like an app bug and hides the cause.

Plain def on purpose: the DB layer here is synchronous SQLAlchemy + psycopg2, so Starlette runs this in the threadpool. Do not make it async def without an async driver - blocking the single uvicorn worker's event loop under a full class of students is worse than having no probe.

get/health/ready

Response

Successful Response

{"stackTrail":"paths:/health/ready:get:responses:200:content:application/json:schema","oasType":"schema","type":"unknown"}

Changes

No recorded changes to this endpoint across all 1 revision of this API.