dify - ✅(Solved) Fix [Refactor/Chore] Replace URL-string Redis config contract with a structured RedisConnectionSpec [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langgenius/dify#35516Fetched 2026-04-24 06:14:20
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
1
Author
Participants
Assignees
Timeline (top)
assigned ×1cross-referenced ×1labeled ×1unlabeled ×1

Today both the main Redis client and the Event Bus (pub/sub) client are wired via a URL string (PUBSUB_REDIS_URL_build_default_pubsub_urlnormalized_pubsub_redis_url). A URL cannot encode Sentinel topology, which needs a list of sentinel nodes plus a service name plus sentinel-level credentials. The consequence is that pub/sub cannot participate in Sentinel HA under any configuration — it either silently mis-routes to REDIS_HOST (which Helm renders to the Sentinel service address) or pins to a single master with no failover. Cluster deployments hit a hard ValueError at startup.

Propose replacing the URL contract with a frozen RedisConnectionSpec dataclass that both the main client and the Event Bus client consume. Pub/sub defaults to inheriting the main spec — zero-config for the common case, reuses the main client object and its failover-aware Sentinel handle. An explicit PUBSUB_REDIS_MODE=sentinel|cluster|standalone unlocks independent topologies, including an independent Sentinel for pub/sub that the URL contract could never express.

Root Cause

Implementation in #35515 (draft). A minimal-invasion alternative for the same root cause exists in #35483 — the two approaches are mutually exclusive; maintainers can choose the balance of risk vs. architectural cleanup they prefer.

Fix Action

Fix / Workaround

  • Fixes #35480 at the contract level rather than patching individual code paths.
  • Unlocks a capability the URL contract fundamentally cannot provide: pub/sub with its own Sentinel topology.
  • Removes the silent-degradation trap in the legacy PUBSUB_REDIS_URL + PUBSUB_REDIS_USE_CLUSTERS combination (pointing PUBSUB_REDIS_URL at a cluster node without the flag yields a single-node client that crashes on MOVED for any keys not on that shard).
  • Consolidates three parallel parameter dicts (RedisBaseParamsDict / RedisHealthParamsDict / RedisClusterHealthParamsDict) into a single transport-params contract in ext_redis.py.
  • Adds a startup guard for CELERY_BROKER_URL=redis+cluster:// (Kombu does not support Redis Cluster).

PR fix notes

PR #35515: refactor(redis): unify main + pub/sub client construction via RedisConnectionSpec

Description (problem / solution / changelog)

Fixes #35480 Closes #35516

Summary

Replaces the URL-string Redis config contract with a structured RedisConnectionSpec dataclass so the main client and the Event Bus (pub/sub) client share one construction path across all three topologies. Addresses #35480 at the contract level; see #35483 for the minimal-invasion alternative for the same root cause.

Why the URL contract was the wrong abstraction

A URL cannot encode Sentinel topology (list of sentinel nodes + service name + sentinel credentials). Under Sentinel, pub/sub silently mis-routed to REDIS_HOST; under Cluster, it hit a hard ValueError at startup. Pub/sub could not participate in Sentinel HA under any configuration.

What this PR does

  • New RedisConnectionSpec (api/configs/middleware/cache/redis_connection_spec.py) — frozen dataclass covering all three topologies. Masks passwords in __repr__; hashable so pubsub_spec == main_spec comparison drives the "reuse main client" decision.
  • build_main_redis_spec / build_pubsub_spec factories read env into specs; cross-validate invalid combinations (e.g. REDIS_USE_SENTINEL + REDIS_USE_CLUSTERS) at the config layer.
  • ext_redis._create_*_client now take (spec, transport) — topology from the spec, tuning knobs from a consolidated RedisTransportParamsDict (replaces three parallel dicts).
  • ext_redis.init_app reuses the main client object when pubsub_spec == main_spec (the default "inherit" path), otherwise builds a dedicated client via the same factory.
  • ext_celery._reject_cluster_broker_url — startup guard that fails fast when CELERY_BROKER_URL / CELERY_RESULT_BACKEND points at redis+cluster:// (Kombu limitation).
  • Hardening: malformed entries in REDIS_SENTINELS / REDIS_CLUSTERS / PUBSUB_REDIS_SENTINELS / PUBSUB_REDIS_CLUSTERS report the 1-based position (never the raw content), so a password accidentally pasted into these env vars does not leak into startup logs.

Unlocked capabilities

  • Pub/sub under Sentinel HA — works by default via main-client reuse, or via PUBSUB_REDIS_MODE=sentinel for an independent Sentinel.
  • Pub/sub under Cluster — works by default via main-client reuse.
  • Startup rejection for invalid combinations, surfaced with clear, env-var-named error messages.

⚠️ Breaking change

PUBSUB_REDIS_URL / EVENT_BUS_REDIS_URL and PUBSUB_REDIS_USE_CLUSTERS / EVENT_BUS_REDIS_USE_CLUSTERS are removed. All three deployment templates shipped these with empty defaults, so most deployments will silently fall back to the inherit path and start working correctly under Sentinel for the first time. Deployments that genuinely want an independent pub/sub Redis should migrate to PUBSUB_REDIS_MODE plus the structured PUBSUB_REDIS_* fields (which, unlike the URL, can express independent Sentinel topologies).

Tests

51 new unit tests across four files covering spec construction, builder cross-validation, password-redaction in __repr__, inheritance semantics, independent Sentinel pub/sub, and the Celery Cluster guard. Full local run: 11723 passed, 4 skipped, 0 failed.

Screenshots

N/A (backend / configuration layer change).

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues.
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and cd web && pnpm exec vp staged (frontend) to appease the lint gods.

Changed files

  • api/.env.example (modified, +31/-8)
  • api/configs/middleware/cache/redis_connection_spec.py (added, +386/-0)
  • api/configs/middleware/cache/redis_pubsub_config.py (modified, +123/-60)
  • api/extensions/ext_celery.py (modified, +36/-1)
  • api/extensions/ext_redis.py (modified, +148/-157)
  • api/tests/unit_tests/configs/middleware/cache/test_build_main_redis_spec.py (added, +263/-0)
  • api/tests/unit_tests/configs/middleware/cache/test_build_pubsub_spec.py (added, +210/-0)
  • api/tests/unit_tests/configs/middleware/cache/test_redis_connection_spec.py (added, +236/-0)
  • api/tests/unit_tests/configs/test_dify_config.py (modified, +113/-10)
  • api/tests/unit_tests/extensions/test_celery_broker_validation.py (added, +89/-0)
  • api/tests/unit_tests/extensions/test_redis.py (modified, +19/-43)
  • docker/.env.example (modified, +31/-8)
  • docker/docker-compose.yaml (modified, +13/-2)
RAW_BUFFERClick to expand / collapse

Self Checks

  • I have read the Contributing Guide and Language Policy.
  • This is only for refactors or chores; if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report, otherwise it will be closed.
  • 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
  • Please do not modify this template :) and fill in all the required fields.

