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.