Nobody has started this yet — be first.
Business impact
Orchestrators (Kubernetes readiness probes, a load balancer's health check, Docker Compose's depends_on: condition: service_healthy for downstream services) need to know "can this instance actually serve traffic right now," not just "is the Python process alive." /health already answers the second question. /health/db exists specifically to answer the first -- but right now it always returns 200 {"status": "ok"} regardless of whether Postgres is reachable. If the database is down, degraded, or the connection pool is exhausted, every instance keeps reporting healthy, traffic keeps getting routed to it, and every request fails with a 500 instead of the orchestrator quietly routing around the problem.
Problem
GET /health/db is a flat placeholder that always returns 200 {"status": "ok"} and never touches the database at all.
Current behavior
health_db()'s body is return jsonify(status="ok") with no query, no db reference, and no error handling of any kind -- it cannot distinguish a healthy database from one that is completely unreachable.
Expected behavior
GET /health/db attempts a trivial, fast query against the real database connection (e.g. SELECT 1) using the app's normal db session, and returns 200 {"status": "ok"} if it succeeds or 503 {"status": "error", ...} -- never an unhandled 500 -- if it raises.
Steps to reproduce
Why this matters
This is the smallest possible example of the difference between a liveness check and a readiness check. /health (already correct) answers "is the process up." /health/db needs to answer "can this process reach the thing it depends on" -- which means it has to actually make the call and handle the failure mode itself, not just report success unconditionally.
Suggested approach
Look at app/extensions.py for how db is exposed, and how app/rls.py issues a raw query through it (db.session.execute(...)) -- app/admin_db.py's separate bypass connection is not what you want here; this route should use the same db session everything else does. Catch whatever specific exception a broken connection raises and turn it into a 503 response instead of letting it propagate as a 500.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_02_health_db_check.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b feat/health-db-checkFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/health-db-checkSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inacmeglobexmake seed
*.localhost subdomains resolve to 127.0.0.1 out of the box in curl and every modern browser -- no /etc/hosts editing needed:
curl -s -X POST http://acme.localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
--cookie-jar /tmp/acme.cookies --cookie /tmp/acme.cookies \
-d '{"email": "owner@acme.test", "password": "owner-acme"}'
curl -s http://acme.localhost:5000/api/tenant/settings --cookie /tmp/acme.cookies
For tooling that cannot fake subdomains (some HTTP clients, CI), send X-Tenant-Slug instead -- honored only when TRUST_TENANT_HEADER=true (the Docker dev setup enables it; production does not):
curl -s http://localhost:5000/api/tenant/settings -H "X-Tenant-Slug: acme"
You do not strictly need Docker to work the tickets, but -- unlike a project that can fake its backing services -- this one genuinely needs a real Postgres and Redis, because the whole point is proving isolation against the real database, not a mock. Without Docker: cp .env.example .env, point DATABASE_URL/ADMIN_DATABASE_URL/TEST_DATABASE_URL/TEST_ADMIN_DATABASE_URL/REDIS_URL at a local Postgres/Redis (docker/init-db.sql documents the exact roles/databases the app expects -- notably the saas_admin/BYPASSRLS role migrations need and the plain saas role every request uses), then:
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/flask db upgrade
.venv/bin/pytest practice_tickets/tests/test_ticket_07_webhook_signature_raw_body.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
./practice_tickets/run_tickets.sh 07 12 13 # just a few, by number
All 15 tests fail at the start; as you fix each ticket in app/ (or, for TICKET-12, in migrations/versions/0002_row_level_security.py, plus re-applying it), its test -- and only its test -- should flip to passing. make test runs the project's own main suite; several of its tests (RBAC, tenant-spoofing, and billing-webhook tests among them) fail at the start for the same underlying reasons as specific tickets -- fixing those tickets fixes those failures too, not a separate task.
Level 2
Implement a feature
Extend the system within its own patterns.