Description

Today both the main Redis client and the Event Bus (pub/sub) client are wired via a URL string (PUBSUB_REDIS_URL_build_default_pubsub_urlnormalized_pubsub_redis_url). A URL cannot encode Sentinel topology, which needs a list of sentinel nodes plus a service name plus sentinel-level credentials. The consequence is that pub/sub cannot participate in Sentinel HA under any configuration — it either silently mis-routes to REDIS_HOST (which Helm renders to the Sentinel service address) or pins to a single master with no failover. Cluster deployments hit a hard ValueError at startup.

Propose replacing the URL contract with a frozen RedisConnectionSpec dataclass that both the main client and the Event Bus client consume. Pub/sub defaults to inheriting the main spec — zero-config for the common case, reuses the main client object and its failover-aware Sentinel handle. An explicit PUBSUB_REDIS_MODE=sentinel|cluster|standalone unlocks independent topologies, including an independent Sentinel for pub/sub that the URL contract could never express.

Motivation

  • Fixes #35480 at the contract level rather than patching individual code paths.
  • Unlocks a capability the URL contract fundamentally cannot provide: pub/sub with its own Sentinel topology.
  • Removes the silent-degradation trap in the legacy PUBSUB_REDIS_URL + PUBSUB_REDIS_USE_CLUSTERS combination (pointing PUBSUB_REDIS_URL at a cluster node without the flag yields a single-node client that crashes on MOVED for any keys not on that shard).
  • Consolidates three parallel parameter dicts (RedisBaseParamsDict / RedisHealthParamsDict / RedisClusterHealthParamsDict) into a single transport-params contract in ext_redis.py.
  • Adds a startup guard for CELERY_BROKER_URL=redis+cluster:// (Kombu does not support Redis Cluster).

Additional Context

Implementation in #35515 (draft). A minimal-invasion alternative for the same root cause exists in #35483 — the two approaches are mutually exclusive; maintainers can choose the balance of risk vs. architectural cleanup they prefer.

extent analysis

TL;DR

Replace the URL contract with a RedisConnectionSpec dataclass to enable pub/sub participation in Sentinel HA.

Guidance

  • Identify the current usage of PUBSUB_REDIS_URL and PUBSUB_REDIS_USE_CLUSTERS in the codebase to understand the impact of the proposed change.
  • Consider the trade-offs between the two proposed approaches: the more invasive implementation in #35515 and the minimal-invasion alternative in #35483.
  • Evaluate the benefits of consolidating the three parallel parameter dicts into a single transport-params contract in ext_redis.py.
  • Review the startup guard for CELERY_BROKER_URL=redis+cluster:// to ensure compatibility with Kombu's limitations.

Example

No code snippet is provided as the issue does not contain specific code that needs to be modified.

Notes

The proposed change requires careful consideration of the existing codebase and the trade-offs between different implementation approaches. The issue highlights the limitations of the current URL contract and the need for a more robust solution.

Recommendation

Apply the workaround by replacing the URL contract with a RedisConnectionSpec dataclass, as it provides a more robust and flexible solution for managing Redis connections and enables pub/sub participation in Sentinel HA.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

dify - ✅(Solved) Fix [Refactor/Chore] Replace URL-string Redis config contract with a structured RedisConnectionSpec [1 pull requests, 1 participants]