Redis
The Redis component executes Redis commands and subscribes to Redis channels. One crate covers both directions. The Producer sends the Exchange body to Redis as a command argument. The Consumer subscribes to Pub/Sub channels or blocks on a list key. The redis URI scheme uses plaintext. The rediss URI scheme uses TLS.
The redis-example wires a string producer, a Pub/Sub consumer, a queue consumer, and a Pub/Sub producer against a testcontainers Redis instance:
use camel_builder::{RouteBuilder, StepAccumulator};
use camel_component_redis::RedisComponent;
ctx.register_component("redis", Box::new(RedisComponent::new()));
// Producer: timer writes a key every 3s
let string_producer = RouteBuilder::from("timer:tick?period=3000&repeatCount=3")
.route_id("redis-string-producer")
.set_header("CamelRedis.Key", Value::String("greeting".into()))
.set_header(
"CamelRedis.Value",
Value::String("hello from rust-camel!".into()),
)
.to("redis://127.0.0.1:6379?command=SET")
.to("log:info?showHeaders=true")
.build()?;
YAML equivalent
routes:
- id: redis-string-producer
from: "timer:tick?period=3000&repeatCount=3"
steps:
- set_header:
CamelRedis.Key: "greeting"
- set_header:
CamelRedis.Value: "hello from rust-camel!"
- to: "redis://127.0.0.1:6379?command=SET"
- to: "log:info?showHeaders=true"
The example reads the Redis port from a testcontainers container. Substitute your real broker address in redis://.
// Consumer: BRPOP blocks on a list key, one Exchange per popped item
let queue_consumer = RouteBuilder::from(
"redis://127.0.0.1:6379?command=BRPOP&key=demo-queue&timeout=2",
)
.route_id("redis-queue-consumer")
.to("log:info?showAll=true")
.build()?;
// Consumer: SUBSCRIBE receives published messages as Exchanges
let pubsub_consumer = RouteBuilder::from(
"redis://127.0.0.1:6379?command=SUBSCRIBE&channels=demo-channel",
)
.route_id("redis-pubsub-consumer")
.to("log:info?showAll=true")
.build()?;
YAML equivalent
routes:
- id: redis-queue-consumer
from: "redis://127.0.0.1:6379?command=BRPOP&key=demo-queue&timeout=2"
steps:
- to: "log:info?showAll=true"
- id: redis-pubsub-consumer
from: "redis://127.0.0.1:6379?command=SUBSCRIBE&channels=demo-channel"
steps:
- to: "log:info?showAll=true"
URI
redis://host:port?command=<cmd>[&key=<key>][&channels=<list>][&timeout=<secs>][&password=<pwd>][&db=<n>][&ssl=<bool>]
| Parameter | Required | Default | Description |
|---|---|---|---|
command | no | SET | Redis command to execute |
key | per-command | none | Redis key for the operation |
channels | Pub/Sub | empty | Comma-separated channel names |
timeout | blocking | 1 | Blocking timeout in seconds |
password | no | none | Redis password |
db | no | 0 | Redis database number (0-255) |
ssl | no | auto | Force TLS on or off |
The command parameter picks the Redis command at Endpoint creation. Exchange data never becomes a command name. Dynamic values like keys, fields, values, channels, and scores cross the trust boundary as length-prefixed Redis protocol arguments. Argument contents cannot inject a second command or change the selected command (CONTEXT "Trust boundary"). Missing required headers return CamelError.
Commands
The component exposes 80+ commands across eight groups. The enum is exhaustive: an unknown command fails URI parsing with CamelError::InvalidUri. The component does not expose EVAL, EVALSHA, or script-loading commands. Script injection through the public surface is not possible.
| Group | Commands |
|---|---|
| String | SET, GET, GETSET, SETNX, SETEX, MGET, MSET, INCR, INCRBY, DECR, DECRBY, APPEND, STRLEN |
| Key | EXISTS, DEL, EXPIRE, EXPIREAT, PEXPIRE, PEXPIREAT, TTL, KEYS, RENAME, RENAMENX, TYPE, PERSIST, MOVE, SORT |
| List | LPUSH, RPUSH, LPUSHX, RPUSHX, LPOP, RPOP, BLPOP, BRPOP, LLEN, LRANGE, LINDEX, LINSERT, LSET, LREM, LTRIM, RPOPLPUSH |
| Hash | HSET, HGET, HSETNX, HMSET, HMGET, HDEL, HEXISTS, HLEN, HKEYS, HVALS, HGETALL, HINCRBY |
| Set | SADD, SREM, SMEMBERS, SCARD, SISMEMBER, SPOP, SMOVE, SINTER, SUNION, SDIFF, SINTERSTORE, SUNIONSTORE, SDIFFSTORE, SRANDMEMBER |
| Sorted set | ZADD, ZREM, ZRANGE, ZREVRANGE, ZRANK, ZREVRANK, ZSCORE, ZCARD, ZINCRBY, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE |
| Pub/Sub | PUBLISH, SUBSCRIBE, PSUBSCRIBE |
| Other | PING, ECHO |
Producer
redis://host:port?command=GET sends the Exchange body to Redis. The Producer holds a single multiplexed connection per Endpoint. The connection opens lazily on the first call and stays open for the route lifetime.
The command parameter picks one of the 80+ commands listed above. The Exchange body, headers, and the URI parameters supply the command arguments. Different commands read different headers. For example, HSET reads CamelRedis.Key and CamelRedis.Value. LRANGE reads CamelRedis.Start and CamelRedis.End. Missing required headers return CamelError. A send failure returns Err to the route ErrorHandler.
The Producer is a Tower Service<Exchange>. It composes with any pipeline step and reports per-route metrics.
Consumer
redis://host:port?command=SUBSCRIBE&channels=foo,bar subscribes to one or more Pub/Sub channels. The Consumer submits one Exchange per published message. The CamelRedis.Channel header carries the channel name. CamelRedis.Pattern carries the matched pattern for PSUBSCRIBE.
redis://host:port?command=BLPOP&key=jobs&timeout=5 blocks on a list key and submits one Exchange per popped item. The CamelRedis.Key header carries the list key. The timeout parameter is the block duration in seconds. Use BLPOP for left pop and BRPOP for right pop.
The Consumer's mode comes from the URI command. SUBSCRIBE and PSUBSCRIBE use Pub/Sub mode. BLPOP and BRPOP use queue mode. A command that fits neither returns an error at consumer creation. The component does not silently fall back to BLPOP (REDIS-003).
Security
The component supports TLS through the rediss:// URI scheme or the ssl=true parameter. The two are equivalent. TLS auto-enables for non-loopback hosts when the redis crate is compiled with a TLS feature (tls-rustls-webpki-roots, tls-rustls-native-certs, or tls-native-tls). The component logs a tracing::warn! when auto-enabling TLS. A missing feature gate stops startup with the required cargo add command.
The component redacts passwords in Debug output. Passwords with special characters (@, :, /) are percent-encoded in the connection URL. The safe_endpoint() helper returns a credential-free identifier for tracing.
Connection handling
The Producer holds a single multiplexed connection per Endpoint. It opens lazily on the first call, and a reconnect drops the cached connection and re-resolves the master through the topology — that re-resolution is where a sentinel failover is picked up. The producer does not re-resolve on every command.
Each Consumer mode — Pub/Sub and queue — holds ONE persistent connection per session. Every message or popped item is delivered over that same connection; the consumer does not reconnect between messages. A blocking-pop timeout (BLPOP/BRPOP returning nil) keeps the connection. The consumer reconnects only when the Pub/Sub stream ends or a transient transport error strikes, and a Pub/Sub reconnect replays all subscriptions. Reconnects are bounded by the configured NetworkRetryPolicy: when the budget is exhausted the consumer returns an error and Route supervision restarts the Route (ADR-0007).
Connections have a 10-second connect timeout by default (connection_timeout_secs in the config block). The route ErrorHandler owns the operational signal for non-transient errors.
The component registers an async health check that sends a PING command. The probe is healthy when Redis responds with PONG and degraded when PING fails or times out.
Sentinel / failover
Redis Sentinel gives Redis high availability. Sentinel nodes monitor a master and its replicas. When the master fails, the sentinels elect a replica and promote it to master. Clients must re-discover the new master to keep working.
The component connects to a Sentinel topology with the redis-sentinel:// scheme:
redis-sentinel://sentinel-a:26379,sentinel-b:26379/<master-name>/<db>?command=<cmd>[&key=<key>][&channels=<list>]
The authority holds the comma-separated sentinel node list. The first path segment is the master group name. The second path segment is the optional database number. It defaults to 0. The rediss-sentinel:// scheme is the TLS variant. It enables TLS on the sentinel and the resolved master connections.
The runtime resolves a route URI by scheme. Register RedisSentinelComponent for redis-sentinel:// routes and RedissSentinelComponent for rediss-sentinel:// routes. Register them next to RedisComponent:
ctx.register_component(RedisComponent::new());
ctx.register_component(RedisSentinelComponent::new());
// Only for rediss-sentinel:// (TLS) routes:
ctx.register_component(RedissSentinelComponent::new());
RedisBundle::register_all registers all three schemes from the [components.redis] block.
You can also select Sentinel with the [components.redis.sentinel] config block:
[components.redis.sentinel]
nodes = ["redis://sentinel-a:26379", "redis://sentinel-b:26379"]
master_name = "mymaster"
# Optional sentinel credentials.
# username = "sentinel-user"
# password = "sentinel-pass"
nodes holds the sentinel node URLs. master_name is the master group name. The optional username and password authenticate the sentinel connections. The node password stays in the top-level [components.redis] block. The two credential sets are separate.
// Producer: timer writes a key every 3s through Sentinel.
let producer = RouteBuilder::from("timer:tick?period=3000&repeatCount=3")
.route_id("redis-sentinel-producer")
.set_header("CamelRedis.Key", Value::String("greeting".into()))
.set_header(
"CamelRedis.Value",
Value::String("hello via redis sentinel!".into()),
)
.to("redis-sentinel://127.0.0.1:26379/mymaster/0?command=SET")
.to("log:info?showHeaders=true")
.build()?;
Failover behavior
On a transport error, the producer and consumer reconnect loops re-resolve the current master through the sentinel nodes. The resolved master is never cached. Every reconnect asks the sentinels again. This is bounded transport reconnect, not consumer self-supervision. The retry budget comes from NetworkRetryPolicy. When the budget runs out, the consumer returns Err and Route supervision takes over (ADR-0007).
The health check also re-resolves the master through the sentinel nodes on each check. After a failover, the check reports the new master, not a cached address.
Best-effort Pub/Sub
Pub/Sub delivery is best-effort. When a sentinel-triggered stream ends, the consumer re-subscribes to the new master. Messages published during the failover gap are lost. Duplicates are possible on reconnect. Do not use Pub/Sub for workloads that need durability.
Feature flag
The sentinel cargo feature on camel-component-redis enables Sentinel topology construction. Enable it in your manifest:
camel-component-redis = { workspace = true, features = ["sentinel"] }
Without the feature, the component recognizes redis-sentinel:// and a non-empty [components.redis.sentinel] block and rejects them at startup with a clear error. It fails closed. It does not fall back to a standalone connection.
Error handling
The Consumer logs at error! for channel-closed conditions on Pub/Sub and BLPOP send paths and for retry-exhaustion. Each site reports a typed metric before the log line. The Producer logs send failures at warn!. Per-message non-transient Redis errors log at error! with a typed metric. The route handler owns the operational signal for transient producer errors.
Reference: Redis crate CONTEXT. Example sources: examples/redis-example, examples/redis-sentinel.