diff --git a/docs/introduction/configuration.md b/docs/introduction/configuration.md index 579740c..e617f5e 100644 --- a/docs/introduction/configuration.md +++ b/docs/introduction/configuration.md @@ -486,7 +486,7 @@ Additional params: - `health_checks_include_in_schema` - `health_checks_probes`: named async callables, each returning `bool`, that the route runs on every request -The health check route on Litestar, FastAPI, FastMCP and FastStream runs every probe concurrently. On FastStream the probes run alongside the broker pings. The route returns 200 with `health_status: true` when everything passes. If any probe returns `False` or raises, the route returns 500 with the same JSON body and `health_status: false`. Each failed probe logs one warning with its name; the response body does not say which probe failed. +The health check route on Litestar, FastAPI, FastMCP and FastStream runs every probe concurrently. On FastStream the probes run alongside the broker pings. The route returns 200 with `health_status: true` when everything passes. If any probe returns `False` or raises, the route returns 503 Service Unavailable with the same JSON body and `health_status: false`. Each failed probe logs one warning with its name; the response body does not say which probe failed. ```python import functools diff --git a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py index 338e8d9..baeb008 100644 --- a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py @@ -194,7 +194,7 @@ def build_fastapi_health_check_router(self) -> "fastapi.APIRouter": async def health_check_handler(response: fastapi.Response) -> HealthCheckTypedDict: health_status = await self.define_health_status() if not health_status: - response.status_code = fastapi.status.HTTP_500_INTERNAL_SERVER_ERROR + response.status_code = fastapi.status.HTTP_503_SERVICE_UNAVAILABLE return self.render_health_check_data(health_status=health_status) return fastapi_router diff --git a/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py b/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py index e99f75c..47f7745 100644 --- a/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py @@ -229,7 +229,7 @@ async def health_check_handler(_: "Request") -> "JSONResponse": health_status = await self.define_health_status() return JSONResponse( dict(self.render_health_check_data(health_status=health_status)), - status_code=status.HTTP_200_OK if health_status else status.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status.HTTP_200_OK if health_status else status.HTTP_503_SERVICE_UNAVAILABLE, ) diff --git a/lite_bootstrap/bootstrappers/faststream_bootstrapper.py b/lite_bootstrap/bootstrappers/faststream_bootstrapper.py index 776c363..949a698 100644 --- a/lite_bootstrap/bootstrappers/faststream_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/faststream_bootstrapper.py @@ -127,7 +127,7 @@ async def check_health(_: object) -> "AsgiResponse": health_status = await self.define_health_status() return AsgiResponse( json.dumps(self.render_health_check_data(health_status=health_status)).encode(), - 200 if health_status else 500, + 200 if health_status else 503, headers={"content-type": "application/json"}, ) diff --git a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py index b819a54..191378e 100644 --- a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py @@ -287,7 +287,7 @@ async def health_check_handler() -> litestar.Response[HealthCheckTypedDict]: health_status = await self.define_health_status() return litestar.Response( self.render_health_check_data(health_status=health_status), - status_code=status_codes.HTTP_200_OK if health_status else status_codes.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status_codes.HTTP_200_OK if health_status else status_codes.HTTP_503_SERVICE_UNAVAILABLE, ) return litestar.Router( diff --git a/tests/conftest.py b/tests/conftest.py index 1c9394c..19e9ce9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -65,8 +65,8 @@ async def raising_probe() -> bool: HEALTH_PROBE_CASES: typing.Final = [ pytest.param({"db": passing_probe, "cache": passing_probe}, 200, True, [], id="all-pass"), - pytest.param({"db": passing_probe, "cache": failing_probe}, 500, False, ["cache"], id="one-returns-false"), - pytest.param({"db": passing_probe, "cache": raising_probe}, 500, False, ["cache"], id="one-raises"), + pytest.param({"db": passing_probe, "cache": failing_probe}, 503, False, ["cache"], id="one-returns-false"), + pytest.param({"db": passing_probe, "cache": raising_probe}, 503, False, ["cache"], id="one-raises"), ] diff --git a/tests/test_faststream_bootstrap.py b/tests/test_faststream_bootstrap.py index b3e41d4..8c8c9c5 100644 --- a/tests/test_faststream_bootstrap.py +++ b/tests/test_faststream_bootstrap.py @@ -132,7 +132,7 @@ async def test_faststream_bootstrap_health_check_wo_broker() -> None: test_client = TestClient(app=application) response = test_client.get(bootstrap_config.health_checks_path) - assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE assert response.json() == { "health_status": False, "service_name": "microservice", @@ -305,7 +305,7 @@ async def test_faststream_health_check_fails_when_second_broker_is_down(broker: TestClient(app=application) as test_client, ): response = test_client.get(config.health_checks_path) - assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE assert response.json()["health_status"] is False finally: bootstrapper.teardown() @@ -573,7 +573,7 @@ async def test_faststream_health_check_fails_on_broker_ping_despite_passing_prob finally: bootstrapper.teardown() - assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE assert response.json()["health_status"] is False