Skip to content

Commit d3bb240

Browse files
authored
fix: ping every broker in the FastStream health check (#293) (#296)
1 parent 4daa3da commit d3bb240

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

‎lite_bootstrap/bootstrappers/faststream_bootstrapper.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import dataclasses
23
import json
34
import logging
@@ -104,10 +105,12 @@ async def check_health(_: object) -> "AsgiResponse":
104105

105106
async def _define_health_status(self) -> bool:
106107
config = self.bootstrap_config
107-
if not config.application or not config.application.broker:
108+
if not config.application or not config.application.brokers:
108109
return False
109110

110-
return await config.application.broker.ping(timeout=config.faststream_health_check_broker_timeout)
111+
timeout = config.faststream_health_check_broker_timeout
112+
results = await asyncio.gather(*(broker.ping(timeout=timeout) for broker in config.application.brokers))
113+
return all(results)
111114

112115

113116
@dataclasses.dataclass(kw_only=True)

‎tests/test_faststream_bootstrap.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,49 @@ async def test_faststream_health_check_uses_configured_broker_timeout(broker: Re
229229
bootstrapper.teardown()
230230

231231

232+
async def test_faststream_health_check_fails_when_second_broker_is_down(broker: RedisBroker) -> None:
233+
second_broker = RedisBroker()
234+
config = build_faststream_config(broker=broker)
235+
config.application.add_broker(second_broker)
236+
bootstrapper = FastStreamBootstrapper(bootstrap_config=config)
237+
application = bootstrapper.bootstrap()
238+
try:
239+
with (
240+
patch.object(broker, "ping", new=AsyncMock(return_value=True)),
241+
patch.object(second_broker, "ping", new=AsyncMock(return_value=False)),
242+
TestClient(app=application) as test_client,
243+
):
244+
response = test_client.get(config.health_checks_path)
245+
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
246+
assert response.text == "Service is unhealthy"
247+
finally:
248+
bootstrapper.teardown()
249+
250+
251+
async def test_faststream_health_check_pings_every_broker(broker: RedisBroker) -> None:
252+
expected_timeout = 3.0
253+
second_broker = RedisBroker()
254+
config = dataclasses.replace(
255+
build_faststream_config(broker=broker),
256+
faststream_health_check_broker_timeout=expected_timeout,
257+
)
258+
config.application.add_broker(second_broker)
259+
bootstrapper = FastStreamBootstrapper(bootstrap_config=config)
260+
application = bootstrapper.bootstrap()
261+
try:
262+
with (
263+
patch.object(broker, "ping", new=AsyncMock(return_value=True)) as first_ping,
264+
patch.object(second_broker, "ping", new=AsyncMock(return_value=True)) as second_ping,
265+
TestClient(app=application) as test_client,
266+
):
267+
response = test_client.get(config.health_checks_path)
268+
assert response.status_code == status.HTTP_200_OK
269+
first_ping.assert_called_once_with(timeout=expected_timeout)
270+
second_ping.assert_called_once_with(timeout=expected_timeout)
271+
finally:
272+
bootstrapper.teardown()
273+
274+
232275
def test_faststream_opentelemetry_excluded_urls_in_built_set(broker: RedisBroker) -> None:
233276
bootstrap_config = dataclasses.replace(
234277
build_faststream_config(broker=broker),

0 commit comments

Comments
 (0)