When a PubSub needs to issue a PING due to the health check feature, it does not consider that there might be no subscriptions at the moment. Redis responds differently to PING depending on whether there are active subscriptions or not: if there are no subscriptions it just returns the argument as a bulk response, instead of a multi-bulk with "pong" and the response. This breaks the code that detects the health check response, and instead the individual bytes of the aioredis-py-health-check string get inserted into the returned message.
To Reproduce
Install aioredis 2.0.0
Run this code:
#!/usr/bin/env python3
import asyncio
import aioredis
async def poll(ps):
while True:
message = await ps.get_message(timeout=1)
if message is not None:
print(message)
Note that 97, 105, 111 are the result of indexing b"aioredis-py-health-check" with indices 0, 1, 2.
### Python Version
```console
$ python --version
Python 3.8.10
aioredis Version
$ python -m pip show aioredis
Name: aioredis
Version: 2.0.0
Additional context
redis-py seems to have a similar bug with the interaction between health checks and pub-sub, but the failure mode is not the same (in redis-py it seems to be some sort of race condition, whereas in aioredis it appears reliably reproducible), so this might need an aioredis-specific fix.
Code of Conduct
[X] I agree to follow the aio-libs Code of Conduct
Describe the bug
When a PubSub needs to issue a PING due to the health check feature, it does not consider that there might be no subscriptions at the moment. Redis responds differently to PING depending on whether there are active subscriptions or not: if there are no subscriptions it just returns the argument as a bulk response, instead of a multi-bulk with "pong" and the response. This breaks the code that detects the health check response, and instead the individual bytes of the
aioredis-py-health-check
string get inserted into the returned message.To Reproduce
import asyncio
import aioredis
async def poll(ps): while True: message = await ps.get_message(timeout=1) if message is not None: print(message)
async def main(): r = aioredis.Redis.from_url("redis://localhost", health_check_interval=2) ps = r.pubsub() await ps.subscribe("foo") poller = asyncio.create_task(poll(ps)) await asyncio.sleep(5) await ps.unsubscribe("foo") await asyncio.sleep(5) await ps.subscribe("baz") poller.cancel() try: await poller except asyncio.CancelledError: pass
asyncio.run(main())
Note that 97, 105, 111 are the result of indexing b"aioredis-py-health-check" with indices 0, 1, 2.
aioredis Version
Additional context
redis-py seems to have a similar bug with the interaction between health checks and pub-sub, but the failure mode is not the same (in redis-py it seems to be some sort of race condition, whereas in aioredis it appears reliably reproducible), so this might need an aioredis-specific fix.
Code of Conduct