We are facing [Errno 54] Connection reset by peer
at very random in our application and looks like it is being triggered by redis server than client. Python's redis client have backoff
strategy implementation but it's unable to handle this scenario.
There are github issues on official repo as well and many people commented recently confirming this problem.
- Random ConnectionErrors (104, Connection reset by peer) #1186
- redis.exceptions.ConnectionError: Error while reading from socket: (104, 'Connection reset by peer') #1439
Step to reproduce
$ ipython
# in python client
import redis
from redis.retry import Retry
from redis.exceptions import (TimeoutError, ConnectionError)
from redis.backoff import ExponentialBackoff
# connect client with exponential backoff retry
client = redis.StrictRedis(retry=Retry(ExponentialBackoff(cap=10, base=1), 25), retry_on_error=[ConnectionError, TimeoutError, ConnectionResetError], health_check_interval=1)
client.keys()
# print all keys
Now reset connection directly from redis server
$ redis-cli
RESET
Wait for 120 second or more and run client again
# in client
client.keys()
---------------------------------------------------------------------------
ConnectionResetError Traceback (most recent call last)
<ipython-input-91-011ce9f936fc> in <module>
----> 1 client.keys("rq*")
~/path-to-python-env/env/lib/python3.8/site-packages/redis/commands/core.py in keys(self, pattern, **kwargs)
1386 For more information check https://redis.io/commands/keys
1387 """
-> 1388 return self.execute_command("KEYS", pattern, **kwargs)
1389
1390 def lmove(self, first_list, second_list, src="LEFT", dest="RIGHT"):
~/path-to-python-env/env/lib/python3.8/site-packages/redis/client.py in execute_command(self, *args, **options)
1168 pool = self.connection_pool
1169 command_name = args[0]
-> 1170 conn = self.connection or pool.get_connection(command_name, **options)
1171
1172 try:
~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in get_connection(self, command_name, *keys, **options)
1315 # closed. either way, reconnect and verify everything is good.
1316 try:
-> 1317 if connection.can_read():
1318 raise ConnectionError("Connection has data")
1319 except ConnectionError:
~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
793 if not sock:
794 self.connect()
--> 795 return self._parser.can_read(timeout)
796
797 def read_response(self, disable_decoding=False):
~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
315
316 def can_read(self, timeout):
--> 317 return self._buffer and self._buffer.can_read(timeout)
318
319 def read_response(self, disable_decoding=False):
~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in can_read(self, timeout)
222
223 def can_read(self, timeout):
--> 224 return bool(self.length) or self._read_from_socket(
225 timeout=timeout, raise_on_timeout=False
226 )
~/path-to-python-env/env/lib/python3.8/site-packages/redis/connection.py in _read_from_socket(self, length, timeout, raise_on_timeout)
192 sock.settimeout(timeout)
193 while True:
--> 194 data = self._sock.recv(socket_read_size)
195 # an empty string indicates the server shutdown the socket
196 if isinstance(data, bytes) and len(data) == 0:
ConnectionResetError: [Errno 54] Connection reset by peer
Running it second time works perfectly, though it should have taken care by Retry strategy.
client.keys()
# prints all keys
configuration
redis server - 6.2.6
python redis - 4.1.0
We can write our own try/catch around redis client, but we are using some libraries like rq
and flask-cache
which uses redis internally and have no interface to modify it's flow.
Any help is much appreciated.
RESET
event. And redis internally uses pool by default if not specified. – Irmine