awx/awx/main/tests/unit/utils/test_redis.py
Lila Yasin 4f41b50a09
AAP-57817 Add Redis connection retry using redis-py 7.0+ built-in (#16176)
* AAP-57817 Add Redis connection retry using redis-py 7.0+ built-in mechanism

* Refactor Redis client helpers to use settings and eliminate code duplication

* Create awx/main/utils/redis.py and move Redis client functions to avoid circular imports

* Fix subsystem_metrics to share Redis connection pool between
  client and pipeline

* Cache Redis clients in RelayConsumer and RelayWebsocketStatsManager to avoid creating new connection pools on every call

* Add cap and base config

* Add Redis retry logic with exponential backoff to handle connection failures during long-running operations

* Add REDIS_BACKOFF_CAP and REDIS_BACKOFF_BASE settings to allow
  adjustment of retry timing in worst-case scenarios without code changes

* Simplify Redis retry tests by removing unnecessary reload logic
2025-12-01 09:08:47 -05:00

62 lines
2.6 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2025 Ansible, Inc.
# All Rights Reserved
from django.test.utils import override_settings
from awx.main.utils.redis import get_redis_client, get_redis_client_async
from redis.exceptions import BusyLoadingError, ConnectionError, TimeoutError
from redis.backoff import ExponentialBackoff
class TestRedisRetryConfiguration:
"""Verify Redis retry configuration is applied to connection objects."""
def test_retry_configuration_applied_to_client(self, settings):
"""Verify all retry settings are applied to the connection pool."""
# Test sync client
client = get_redis_client()
retry = client.connection_pool.connection_kwargs['retry']
backoff = retry._backoff
retry_errors = client.connection_pool.connection_kwargs['retry_on_error']
# Assert provided values match values on the object
assert retry._retries == settings.REDIS_RETRY_COUNT == 3
assert isinstance(backoff, ExponentialBackoff)
assert backoff._base == settings.REDIS_BACKOFF_BASE == 0.5
assert backoff._cap == settings.REDIS_BACKOFF_CAP == 1.0
assert BusyLoadingError in retry_errors
assert ConnectionError in retry_errors
assert TimeoutError in retry_errors
# Test async client has same config
client_async = get_redis_client_async()
retry_async = client_async.connection_pool.connection_kwargs['retry']
backoff_async = retry_async._backoff
retry_errors_async = client_async.connection_pool.connection_kwargs['retry_on_error']
assert retry_async._retries == settings.REDIS_RETRY_COUNT
assert backoff_async._base == settings.REDIS_BACKOFF_BASE
assert backoff_async._cap == settings.REDIS_BACKOFF_CAP
assert ConnectionError in retry_errors_async
@override_settings(REDIS_RETRY_COUNT=5)
def test_override_settings_applied_to_client(self):
"""Verify override_settings changes are applied to client object."""
client = get_redis_client()
retry = client.connection_pool.connection_kwargs['retry']
assert retry._retries == 5
@override_settings(REDIS_BACKOFF_CAP=2.0, REDIS_BACKOFF_BASE=1.0)
def test_override_backoff_settings_applied_to_client(self):
"""Verify override_settings for backoff parameters are applied to client object."""
client = get_redis_client()
retry = client.connection_pool.connection_kwargs['retry']
backoff = retry._backoff
# Assert provided values match values on object
assert backoff._cap == 2.0
assert backoff._base == 1.0