Open-source project
redis/lettuce avatar
redis/lettuce

Lettuce, the netty based Redis client for Java: one shared connection, a placeholder Maven version, and a make driven test fleet

Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.

5,787 stars1,124 forksJavaMIT

At a glance

What is it?
A close reading of redis/lettuce, the Java Redis client built on netty: what the shared connection rule actually allows, why the published dependency snippet has no version in it, and what the Makefile demands before the test suite will run.
Who is it for?
Lettuce fits a Java service that wants one netty backed connection to Sentinel or Cluster with sync, async and reactive views of the same command surface, and the project is still shipping: 7.9.0.RELEASE and 8.0.0.BETA1 landed on 2026-10-14 and 6.9.0.RELEASE on 2026-10-16, and the last push was 2026-11-02.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • redis/lettuce5,787 ★ · +17
Star history
Repository2026-07-282026-11-13
redis/lettuce5.8k5.8k

17 Stars in the last 108 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One connection is thread-safe until BLPOP or MULTI/EXEC shows up

Lettuce is built with netty, and its central promise is that a single connection can be shared by several threads. The condition comes with the promise: multiple threads may share one connection if they avoid blocking and transactional operations such as BLPOP and MULTI/EXEC. That clause is the entire caveat, and it is a sharp one. A blocking list pop and a transaction are the two cases where the connection stops behaving like a neutral pipe between threads, so a service that mixes either of them with shared handles has to arrange its own exclusion.

Nothing in the project description offers a pool of per thread connections to route around that. Synchronous, asynchronous and reactive usage are three ways of waiting for a reply on the same connection object, not three separate transports. Sharing is therefore a deliberate design decision, and the bill for that decision arrives in whichever class mixes BLPOP with a shared synchronous handle.

sync(), async() and reactive() are three faces of one command set

connection.sync() hands back RedisStringCommands, connection.async() hands back RedisStringAsyncCommands whose calls return a RedisFuture, and connection.reactive() hands back RedisStringReactiveCommands whose calls return a Mono. What changes across the three is how a caller waits, not which connection carries the command. The smallest complete example opens the client, connects, and reads one key:

java
RedisClient client = RedisClient.create("redis://localhost");
StatefulRedisConnection<String, String> connection = client.connect();
RedisStringCommands sync = connection.sync();
String value = sync.get("key");

The async view starts a set and a get before reading either result, then waits on the pair at once:

java
StatefulRedisConnection<String, String> connection = client.connect();
RedisStringAsyncCommands<String, String> async = connection.async();
RedisFuture<String> set = async.set("key", "value");
RedisFuture<String> get = async.get("key");

LettuceFutures.awaitAll(set, get) == true

set.get() == "OK"
get.get() == "value"

The reactive view returns Mono values instead of futures, one subscribed and one blocked:

java
StatefulRedisConnection<String, String> connection = client.connect();
RedisStringReactiveCommands<String, String> reactive = connection.reactive();
Mono<String> set = reactive.set("key", "value");
Mono<String> get = reactive.get("key");

set.subscribe();

get.block() == "value"

Pub/Sub takes yet another entry point, connectPubSub(), whose sync view returns RedisPubSubCommands; a listener is attached through connection.getStatefulConnection().addListener(new RedisPubSubListener<String, String>() { ... }) before connection.subscribe("channel") runs. A subscription therefore lives on its own connection handle rather than on the shared command connection, which is one more reason the shared connection rule has room to work.

Method names copy the Redis command with the modifier folded in

Every Redis command is implemented by one or more methods whose names are identical to the lowercase Redis command name, so a reader who already knows the redis-cli can guess the Java method. Complex commands that carry several modifiers, where a modifier changes the result type, put that modifier in CamelCase inside the method name: zrangebyscore and zrangebyscoreWithScores. The type change is visible in the identifier rather than hidden behind an argument, which keeps the overload chain short and lets the compiler carry part of the check.

A second naming axis sits alongside the command names. The README also lists support for multiple Command Interfaces, documented on the project wiki. The concrete signatures live in the javadoc for the lettuce-core artifact on javadoc.io. Runtime compatibility is stated as Java 8 and later, delivered as an implicit automatic module without descriptors, which means a project on the module path gets an automatic module name rather than a hand written module-info.

The Maven coordinates are published with a placeholder version

Releases sit in the Maven Central repository, and the whole declaration is one coordinate pair: groupId io.lettuce, artifactId lettuce-core. The version element in the sample is the literal x.y.z, so the snippet is a shape to fill in rather than a pin that can be pasted and built:

xml
<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>x.y.z</version>
</dependency>

A first build therefore fails on version resolution until somebody supplies a real number. The variant for the upcoming major version swaps the suffix for BUILD-SNAPSHOT and adds the Sonatype snapshot repository with snapshots enabled:

xml
<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>x.y.z.BUILD-SNAPSHOT</version>
</dependency>

<repositories>
  <repository>
    <id>sonatype-snapshots</id>
    <name>Sonatype Snapshot Repository</name>
    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    <snapshots>
      <enabled>true</enabled>
    </snapshots>
  </repository>
</repositories>

