Redis transport
com.babelqueue:babelqueue-redis is a Redis transport on the Java core, built on the
Lettuce client. It sends the
canonical envelope as a Redis list element with the
§1 reliable-queue pattern, and consumes by routing each
message to a handler by URN — so a message it produces is consumed by any other BabelQueue SDK,
and vice-versa.
Redis lists carry no native metadata, so — unlike the SQS, RabbitMQ or Kafka bindings — there is no property projection. The list element is the canonical envelope JSON, byte-for-byte, with no wrapping and no added fields; routing and tracing read the body directly.
Install
Maven:
<dependency>
<groupId>com.babelqueue</groupId>
<artifactId>babelqueue-redis</artifactId>
<version>1.0.0</version>
</dependency>
Requirements: Java 17+. It pulls babelqueue-core and io.lettuce:lettuce-core transitively.
Produce
import com.babelqueue.redis.RedisPublisher;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.sync.RedisCommands;
import java.util.Map;
RedisClient client = RedisClient.create("redis://localhost:6379");
RedisCommands<String, String> redis = client.connect().sync(); // the command seam
String id = RedisPublisher.create(redis, "orders")
.publish("urn:babel:orders:created", Map.of("order_id", 1042L));
publish(urn, data) returns the message meta.id; an overload adds a traceId to continue a
trace. Produce is RPUSH <queue> <envelope>.
Consume
import com.babelqueue.redis.RedisConsumer;
RedisConsumer consumer = RedisConsumer.builder(redis, "orders")
.handler("urn:babel:orders:created", (env, body) -> {
// env.data(), env.traceId(), env.attempts() ...
})
.onError((err, env, body) -> log.warn("bad message", err))
.build();
consumer.run(); // blocking-reserves until the thread is interrupted
A reserve atomically moves the head of the queue onto a per-queue <queue>:processing list
(BLMOVE <queue> <queue>:processing LEFT RIGHT), so an in-flight message survives a crash; a
successful handler LREMs it. Retry is at-least-once: a throwing handler leaves the element on
the processing list (a recovery sweep can requeue it). The poll loop never stops on a bad message —
observe via onError / onUnknownUrn. Unknown-URN strategy is one of
fail / delete / release / dead_letter.
The command seam is Lettuce’s RedisCommands<String, String> interface, so the publisher and
consumer unit-test against a mock with no Redis and no network.
Contract mapping (§1)
| Envelope | Redis |
|---|---|
| body | the list element — the canonical envelope JSON, byte-for-byte, no wrapping |
job (URN) |
read from the body and routed consumer-side (Redis lists carry no native metadata) |
| produce | RPUSH <queue> <envelope> |
| reserve | BLMOVE <queue> <queue>:processing LEFT RIGHT (head → tail; crash-safe in-flight) |
| ack | LREM <queue>:processing 1 <envelope> |
attempts |
taken from the body unchanged (Redis has no native delivery counter) |
This is a Java-owned reliable queue, mirroring the Go runtime’s reliable-queue mechanism —
pointed at a queue this SDK owns end-to-end it is a complete, crash-safe transport. Full parity with
Laravel’s reserved-sorted-set reservation on a shared PHP+Java Redis queue is a separate task (see
broker-bindings §1.4). The envelope is unchanged (schema_version stays 1); Redis is purely
additive.