redis / redis-py

Redis Python client
MIT License
12.67k stars 2.53k forks source link

Error: Event loop is closed when running asyncio + sentinel examples #3431

Open pedroplytix opened 3 days ago

pedroplytix commented 3 days ago

Version: redis-py: 5.2 / redis 6.2.7

Platform: Python 3.12.7 on Debian (Docker container: python:3.12.7-slim-bookworm)

Description: Running the example provided in redis-py documentation to connect to sentinel using asyncio:

import asyncio
from redis.asyncio.sentinel import Sentinel

async def main():
    sentinel = Sentinel([("localhost", 26379), ("sentinel2", 26379)])
    r = sentinel.master_for("mymaster")

    ok = await r.set("key", "value")
    assert ok
    val = await r.get("key")
    assert val == b"value"

if __name__ == "__main__":
    asyncio.run(main())

I get this error:

Exception ignored in: <function AbstractConnection.__del__ at 0x7f53bc6e14e0>
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 215, in __del__
    f"unclosed Connection {self!r}", ResourceWarning, source=self
                          ^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/sentinel.py", line 35, in __repr__
    f"(service={pool.service_name}"
                ^^^^^^^^^^^^^^^^^
ReferenceError: weakly-referenced object no longer exists

If I try to close the connection after assert val == b"value":

...
    assert val == b"value"
    await r.aclose()
...

I get this error:

Exception ignored in: <function AbstractConnection.__del__ at 0x7ff6587ed580>
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 217, in __del__
    self._close()
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 224, in _close
    self._writer.close()
  File "/usr/local/lib/python3.12/asyncio/streams.py", line 358, in close
    return self._transport.close()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/selector_events.py", line 1210, in close
    super().close()
  File "/usr/local/lib/python3.12/asyncio/selector_events.py", line 875, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 795, in call_soon
    self._check_closed()
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 541, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

Checking with older redis-py versions, I've checked that this code worked fine in redis==5.0.0 and redis==5.0.1, but started to fail in redis==5.0.2

stg609 commented 2 days ago

I get the same error in redis==5.1.0 , 5.2.0

rad-pat commented 1 day ago

Have you tried also adding await sentinel.aclose() ?

IlianIliev commented 1 day ago

Ok, I dug a bit into the problem and I kind of managed to reproduce it. For me, the error is thrown only if the service name does not match the one of the actual sentinel. For example:

    r = sentinel.master_for("mymaster")  # wrong name, causes the event loop closed
    r = sentinel.master_for("redis-py-test")  # correct name, everything works

With that said, please open your redis.conf and check if the name of the service matches the one you are providing. This should solve the issue you are facing.

As for the actual reason to see this error instead of a more informative one, I will have to dig a bit more to see what causes it.

pedroplytix commented 1 day ago

@IlianIliev it is not my case:

127.0.0.1:26379> sentinel master other
(error) ERR No such master with that name

127.0.0.1:26379> sentinel master mymaster
 1) "name"
 2) "mymaster"

Before I ran the script, I check key key in redis and it is empty:

127.0.0.1:6379> get key
(nil)

After I ran the test:

> python app.py 
Exception ignored in: <function AbstractConnection.__del__ at 0x7f103ca9e980>
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 215, in __del__
    f"unclosed Connection {self!r}", ResourceWarning, source=self
                          ^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/sentinel.py", line 35, in __repr__
    f"(service={pool.service_name}"
                ^^^^^^^^^^^^^^^^^
ReferenceError: weakly-referenced object no longer exists
Exception ignored in: <function AbstractConnection.__del__ at 0x7f103ca9e980>
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 217, in __del__
    self._close()
  File "/usr/local/lib/python3.12/site-packages/redis/asyncio/connection.py", line 224, in _close
    self._writer.close()
  File "/usr/local/lib/python3.12/asyncio/streams.py", line 358, in close
    return self._transport.close()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/selector_events.py", line 1210, in close
    super().close()
  File "/usr/local/lib/python3.12/asyncio/selector_events.py", line 875, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 795, in call_soon
    self._check_closed()
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 541, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

If I query redis for key key:

127.0.0.1:6379> get key
"value"
vladvildanov commented 22 hours ago

Thanks for reaching out! Will start an investigation on this

vladvildanov commented 21 hours ago

@pedroplytix You're right, it's started since 5.0.2 because of this PR. It's a part of 5.0.2 release

https://github.com/redis/redis-py/pull/3001/files#diff-9d65b3446d551c0ed9e7bb0a16cf7b31f8b94c1024b9bf05afaa63ce70950019R29

vladvildanov commented 21 hours ago

Looks like up to this point garbage collector already removed an original ConnectionPool object, so it's impossible to reference it