From 808862a55f618e52a1164719dd0fdf7512c29773 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 24 Oct 2024 15:29:46 +0800 Subject: [PATCH 01/10] ext_redis.py support redis cluster Signed-off-by: root --- api/configs/middleware/cache/redis_config.py | 15 +++++++++++++++ api/extensions/ext_redis.py | 9 ++++++++- docker/docker-compose.yaml | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/api/configs/middleware/cache/redis_config.py b/api/configs/middleware/cache/redis_config.py index 26b9b1347c..8e19b8c95e 100644 --- a/api/configs/middleware/cache/redis_config.py +++ b/api/configs/middleware/cache/redis_config.py @@ -68,3 +68,18 @@ class RedisConfig(BaseSettings): description="Socket timeout in seconds for Redis Sentinel connections", default=0.1, ) + + REDIS_USE_CLUSTERS: Optional[bool] = Field( + description="Enable Redis Clusters mode for high availability", + default=False, + ) + + REDIS_CLUSTERS: Optional[str] = Field( + description="Comma-separated list of Redis Clusters nodes (host:port)", + default=None, + ) + + REDIS_CLUSTERS_PASSWORD: Optional[str] = Field( + description="Password for Redis Clusters authentication (if required)", + default=None, + ) diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py index e1f8409f21..d79bdf8b2f 100644 --- a/api/extensions/ext_redis.py +++ b/api/extensions/ext_redis.py @@ -1,11 +1,12 @@ import redis from redis.connection import Connection, SSLConnection from redis.sentinel import Sentinel +from redis.cluster import RedisCluster, ClusterNode from configs import dify_config -class RedisClientWrapper(redis.Redis): +class RedisClientWrapper(): """ A wrapper class for the Redis client that addresses the issue where the global `redis_client` variable cannot be updated when a new Redis instance is returned @@ -71,6 +72,12 @@ def init_app(app): ) master = sentinel.master_for(dify_config.REDIS_SENTINEL_SERVICE_NAME, **redis_params) redis_client.initialize(master) + elif dify_config.REDIS_USE_CLUSTERS: + startup_nodes = [ + {"host": node.split(":")[0], "port": int(node.split(":")[1])} for node in dify_config.REDIS_CLUSTERS.split(",") + ] + nodes = [ClusterNode(host=node["host"], port=node["port"]) for node in startup_nodes] + redis_client.initialize(RedisCluster(startup_nodes=nodes,password=dify_config.REDIS_CLUSTERS_PASSWORD)) else: redis_params.update( { diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index da1a6b4d4e..86dc866773 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -51,6 +51,9 @@ x-shared-env: &shared-api-worker-env REDIS_SENTINEL_PASSWORD: ${REDIS_SENTINEL_PASSWORD:-} ACCESS_TOKEN_EXPIRE_MINUTES: ${ACCESS_TOKEN_EXPIRE_MINUTES:-60} REDIS_SENTINEL_SOCKET_TIMEOUT: ${REDIS_SENTINEL_SOCKET_TIMEOUT:-0.1} + REDIS_CLUSTERS: ${REDIS_CLUSTERS:-} + REDIS_USE_CLUSTERS: ${REDIS_USE_CLUSTERS:-false} + REDIS_CLUSTERS_PASSWORD: ${REDIS_CLUSTERS_PASSWORD:-} CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://:difyai123456@redis:6379/1} BROKER_USE_SSL: ${BROKER_USE_SSL:-false} CELERY_USE_SENTINEL: ${CELERY_USE_SENTINEL:-false} From 198f2be1a53012a391216740c314a7412f976961 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 24 Oct 2024 20:13:02 +0800 Subject: [PATCH 02/10] Fix the issues with #9538 Signed-off-by: root --- api/extensions/ext_redis.py | 5 +++-- api/tests/unit_tests/core/test_model_manager.py | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py index d79bdf8b2f..410ebaf4bf 100644 --- a/api/extensions/ext_redis.py +++ b/api/extensions/ext_redis.py @@ -74,10 +74,11 @@ def init_app(app): redis_client.initialize(master) elif dify_config.REDIS_USE_CLUSTERS: startup_nodes = [ - {"host": node.split(":")[0], "port": int(node.split(":")[1])} for node in dify_config.REDIS_CLUSTERS.split(",") + {"host": node.split(":")[0], "port": int(node.split(":")[1])} + for node in dify_config.REDIS_CLUSTERS.split(",") ] nodes = [ClusterNode(host=node["host"], port=node["port"]) for node in startup_nodes] - redis_client.initialize(RedisCluster(startup_nodes=nodes,password=dify_config.REDIS_CLUSTERS_PASSWORD)) + redis_client.initialize(RedisCluster(startup_nodes=nodes, password=dify_config.REDIS_CLUSTERS_PASSWORD)) else: redis_params.update( { diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index 2808b5b0fa..b311baf42c 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -1,6 +1,8 @@ from unittest.mock import MagicMock import pytest +import redis +from api.extensions.ext_redis import RedisClientWrapper from core.entities.provider_entities import ModelLoadBalancingConfiguration from core.model_manager import LBModelManager @@ -55,6 +57,10 @@ def test_lb_model_manager_fetch_next(mocker, lb_model_manager): start_index += 1 return start_index + fake_redis_client = redis.Redis(host="localhost", port=6379, db=0) + redis_client = RedisClientWrapper() + redis_client.initialize(fake_redis_client) + mocker.patch("redis.Redis.incr", side_effect=incr) mocker.patch("redis.Redis.set", return_value=None) mocker.patch("redis.Redis.expire", return_value=None) From d66ae4ae126496807b47be6cfae6e3fee123d491 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 28 Oct 2024 08:54:23 +0800 Subject: [PATCH 03/10] Fix2 the issues with #9538 Signed-off-by: root --- api/extensions/ext_redis.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py index 410ebaf4bf..1e0ad59689 100644 --- a/api/extensions/ext_redis.py +++ b/api/extensions/ext_redis.py @@ -73,11 +73,10 @@ def init_app(app): master = sentinel.master_for(dify_config.REDIS_SENTINEL_SERVICE_NAME, **redis_params) redis_client.initialize(master) elif dify_config.REDIS_USE_CLUSTERS: - startup_nodes = [ - {"host": node.split(":")[0], "port": int(node.split(":")[1])} + nodes = [ + ClusterNode(host=node.split(":")[0], port=int(node.split.split(":")[1])) for node in dify_config.REDIS_CLUSTERS.split(",") ] - nodes = [ClusterNode(host=node["host"], port=node["port"]) for node in startup_nodes] redis_client.initialize(RedisCluster(startup_nodes=nodes, password=dify_config.REDIS_CLUSTERS_PASSWORD)) else: redis_params.update( From ec5229f79c623d97d14bee0eed396bee81dbd59a Mon Sep 17 00:00:00 2001 From: liuhaoran <75237518+liuhaoran1212@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:18:56 +0800 Subject: [PATCH 04/10] Update api/configs/middleware/cache/redis_config.py Okay Co-authored-by: Bowen Liang --- api/configs/middleware/cache/redis_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/configs/middleware/cache/redis_config.py b/api/configs/middleware/cache/redis_config.py index 8e19b8c95e..2e98c31ec3 100644 --- a/api/configs/middleware/cache/redis_config.py +++ b/api/configs/middleware/cache/redis_config.py @@ -69,7 +69,7 @@ class RedisConfig(BaseSettings): default=0.1, ) - REDIS_USE_CLUSTERS: Optional[bool] = Field( + REDIS_USE_CLUSTERS: bool = Field( description="Enable Redis Clusters mode for high availability", default=False, ) From ac817303ce0446ed580b9db79bc00f05e35f983c Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Oct 2024 17:21:08 +0800 Subject: [PATCH 05/10] Fix3 the issues with langgenius#9538 Signed-off-by: root --- api/tests/unit_tests/core/test_model_manager.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index b311baf42c..1f2f258fe6 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -39,6 +39,11 @@ def lb_model_manager(): return lb_model_manager +class FakeRedisClient: + def __init__(self, host='localhost', port=6379, db=0): + self.client = redis.Redis(host=host, port=port, db=db) + + def test_lb_model_manager_fetch_next(mocker, lb_model_manager): assert len(lb_model_manager._load_balancing_configs) == 3 @@ -57,7 +62,7 @@ def test_lb_model_manager_fetch_next(mocker, lb_model_manager): start_index += 1 return start_index - fake_redis_client = redis.Redis(host="localhost", port=6379, db=0) + fake_redis_client = FakeRedisClient() redis_client = RedisClientWrapper() redis_client.initialize(fake_redis_client) From c6d4e50e3fd706523219842d8504d88da92478c6 Mon Sep 17 00:00:00 2001 From: liuhaoran <75237518+liuhaoran1212@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:17:58 +0800 Subject: [PATCH 06/10] fix test_model_manager.py --- api/tests/unit_tests/core/test_model_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index 1f2f258fe6..96176bb6c4 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -2,7 +2,7 @@ from unittest.mock import MagicMock import pytest import redis -from api.extensions.ext_redis import RedisClientWrapper +from extensions.ext_redis import RedisClientWrapper from core.entities.provider_entities import ModelLoadBalancingConfiguration from core.model_manager import LBModelManager From 6327d72bc84d95f92da510c2c1ea81068ed072f1 Mon Sep 17 00:00:00 2001 From: liuhaoran <75237518+liuhaoran1212@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:42:39 +0800 Subject: [PATCH 07/10] fix ext_redis.py --- api/extensions/ext_redis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/extensions/ext_redis.py b/api/extensions/ext_redis.py index 1e0ad59689..36f06c1104 100644 --- a/api/extensions/ext_redis.py +++ b/api/extensions/ext_redis.py @@ -1,12 +1,12 @@ import redis +from redis.cluster import ClusterNode, RedisCluster from redis.connection import Connection, SSLConnection from redis.sentinel import Sentinel -from redis.cluster import RedisCluster, ClusterNode from configs import dify_config -class RedisClientWrapper(): +class RedisClientWrapper: """ A wrapper class for the Redis client that addresses the issue where the global `redis_client` variable cannot be updated when a new Redis instance is returned From ec0c4a25fc64dc7ddd6880b8ab523c17c361b571 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 11 Nov 2024 18:23:10 +0800 Subject: [PATCH 08/10] fixed test_model_manager.py Signed-off-by: root --- api/extensions/ext_celery.py | 4 +++ .../unit_tests/core/test_model_manager.py | 33 ++++++++----------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/api/extensions/ext_celery.py b/api/extensions/ext_celery.py index 504899c276..52e14cc49b 100644 --- a/api/extensions/ext_celery.py +++ b/api/extensions/ext_celery.py @@ -43,6 +43,10 @@ def init_app(app: Flask) -> Celery: result_backend=dify_config.CELERY_RESULT_BACKEND, broker_transport_options=broker_transport_options, broker_connection_retry_on_startup=True, + worker_log_format=dify_config.LOG_FORMAT, + worker_task_log_format=dify_config.LOG_FORMAT, + worker_logfile=dify_config.LOG_FILE, + worker_hijack_root_logger=False, ) if dify_config.BROKER_USE_SSL: diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index 96176bb6c4..e37533e520 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -1,9 +1,9 @@ -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest import redis -from extensions.ext_redis import RedisClientWrapper +from extensions.ext_redis import redis_client from core.entities.provider_entities import ModelLoadBalancingConfiguration from core.model_manager import LBModelManager from core.model_runtime.entities.model_entities import ModelType @@ -39,12 +39,10 @@ def lb_model_manager(): return lb_model_manager -class FakeRedisClient: - def __init__(self, host='localhost', port=6379, db=0): - self.client = redis.Redis(host=host, port=port, db=db) - - def test_lb_model_manager_fetch_next(mocker, lb_model_manager): + # initialize redis client + redis_client.initialize(redis.Redis()) + assert len(lb_model_manager._load_balancing_configs) == 3 config1 = lb_model_manager._load_balancing_configs[0] @@ -62,16 +60,13 @@ def test_lb_model_manager_fetch_next(mocker, lb_model_manager): start_index += 1 return start_index - fake_redis_client = FakeRedisClient() - redis_client = RedisClientWrapper() - redis_client.initialize(fake_redis_client) - - mocker.patch("redis.Redis.incr", side_effect=incr) - mocker.patch("redis.Redis.set", return_value=None) - mocker.patch("redis.Redis.expire", return_value=None) + with ( + patch.object(redis_client, "incr", side_effect=incr), + patch.object(redis_client, "set", return_value=None), + patch.object(redis_client, "expire", return_value=None), + ): + config = lb_model_manager.fetch_next() + assert config == config2 - config = lb_model_manager.fetch_next() - assert config == config2 - - config = lb_model_manager.fetch_next() - assert config == config3 + config = lb_model_manager.fetch_next() + assert config == config3 From de3c43c924bb757a09cb97dc1ce8a62a0010d015 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 11 Nov 2024 18:26:42 +0800 Subject: [PATCH 09/10] remove ext_celery.py Signed-off-by: root --- api/extensions/ext_celery.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/extensions/ext_celery.py b/api/extensions/ext_celery.py index 52e14cc49b..504899c276 100644 --- a/api/extensions/ext_celery.py +++ b/api/extensions/ext_celery.py @@ -43,10 +43,6 @@ def init_app(app: Flask) -> Celery: result_backend=dify_config.CELERY_RESULT_BACKEND, broker_transport_options=broker_transport_options, broker_connection_retry_on_startup=True, - worker_log_format=dify_config.LOG_FORMAT, - worker_task_log_format=dify_config.LOG_FORMAT, - worker_logfile=dify_config.LOG_FILE, - worker_hijack_root_logger=False, ) if dify_config.BROKER_USE_SSL: From 0a53be946d2c32b0d213a08106957d7397e5c137 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 12 Nov 2024 09:30:25 +0800 Subject: [PATCH 10/10] fix test_model_manager.py import Signed-off-by: root --- api/tests/unit_tests/core/test_model_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/tests/unit_tests/core/test_model_manager.py b/api/tests/unit_tests/core/test_model_manager.py index e37533e520..d98e9f6bad 100644 --- a/api/tests/unit_tests/core/test_model_manager.py +++ b/api/tests/unit_tests/core/test_model_manager.py @@ -3,10 +3,10 @@ from unittest.mock import MagicMock, patch import pytest import redis -from extensions.ext_redis import redis_client from core.entities.provider_entities import ModelLoadBalancingConfiguration from core.model_manager import LBModelManager from core.model_runtime.entities.model_entities import ModelType +from extensions.ext_redis import redis_client @pytest.fixture