Hone logo
Hone
Problems

Go Health Checker: Building a Resilient Application Endpoint

Modern applications, especially microservices, need to be robust and reliable. A crucial aspect of this is ensuring that services are healthy and responsive. Implementing health checks allows external systems, like load balancers or orchestration platforms, to monitor the status of your application and react accordingly, such as rerouting traffic away from unhealthy instances.

Problem Description

Your task is to implement a simple HTTP health check endpoint for a Go application. This endpoint will be responsible for reporting the overall health status of the application.

Key Requirements:

  1. HTTP Endpoint: Create an HTTP server that listens on a specific port (e.g., 8080).
  2. Health Check Path: Define an HTTP GET endpoint, typically /health, that external systems can query.
  3. Status Reporting:
    • If the application is healthy, the /health endpoint should return an HTTP status code of 200 OK and a JSON response indicating "status": "healthy".
    • If the application is unhealthy, the /health endpoint should return an HTTP status code of 503 Service Unavailable and a JSON response indicating "status": "unhealthy".
  4. Simulating Unhealthiness: For demonstration purposes, you should be able to toggle the application's health status. This could be through a simple mechanism like a global variable or by having a separate administrative endpoint (though for this challenge, a global variable is sufficient).
  5. Graceful Shutdown: The server should handle interrupt signals (like SIGINT from Ctrl+C) gracefully, allowing it to shut down cleanly.

Expected Behavior:

  • When the application starts, it should be considered healthy by default.
  • When a request is made to /health while the application is healthy, a 200 OK with {"status": "healthy"} should be returned.
  • When the application's health is toggled to unhealthy, a request to /health should return 503 Service Unavailable with {"status": "unhealthy"}.
  • When the application's health is toggled back to healthy, /health should return 200 OK again.
  • Upon receiving an interrupt signal, the server should stop listening for new connections and exit.

Edge Cases to Consider:

  • What happens if multiple requests are made concurrently to the health check endpoint?
  • How can we ensure the status change is reflected immediately?

Examples

Example 1: Healthy Application

Input:
GET /health HTTP/1.1
Host: localhost:8080

Output:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 20

{"status": "healthy"}

Example 2: Unhealthy Application

Assume the health status has been toggled to unhealthy.

Input:
GET /health HTTP/1.1
Host: localhost:8080

Output:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Content-Length: 21

{"status": "unhealthy"}

Example 3: Toggling Health Status (Conceptual - no direct HTTP interaction shown)

This illustrates how the internal state might change.

  • Initial State: isHealthy = true
  • Action: Code logic changes isHealthy to false.
  • Subsequent Request: GET /health now returns 503 Service Unavailable.
  • Action: Code logic changes isHealthy back to true.
  • Subsequent Request: GET /health now returns 200 OK.

Constraints

  • The HTTP server must listen on port 8080.
  • The health check endpoint path must be /health.
  • The JSON response must strictly adhere to the format: {"status": "healthy"} or {"status": "unhealthy"}.
  • The application should handle at least 100 concurrent requests to the /health endpoint without significant latency degradation (for this simplified version, basic Go net/http concurrency is acceptable).
  • The solution must be written in Go.

Notes

  • You'll likely want to use the built-in net/http package in Go.
  • Consider how to manage the health status safely across potential concurrent requests. A sync.Mutex might be useful if you decide to implement a toggle mechanism that modifies a shared state.
  • For graceful shutdown, look into os/signal and how to pass a context to http.Server.ListenAndServe.
  • Think about how you would extend this to check the health of external dependencies (databases, other microservices) if this were a more complex scenario.
Loading editor...
go