Skip to content

v5 Redis

Jake Moore edited this page Aug 30, 2026 · 2 revisions

Redis

⚠️ Usage ⚠️

Available in shared-jar and its inheritors (standalone-jar, spigot-jar).

KamiCommon wraps Lettuce with a connection cache, a reconnect monitor, and a pub/sub channel API. Everything is plain String. See Migrating from v4.

Connecting

Describe the server with a RedisConf, then ask the connector for an API.

RedisConf conf = RedisConf.of("redis.internal", 6379, "s3cret");
RedisAPI redis = RedisConnector.getAPI(conf);

From a connection URL

RedisConf conf = RedisConf.fromUrl("rediss://transcripts:s3cret@redis.internal:6380/3");

Accepted forms, where everything after the host is optional:

redis://host
redis://host:6380
redis://:password@host
redis://username:password@host:6380/3
rediss://username:password@host        (TLS)

Credentials must be written with a colon. redis://value@host is rejected rather than guessed at, because Lettuce reads a colon-less value as the password, so redis://myuser@host against a server with requirepass set would connect happily as default while you believed you were myuser. Write username:password@ for an ACL user, or :password@ for a password alone.

Also rejected: any scheme other than redis:// and rediss://, a non-numeric or negative database index, query parameters, and a missing host. No failure message ever contains the URL, because the URL contains the password.

ACL users

Redis 6 identifies a connection by username as well as password. Supplying only a password authenticates as default, which is the pre-ACL behaviour and still supported.

new RedisConf("redis.internal", 6379, "transcripts", "s3cret");            // ACL user
new RedisConf("redis.internal", 6379, "transcripts", "s3cret", 3, true);   // + database 3, TLS

RedisConf is a value object: two configs describing the same connection are equal and share one client. Two configs differing only by username are not equal and get separate connections, because they are different identities on the server.

Publishing and subscribing

RedisChannel channel = redis.registerChannel("server-events");

channel.subscribe((chan, message) -> {
    // called on every message published to "server-events"
});

channel.publishSync("player joined");
channel.publishAsync("player joined");
channel.publish("player joined", true);   // sync = true

For several channels sharing one callback:

RedisMultiChannel multi = redis.registerMultiChannel("chat", "punishments");
multi.subscribe((chan, message) -> { /* chan tells you which */ });
multi.publishAsync("chat", "hello");

Direct commands

redis.getCmdsSync().set("key", "value");
redis.getCmdsAsync().get("key");
redis.getConnection();       // the raw Lettuce StatefulRedisConnection
redis.isConnected();

getCmdsSync() and getCmdsAsync() return Lettuce's own command interfaces, so Lettuce's documentation applies from there down.

Shutdown is a release, not a close

redis.shutdown();

Instances are shared. Every caller passing an equal RedisConf receives the same RedisAPI, and the connection closes only once the last holder has called shutdown(). A plugin shutting down therefore no longer takes the connection away from anything else in the same JVM.

Once the connection really is closed, that instance is spent. Call RedisConnector.getAPI(...) again rather than reusing the reference.

getAPI(conf, logger) only uses your logger if that call is the one that creates the client. A cached client keeps the logger it was built with.

Migrating from v4

Jackson was removed, so channels no longer serialize objects. Everything is a String.

v4 v5
registerChannel(Class<T>, String) registerChannel(String)
registerMultiChannel(Class<T>, String...) registerMultiChannel(String...)
RedisChannel<T> RedisChannel
RedisMultiChannel<T> RedisMultiChannel
RedisChannelCallback<T> with onMessage(String, T) RedisChannelCallback with onMessage(String, String)
RedisChannelRaw removed, use RedisChannel
publishRaw / publishRawSync / publishRawAsync publish / publishSync / publishAsync
subscribeRaw subscribe

The Class<T> parameter is gone, not just the type argument. Deleting only the <T> will not compile.

Two consequences that are easy to miss:

  • Jackson left your compile classpath. It was an api dependency of shared-jar in v4, so it was available transitively. If you used Jackson without declaring it yourself, declare it now. the old JacksonUtil configured a fields-only visibility checker, so a bare new ObjectMapper() will not produce identical JSON.
  • Malformed payloads are no longer reported. v4 caught and logged deserialization failures. Now that messages are plain strings there is nothing to deserialize, so validation is yours.

Connection reuse is also new in practice. v4 documented that instances were cached, but RedisConf had no equals/hashCode, so every getAPI call built another client with its own connections and threads. That now works as documented.

Clone this wiki locally