The most recent releases are 7.9.0.RELEASE and 8.0.0.BETA1, both on 2026-10-14, and 6.9.0.RELEASE on 2026-10-16. Dependency information for Maven, Ivy, Gradle and other tools is pointed at http://search.maven.org, yet no Gradle or Ivy snippet appears in the project files, so a Gradle build has to translate the coordinate on its own.

make start assembles a Redis fleet from a docker compose file

The build is Apache Maven, and the tests need multiple running Redis instances for different test cases, which the Makefile provisions. The contributor path is three commands: clone the repository, change into the directory, then start the environment.

code
$ git clone https://github.com/redis/lettuce.git
$ cd lettuce/
$ make start

The start target creates the work directory, runs the cleanup service, and then brings the compose stack up one service at a time with wait enabled, using src/test/resources/docker-env/docker-compose.yml together with the environment file src/test/resources/docker-env/.env, plus a per version override when a file named .env.v and the version number exists. Four targets cover the loop: make test, make start, make stop and make clean.

Two details mark this as a contributor setup rather than an install step for an application. The Makefile prepends ./work/redis-git/src to PATH, and it defines REDIS_ENV_WORK_DIR as ROOT_DIR/work unless the caller exports a different one. The test target also hands two socket paths to Maven through MVN_SOCKET_ARGS, one for a domain socket and one for a Sentinel domain socket. Tests run by default against Redis latest.

The version matrix is a closed list from 7.4 to 8.12 with 8.10 as default

Which Redis the tests talk to is a Make variable rather than a command line flag, and the list is fixed:

code
SUPPORTED_TEST_ENV_VERSIONS := 8.12 8.10 8.8 8.6 8.4 8.2 8.0 7.4 7.2
DEFAULT_TEST_ENV_VERSION := 8.10
PROFILE ?= ci
MVN_EXTRA_ARGS ?=

When the version argument is empty the macro falls back to 8.10, and a version outside that list makes it print an error naming the supported versions and exit 1. Setting CLIENT_LIBS_TEST_IMAGE_TAG blanks the version out and prints the image tag in use instead, which is the escape hatch for a locally built test image. PROFILE and MVN_EXTRA_ARGS can both be overridden from the command line because of the ?= assignment.

The closed list is the constraint worth knowing before a test run is planned: anything newer than 8.12 or older than 7.2 needs a Makefile edit. The project description also states that this version has been tested against the latest Redis source build, and that is a separate claim from membership in this matrix.

Docs are a mkdocs site, and the samples stop at an unencrypted local socket

The reference documentation and the Javadoc are the two entry points, and the documentation is itself a mkdocs project in the repository: mkdocs.yml, a docs directory, and four Python pins for the site build.

code
mkdocs~=1.6
mkdocs-material~=9.5
pymdown-extensions~=10.8
mkdocs-macros-plugin~=1.0

Rebuilding the docs locally means a Python environment that satisfies those pins, which is a second toolchain beside Maven and docker compose.

The samples themselves stop at the plain case. Every snippet in the project description opens redis://localhost with no credentials and no TLS, even though SSL and Unix domain socket connections, the streaming API, codecs for UTF8, bit and JSON representation of your data, native transports, Redis data models, Sentinel, Cluster, RediSearch, RedisJSON and Vector Sets are all listed as supported. None of those settings is demonstrated in code, so the configuration work sits in the reference documentation rather than in the README. The code is MIT licensed and forked from wg/lettuce, the repository is not archived, and the last push was 2026-11-02.

Editorial conclusion

Lettuce fits a Java service that wants one netty backed connection to Sentinel or Cluster with sync, async and reactive views of the same command surface, and the project is still shipping: 7.9.0.RELEASE and 8.0.0.BETA1 landed on 2026-10-14 and 6.9.0.RELEASE on 2026-10-16, and the last push was 2026-11-02. Weigh it against code that mixes blocking list pops with shared handles, because that exclusion is the caller's job, and against teams that want a ready made dependency snippet, because the published coordinate leaves the version as x.y.z. Before planning a test run, check the Makefile version list, which stops at 8.12 on the high end and 7.2 on the low end.

Frequently asked questions

Can several threads share one Lettuce connection?

Yes, as long as they stay away from blocking and transactional commands. Multiple threads may share one connection if they avoid blocking and transactional operations such as BLPOP and MULTI/EXEC.

How do I add Lettuce to a Maven project?

Declare groupId io.lettuce with artifactId lettuce-core. The version element in the published snippet is the literal x.y.z, so you have to supply a real version; releases are in the Maven Central repository, and snapshots of the upcoming major version come from the Sonatype snapshot repository.

Which Java versions does Lettuce support?

Java 8 and later, packaged as an implicit automatic module without descriptors.

Which Redis versions can the Lettuce test suite run against?

The Makefile lists 8.12, 8.10, 8.8, 8.6, 8.4, 8.2, 8.0, 7.4 and 7.2, and defaults to 8.10. A version outside that list makes the start target print an error and exit 1.

What does running the Lettuce test suite require?

Apache Maven and docker compose; the Makefile also prepends ./work/redis-git/src to PATH. The Makefile starts the multiple Redis instances the tests need with make start, and make test runs the build against Redis latest by default.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/redis-lettuce.svg)](https://hysenlabs.com/projects/redis-lettuce)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/redis-lettuce.svg)](https://hysenlabs.com/projects/redis-lettuce)

Was this useful?