On a server, the shop usually runs as several copies behind a load balancer, which shares the visitors' requests between them. Every few seconds, the load balancer and the platform that starts the copies ask each copy two questions over HTTP.
Health: are you alive? If a copy doesn't answer 200, the platform restarts it, so keep this check simple: a restart can't fix a database problem. Readiness: can you serve requests right now? If a copy doesn't answer 200, the load balancer sends it no requests until it does. Here you really try what the app needs, and answer 503 Service Unavailable when it fails.
For example, an app that keeps uploads in a folder could check that the folder is there:
app.HandleFunc("GET /uploads-ok", func(w http.ResponseWriter, r *http.Request) {
_, err := os.ReadDir("uploads")
if err != nil {
writeJSON(w, 503, map[string]string{"uploads": "missing"})
return
}
writeJSON(w, 200, map[string]string{"uploads": "ok"})
})
The check plays the load balancer: it asks /health and /ready, takes away the disk with shop.db, asks again, then brings the disk back. When your code is done, it prints:
Every few seconds, each copy of the shop is asked:
GET /health -> 200 {"status": "ok"} -> alive, keep it running
GET /ready -> 200 {"status": "ready"} -> ready, the load balancer sends it requests
The disk with shop.db is gone:
GET /health -> 200 {"status": "ok"} -> alive, keep it running
GET /ready -> 503 {"status": "not ready"} -> not ready, the load balancer sends it no requests for now
The disk is back:
GET /ready -> 200 {"status": "ready"} -> ready, the load balancer sends it requests
Your task: in newApp, register GET /health, which answers {"status": "ok"}, and GET /ready, which opens shop.db, runs SELECT COUNT(*) FROM products with db.QueryRow(…).Scan(&count) and answers {"status": "ready"}, or {"status": "not ready"} with status 503 when Scan returns an error.
Assignment
Follow the Coding Tutorial to add the /health and /ready handlers.
Hint
Don't check the database in /health. In /ready, run a query: sql.Open doesn't even look at the file, and a missing shop.db quietly becomes a new, empty one. Scan returns every failure as err, and after the 503 answer, return, so the handler doesn't also answer ready.
Free plan: 30 tutor messages. PRO is unlimited.
