6
6
"""
7
7
import asyncio
8
8
import base64
9
+ import logging
9
10
import os
10
11
from datetime import datetime , timedelta , timezone
11
12
from typing import Tuple , Union
12
13
13
14
import aioredis
14
15
from aioredis .pool import RedisPool
16
+ from async_timeout import timeout
15
17
16
18
__all__ = ['RedisSettings' , 'RedisMixin' ]
19
+ logger = logging .getLogger ('arq.utils' )
17
20
18
21
19
22
class RedisSettings :
20
23
"""
21
24
No-Op class used to hold redis connection redis_settings.
22
25
"""
26
+ __slots__ = 'host' , 'port' , 'database' , 'password' , 'conn_retries' , 'conn_timeout' , 'conn_retry_delay'
27
+
23
28
def __init__ (self ,
24
29
host = 'localhost' ,
25
30
port = 6379 ,
26
31
database = 0 ,
27
- password = None ):
32
+ password = None ,
33
+ conn_timeout = 1 ,
34
+ conn_retries = 5 ,
35
+ conn_retry_delay = 1 ):
28
36
"""
29
37
:param host: redis host
30
38
:param port: redis port
@@ -35,6 +43,9 @@ def __init__(self,
35
43
self .port = port
36
44
self .database = database
37
45
self .password = password
46
+ self .conn_timeout = conn_timeout
47
+ self .conn_retries = conn_retries
48
+ self .conn_retry_delay = conn_retry_delay
38
49
39
50
40
51
class RedisMixin :
@@ -56,12 +67,23 @@ def __init__(self, *,
56
67
self .redis_settings = redis_settings or getattr (self , 'redis_settings' , None ) or RedisSettings ()
57
68
self ._redis_pool = existing_pool
58
69
59
- async def create_redis_pool (self ) -> RedisPool :
70
+ async def create_redis_pool (self , * , _retry = 0 ) -> RedisPool :
60
71
"""
61
72
Create a new redis pool.
62
73
"""
63
- return await aioredis .create_pool ((self .redis_settings .host , self .redis_settings .port ), loop = self .loop ,
64
- db = self .redis_settings .database , password = self .redis_settings .password )
74
+ addr = self .redis_settings .host , self .redis_settings .port
75
+ try :
76
+ with timeout (self .redis_settings .conn_timeout ):
77
+ return await aioredis .create_pool (addr , loop = self .loop , db = self .redis_settings .database ,
78
+ password = self .redis_settings .password )
79
+ except (ConnectionError , OSError , aioredis .RedisError , asyncio .TimeoutError ) as e :
80
+ if _retry < self .redis_settings .conn_retries :
81
+ logger .warning ('redis connection error %s %s, %d retries remaining...' ,
82
+ e .__class__ .__name__ , e , self .redis_settings .conn_retries - _retry )
83
+ await asyncio .sleep (self .redis_settings .conn_retry_delay )
84
+ return await self .create_redis_pool (_retry = _retry + 1 )
85
+ else :
86
+ raise
65
87
66
88
async def get_redis_pool (self ) -> RedisPool :
67
89
"""
0 commit comments