Skip to content

(asyncio) PubSub does not automatically reconnect #2089

Closed
@kristjanvalur

Description

@kristjanvalur

Version: 4.2.2

Platform: python 3.8 on windows

Description: Running a pub-sub task in a loop, requires me to manually call pubsub.connection.connect()

So:

I'm new to python redis, having previously used hiredis and written clients for UnrealEngine using it.

So, I was pleasantly surprised at how the client typically handles reconnections for you, in case there are disconnects.
However, this does not happen with the PubSub client.

Please note that I am using the redis.asyncio module here.

There is a retry mechanism in PubSub._execute, which callse PubSub._disconnect_raise_connect in case of failure.
However, that function only calls connect() in case of a TimeoutError.

If there is a disconnect, for example, because of the connection to the server disappearing for a few seconds, or other,
the connection stays in a disconnected state.

In my tests, I killed and restarted the server and the PubSub.connection stayed in a is_connected==False state, with any read operations resulting in a ConnectionError("Connection closed by server.").

Previously the loop looked somethign like:

async def loop(self):
    await self.perform_any_subscriptions()
    while True:
        try:
            await self.loop_step()
        except redis.ConnectionError:
            pass

async def loop_step(self):
    async for message in self.pubsub.listen():
        await self.dispatch_message(message)
        break

If the connection becomes disconnected, the listen() will simply continually raise ConnectionError("Connection closed by server.").

What I had to do, as a workaround, was to do something like this, for my listening loop:

async def loop(self):
    while True:
        await self.pubsub.connection.call_with_retry(
            self.loop_step,
            lambda e: if isinstance(e, redis.ConnectionError) and self.pubsub.connection and not self.pubsub.connection.is_connected: await self.pubsubconnection.connect()
            )

(use the call_with_retry as a handy catch-all system)

I'm not sure if this is expected, or if it is an error, but from the documentation I'm reading, it seems like PubSub ought to be re-usable across disconnects, automatically reconnecting any previous subscriptions, etc.

UPDATE:

After PR #2148, the async PubSub class now has a connect() method, which can be used to remedy
this. The top loop becomes:

async def loop(self):
    await self.perform_any_subscriptions()
    while True:
        try:
            await self.pubsub.connect()
            await self.loop_step()
        except redis.ConnectionError:
            pass

Previously it was not possible to reconnect a PubSub object after a ConnectionError without issuing a "subscribe()" or "unsubscribe"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions