-
Notifications
You must be signed in to change notification settings - Fork 983
Using Lettuce with Native Images
This section explains how to use Lettuce with Graal Native Image compilation.
The GraalVM native-image
tool enables ahead-of-time (AOT) compilation of Java applications into native executables or shared libraries. While traditional Java code is just-in-time (JIT) compiled at run time, AOT compilation has two main advantages:
-
First, it improves the start-up time since the code is already pre-compiled into efficient machine code.
-
Second, it reduces the memory footprint of Java applications since it eliminates the need to include infrastructure to load and optimize code at run time.
There are additional advantages such as more predictable performance and less total CPU usage.
Native images assume a closed world principle in which all code needs to be known at the time the native image is built. Graal’s SubstrateVM analyzes class files during native image build-time to determine what bytecode needs to be translated into a native image. While this task can be achieved to a good extent by analyzing static bytecode, it’s harder for dynamic parts of the code such as reflection. When using reflective access or Java proxies, the native image build process requires a little bit of help so it can include parts that are required during runtime.
Lettuce ships with configuration files that specifically describe which classes are used by Lettuce during runtime and which Java proxies get created.
Starting as of Lettuce 5.3.2, the following configuration files are available:
-
META-INF/native-image/io.lettuce/lettuce-core/native-image.properties
-
META-INF/native-image/io.lettuce/lettuce-core/proxy-config.json
-
META-INF/native-image/io.lettuce/lettuce-core/reflect-config.json
Those cover Lettuce operations for RedisClient
and RedisClusterClient
.
Depending on your configuration you might need additional configuration for Netty, HdrHistorgram (metrics collection), Reactive Libraries, and dynamic Redis Command interfaces.
Lettuce uses HdrHistogram and LatencyUtils to accumulate metrics. You can use your application without these. If you want to use Command Latency Metrics, please add the following lines to your own reflect-config.json
file:
{
"name": "org.HdrHistogram.Histogram"
},
{
"name": "org.LatencyUtils.PauseDetector"
}
You can use Dynamic Command Interfaces when compiling your code to a GraalVM Native Image. GraalVM requires two information as Lettuce inspects command interfaces using reflection and it creates a Java proxy:
-
Add the command interface class name to your
reflect-config.json
using ideallyallDeclaredMethods:true
. -
Add the command interface class name to your
proxy-config.json
reflect-config.json
[
{
"name": "com.example.MyCommands",
"allDeclaredMethods": true
},
]
proxy-config.json
[
["com.example.MyCommands"]
]
If you decide to use a specific reactive library with dynamic command interfaces, please add the following lines to your reflect-config.json
file, depending on the presence of Rx Java 1-3:
{
"name": "rx.Completable"
},
{
"name": "io.reactivex.Flowable"
},
{
"name": "io.reactivex.rxjava3.core.Flowable"
}
For now, native images must be compiled with --report-unsupported-elements-at-runtime
to ignore missing Method Handles and annotation synthetization failures.
To properly start up the netty stack, the following reflection configuration is required for netty and the JDK in reflect-config.json
:
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields",
"fields":[{"name":"producerLimit","allowUnsafeAccess" : true}]
},
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields",
"fields":[{"name":"consumerIndex","allowUnsafeAccess" : true}]
},
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields",
"fields":[{"name":"producerIndex", "allowUnsafeAccess" : true}]
},
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField",
"fields":[{"name":"consumerIndex", "allowUnsafeAccess" : true}]
},
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField",
"fields":[{"name":"producerIndex", "allowUnsafeAccess" : true}]
},
{
"name":"io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField",
"fields":[{"name":"producerLimit","allowUnsafeAccess" : true}]
},
{
"name":"java.nio.Buffer",
"fields":[{"name":"address", "allowUnsafeAccess":true}]
},
{
"name":"java.nio.DirectByteBuffer",
"fields":[{"name":"cleaner", "allowUnsafeAccess":true}],
"methods":[{"name":"<init>","parameterTypes":["long","int"] }]
},
{
"name":"io.netty.buffer.AbstractReferenceCountedByteBuf",
"fields":[{"name":"refCnt", "allowUnsafeAccess":true}]
},
{
"name":"io.netty.buffer.AbstractByteBufAllocator",
"allPublicMethods": true,
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true
},
{
"name":"io.netty.buffer.PooledByteBufAllocator",
"allPublicMethods": true,
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true
},
{
"name":"io.netty.channel.ChannelDuplexHandler",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name":"io.netty.channel.ChannelHandlerAdapter",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.ChannelInboundHandlerAdapter",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.ChannelInitializer",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.ChannelOutboundHandlerAdapter",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.DefaultChannelPipeline$HeadContext",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.DefaultChannelPipeline$TailContext",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.channel.socket.nio.NioSocketChannel",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name": "io.netty.handler.codec.MessageToByteEncoder",
"allPublicMethods": true,
"allDeclaredConstructors":true
},
{
"name":"io.netty.util.ReferenceCountUtil",
"allPublicMethods": true,
"allDeclaredConstructors":true
}
Lettuce documentation was moved to https://redis.github.io/lettuce/overview/
Intro
Getting started
- Getting started
- Redis URI and connection details
- Basic usage
- Asynchronous API
- Reactive API
- Publish/Subscribe
- Transactions/Multi
- Scripting and Functions
- Redis Command Interfaces
- FAQ
HA and Sharding
Advanced usage
- Configuring Client resources
- Client Options
- Dynamic Command Interfaces
- SSL Connections
- Native Transports
- Unix Domain Sockets
- Streaming API
- Events
- Command Latency Metrics
- Tracing
- Stateful Connections
- Pipelining/Flushing
- Connection Pooling
- Graal Native Image
- Custom commands
Integration and Extension
Internals