|
|
@ -1,4 +1,3 @@ |
|
|
import logging |
|
|
|
|
|
import time |
|
|
import time |
|
|
from urllib.parse import urlparse |
|
|
from urllib.parse import urlparse |
|
|
|
|
|
|
|
|
@ -18,8 +17,6 @@ except ImportError: # pragma: no cover |
|
|
|
|
|
|
|
|
from .pubsub_manager import PubSubManager |
|
|
from .pubsub_manager import PubSubManager |
|
|
|
|
|
|
|
|
logger = logging.getLogger('socketio') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_redis_sentinel_url(url): |
|
|
def parse_redis_sentinel_url(url): |
|
|
"""Parse a Redis Sentinel URL with the format: |
|
|
"""Parse a Redis Sentinel URL with the format: |
|
|
@ -112,7 +109,7 @@ class RedisManager(PubSubManager): |
|
|
'Redis requires a monkey patched socket library to work ' |
|
|
'Redis requires a monkey patched socket library to work ' |
|
|
'with ' + self.server.async_mode) |
|
|
'with ' + self.server.async_mode) |
|
|
|
|
|
|
|
|
def _get_redis_module_and_error(self): |
|
|
def _get_redis_module(self): |
|
|
parsed_url = urlparse(self.redis_url) |
|
|
parsed_url = urlparse(self.redis_url) |
|
|
scheme = parsed_url.scheme.split('+', 1)[0].lower() |
|
|
scheme = parsed_url.scheme.split('+', 1)[0].lower() |
|
|
if scheme in ['redis', 'rediss']: |
|
|
if scheme in ['redis', 'rediss']: |
|
|
@ -120,13 +117,13 @@ class RedisManager(PubSubManager): |
|
|
raise RuntimeError('Redis package is not installed ' |
|
|
raise RuntimeError('Redis package is not installed ' |
|
|
'(Run "pip install redis" ' |
|
|
'(Run "pip install redis" ' |
|
|
'in your virtualenv).') |
|
|
'in your virtualenv).') |
|
|
return redis, RedisError |
|
|
return redis |
|
|
if scheme in ['valkey', 'valkeys']: |
|
|
if scheme in ['valkey', 'valkeys']: |
|
|
if valkey is None or ValkeyError is None: |
|
|
if valkey is None or ValkeyError is None: |
|
|
raise RuntimeError('Valkey package is not installed ' |
|
|
raise RuntimeError('Valkey package is not installed ' |
|
|
'(Run "pip install valkey" ' |
|
|
'(Run "pip install valkey" ' |
|
|
'in your virtualenv).') |
|
|
'in your virtualenv).') |
|
|
return valkey, ValkeyError |
|
|
return valkey |
|
|
if scheme == 'unix': |
|
|
if scheme == 'unix': |
|
|
if redis is None or RedisError is None: |
|
|
if redis is None or RedisError is None: |
|
|
if valkey is None or ValkeyError is None: |
|
|
if valkey is None or ValkeyError is None: |
|
|
@ -135,14 +132,14 @@ class RedisManager(PubSubManager): |
|
|
'or "pip install valkey" ' |
|
|
'or "pip install valkey" ' |
|
|
'in your virtualenv).') |
|
|
'in your virtualenv).') |
|
|
else: |
|
|
else: |
|
|
return valkey, ValkeyError |
|
|
return valkey |
|
|
else: |
|
|
else: |
|
|
return redis, RedisError |
|
|
return redis |
|
|
error_msg = f'Unsupported Redis URL scheme: {scheme}' |
|
|
error_msg = f'Unsupported Redis URL scheme: {scheme}' |
|
|
raise ValueError(error_msg) |
|
|
raise ValueError(error_msg) |
|
|
|
|
|
|
|
|
def _redis_connect(self): |
|
|
def _redis_connect(self): |
|
|
module, _ = self._get_redis_module_and_error() |
|
|
module = self._get_redis_module() |
|
|
parsed_url = urlparse(self.redis_url) |
|
|
parsed_url = urlparse(self.redis_url) |
|
|
if parsed_url.scheme in {"redis+sentinel", "valkey+sentinel"}: |
|
|
if parsed_url.scheme in {"redis+sentinel", "valkey+sentinel"}: |
|
|
sentinels, service_name, connection_kwargs = \ |
|
|
sentinels, service_name, connection_kwargs = \ |
|
|
@ -158,28 +155,26 @@ class RedisManager(PubSubManager): |
|
|
self.connected = True |
|
|
self.connected = True |
|
|
|
|
|
|
|
|
def _publish(self, data): # pragma: no cover |
|
|
def _publish(self, data): # pragma: no cover |
|
|
_, error = self._get_redis_module_and_error() |
|
|
|
|
|
for retries_left in range(1, -1, -1): # 2 attempts |
|
|
for retries_left in range(1, -1, -1): # 2 attempts |
|
|
try: |
|
|
try: |
|
|
if not self.connected: |
|
|
if not self.connected: |
|
|
self._redis_connect() |
|
|
self._redis_connect() |
|
|
return self.redis.publish(self.channel, self.json.dumps(data)) |
|
|
return self.redis.publish(self.channel, self.json.dumps(data)) |
|
|
except error as exc: |
|
|
except Exception as exc: |
|
|
if retries_left > 0: |
|
|
if retries_left > 0: |
|
|
logger.error( |
|
|
self._get_logger().error( |
|
|
'Cannot publish to redis... retrying', |
|
|
'Cannot publish to redis... retrying', |
|
|
extra={"redis_exception": str(exc)} |
|
|
extra={"redis_exception": str(exc)} |
|
|
) |
|
|
) |
|
|
self.connected = False |
|
|
self.connected = False |
|
|
else: |
|
|
else: |
|
|
logger.error( |
|
|
self._get_logger().error( |
|
|
'Cannot publish to redis... giving up', |
|
|
'Cannot publish to redis... giving up', |
|
|
extra={"redis_exception": str(exc)} |
|
|
extra={"redis_exception": str(exc)} |
|
|
) |
|
|
) |
|
|
break |
|
|
break |
|
|
|
|
|
|
|
|
def _redis_listen_with_retries(self): # pragma: no cover |
|
|
def _redis_listen_with_retries(self): # pragma: no cover |
|
|
_, error = self._get_redis_module_and_error() |
|
|
|
|
|
retry_sleep = 1 |
|
|
retry_sleep = 1 |
|
|
subscribed = False |
|
|
subscribed = False |
|
|
while True: |
|
|
while True: |
|
|
@ -189,10 +184,11 @@ class RedisManager(PubSubManager): |
|
|
self.pubsub.subscribe(self.channel) |
|
|
self.pubsub.subscribe(self.channel) |
|
|
retry_sleep = 1 |
|
|
retry_sleep = 1 |
|
|
yield from self.pubsub.listen() |
|
|
yield from self.pubsub.listen() |
|
|
except error as exc: |
|
|
except Exception as exc: |
|
|
logger.error('Cannot receive from redis... ' |
|
|
self._get_logger().error( |
|
|
f'retrying in {retry_sleep} secs', |
|
|
'Cannot receive from redis... ' |
|
|
extra={"redis_exception": str(exc)}) |
|
|
f'retrying in {retry_sleep} secs', |
|
|
|
|
|
extra={"redis_exception": str(exc)}) |
|
|
subscribed = False |
|
|
subscribed = False |
|
|
time.sleep(retry_sleep) |
|
|
time.sleep(retry_sleep) |
|
|
retry_sleep *= 2 |
|
|
retry_sleep *= 2 |
|
|
|