Problem
Background
The Redis plugin only supports single-node connections today. RedisPluginConnection.swift:196 calls redisConnectWithTimeout(host, Int32(port), …) with a single endpoint, and docs/databases/redis.mdx:102 notes "Cluster mode unsupported". Sentinel is similarly absent.
In production, Redis is rarely a single node. Sentinel-based HA and Cluster sharding are the two standard topologies, and users running either of them currently can't connect TablePro to their cluster, only to one underlying node, with no failover or routing awareness.
Current behavior on a non-single-node deployment
- Pointing the form at a Sentinel port (26379): connection appears to succeed, but every data command fails because Sentinels only accept the
SENTINEL command set. Confusing error messages.
- Pointing the form at one Cluster master: connection succeeds, but reads/writes for any key whose slot belongs to a different master fail with
MOVED <slot> <host:port>. The driver doesn't follow the redirect, so the user sees raw Redis errors with no guidance.
Both failure modes look like bugs rather than missing features.
Proposed solution
Scope (recommended phasing)
These have very different complexity. Sentinel is mostly a connect-time concern; Cluster touches every command path and the key tree UI. Suggest landing as two PRs:
Phase 1: Sentinel mode
- Connection form: list of Sentinel nodes (host:port, repeatable), master name, optional Sentinel auth (separate from data-plane auth, since Redis 6.2+ supports
SENTINEL AUTH).
- Resolution: before opening the data connection, query each reachable Sentinel with
SENTINEL get-master-addr-by-name <master> until one succeeds, then connect to the resolved master through the existing path.
- Failover: on a dropped connection or
READONLY error, re-query Sentinels and reconnect. Cache the last-known address but always re-resolve on failure.
- Optional read-from-replica:
SENTINEL replicas <master> for read scaling. Probably out of scope for the first iteration.
Phase 2: Cluster mode
- Connection form: list of seed nodes (host:port, repeatable). Same auth fields as single-node.
- Topology discovery: on connect, run
CLUSTER SLOTS (or CLUSTER SHARDS on Redis 7+) to learn the slot-to-node map. Refresh on MOVED redirects and on a configurable interval.
- Per-command routing: compute CRC16 slot for the key, route to the owning master. Honor
MOVED (refresh and retry) and ASK (one-shot redirect with ASKING prefix).
- Cross-slot operations:
KEYS, SCAN, DBSIZE, INFO, FLUSHDB, etc. need to fan out across all masters and aggregate. The current sidebar key tree assumes one keyspace, which doesn't hold across shards.
- Multi-key commands:
MGET, DEL of multiple keys, transactions, and Lua scripts that touch multiple slots will need slot-grouping (multi-pass) or just surfacing the cluster error to the user.
Implementation note: hiredis doesn't ship cluster support
The plugin uses hiredis via Plugins/RedisDriverPlugin/CRedis/. Hiredis itself is single-connection only. Two realistic paths:
- Build slot routing in Swift on top of hiredis, holding one
redisContext per master and dispatching by slot. More code, but the plugin already has the C bridge wired up.
- Vendor a separate cluster-aware C library like hiredis-cluster. Smaller code surface here but adds another
.a to Libs/ and another C bridge module.
Sentinel can be implemented purely on top of the existing hiredis without either change, since it's just a different connect-time discovery step.
Affected files
Plugins/RedisDriverPlugin/RedisPluginConnection.swift — connect / reconnect.
Plugins/RedisDriverPlugin/RedisPluginDriver.swift — per-command routing for cluster.
Plugins/RedisDriverPlugin/RedisPlugin.swift — connection schema (mode picker, sentinel/seed node lists).
Plugins/RedisDriverPlugin/RedisCommandParser.swift — may need awareness of which commands are cluster-shardable.
docs/databases/redis.mdx — drop the "Cluster mode unsupported" note, document setup.
CHANGELOG.md — entry under [Unreleased].
Alternatives considered
Open questions
- Connection mode picker: extend the existing connection form with a "Mode" segmented control (Single / Sentinel / Cluster), or add Sentinel/Cluster as separate
DatabaseType entries in the picker? The first preserves one Redis driver entry; the second matches how some clients expose them.
- Key tree under Cluster: aggregate keys from all shards into a single tree (matching the current single-keyspace UX), or show shards as top-level nodes? Aggregation matches user intuition but multiplies SCAN cost.
- Hiredis vs hiredis-cluster: option (1) or (2) above. Probably the maintainer's call based on long-term plugin maintenance preferences.
- Auth model: do we assume the same password works across all nodes (typical), or expose per-node auth?
Happy to send a draft PR for Phase 1 (Sentinel) once direction is confirmed.
Related database type
None
Problem
Background
The Redis plugin only supports single-node connections today.
RedisPluginConnection.swift:196callsredisConnectWithTimeout(host, Int32(port), …)with a single endpoint, anddocs/databases/redis.mdx:102notes "Cluster mode unsupported". Sentinel is similarly absent.In production, Redis is rarely a single node. Sentinel-based HA and Cluster sharding are the two standard topologies, and users running either of them currently can't connect TablePro to their cluster, only to one underlying node, with no failover or routing awareness.
Current behavior on a non-single-node deployment
SENTINELcommand set. Confusing error messages.MOVED <slot> <host:port>. The driver doesn't follow the redirect, so the user sees raw Redis errors with no guidance.Both failure modes look like bugs rather than missing features.
Proposed solution
Scope (recommended phasing)
These have very different complexity. Sentinel is mostly a connect-time concern; Cluster touches every command path and the key tree UI. Suggest landing as two PRs:
Phase 1: Sentinel mode
SENTINEL AUTH).SENTINEL get-master-addr-by-name <master>until one succeeds, then connect to the resolved master through the existing path.READONLYerror, re-query Sentinels and reconnect. Cache the last-known address but always re-resolve on failure.SENTINEL replicas <master>for read scaling. Probably out of scope for the first iteration.Phase 2: Cluster mode
CLUSTER SLOTS(orCLUSTER SHARDSon Redis 7+) to learn the slot-to-node map. Refresh onMOVEDredirects and on a configurable interval.MOVED(refresh and retry) andASK(one-shot redirect withASKINGprefix).KEYS,SCAN,DBSIZE,INFO,FLUSHDB, etc. need to fan out across all masters and aggregate. The current sidebar key tree assumes one keyspace, which doesn't hold across shards.MGET,DELof multiple keys, transactions, and Lua scripts that touch multiple slots will need slot-grouping (multi-pass) or just surfacing the cluster error to the user.Implementation note: hiredis doesn't ship cluster support
The plugin uses hiredis via
Plugins/RedisDriverPlugin/CRedis/. Hiredis itself is single-connection only. Two realistic paths:redisContextper master and dispatching by slot. More code, but the plugin already has the C bridge wired up..atoLibs/and another C bridge module.Sentinel can be implemented purely on top of the existing hiredis without either change, since it's just a different connect-time discovery step.
Affected files
Plugins/RedisDriverPlugin/RedisPluginConnection.swift— connect / reconnect.Plugins/RedisDriverPlugin/RedisPluginDriver.swift— per-command routing for cluster.Plugins/RedisDriverPlugin/RedisPlugin.swift— connection schema (mode picker, sentinel/seed node lists).Plugins/RedisDriverPlugin/RedisCommandParser.swift— may need awareness of which commands are cluster-shardable.docs/databases/redis.mdx— drop the "Cluster mode unsupported" note, document setup.CHANGELOG.md— entry under[Unreleased].Alternatives considered
Open questions
DatabaseTypeentries in the picker? The first preserves one Redis driver entry; the second matches how some clients expose them.Happy to send a draft PR for Phase 1 (Sentinel) once direction is confirmed.
Related database type
None