redis/lettuce

How to reduce memory footprint of RedisClient

QuChen88 opened this issue · 1 comments

Current Behavior

We noticed that each lettuce client uses ~4 MB of memory even when we are not issuing large commands that are several MBs in size to Redis. I would expect ~KBs of memory usage in our case.

I did a heap dump analysis and observed those byte[] buffers are allocated from io.lettuce.core.protocol.CommandHandler and io.lettuce.core.protocol.RedisStateMachine

Screenshot 2024-07-10 at 11 54 35 AM

Input Code

Input Code
    ClientResources resources = ClientResources.builder().nettyCustomizer(new NettyCustomizer() {
        @Override
        public void afterBootstrapInitialized(Bootstrap bootstrap) {
            String dbInterfaceName = System.getProperty(SystemSetting.DB_INTERFACE_NAME.getName(), null);
            try {
                if (dbInterfaceName == null) {
                    bootstrap.localAddress(new InetSocketAddress("0.0.0.0", 0)); // <- This is the case for me
                } else {
                    NetworkInterface networkInterface = NetworkInterface.getByName(dbInterfaceName);
                    InetAddress inetAddress = networkInterface.getInetAddresses().nextElement();
                    bootstrap.localAddress(new InetSocketAddress(inetAddress, 0));
                }
            } catch (SocketException e) {
                throw new RuntimeException("Unable to lookup IP address for interface '" + dbInterfaceName + "'", e);
            }

        }
    }).eventExecutorGroup(eventLoopGroup).build();
    final RedisURI redisUriCluster =
            RedisURI.Builder.redis(endpoint).withPort(port).withSsl(true).withVerifyPeer(false).build();
    RedisClient redisClient = RedisClient.create(resources, redisUriCluster);
    ClientOptions options = ClientOptions.builder()
                .socketOptions(SocketOptions.builder()
                        .keepAlive(true)
                        .connectTimeout(CONNECTION_TIMEOUT_MILLIS)
                        .build())
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .autoReconnect(true)
                .timeoutOptions(TimeoutOptions.builder()
                        .fixedTimeout(FIXED_TIMEOUT_MILLIS)
                        .build())
                .build();
    redisClient.setOptions(options);

Expected behavior/code

Is this expected behavior? If so, are there ways for me to reduce the memory footprint of lettuce clients?

We are running on a relatively memory constraint system with only ~1GB of memory. We configured about 570MB of direct memory. (i.e. JVM config parameter -Xms256m -Xmx640m -XX:MaxDirectMemorySize=570m -DdirectMemorySoftLimit=320m). We can support more lettuce client connections if the memory footprint can be lowered here.

Environment

  • Lettuce version(s): 6.2.5 RELEASE
  • Redis version: 7.0

This is by design. Lettuce is intended for advanced high-performance usage where we have several pre-initialized buffers to be used for sending and receiving commands. The event-based design requires a different approach than an InputStream-based client that just reads and writes protocol bytes.

If you have to limit memory to 1GB, then you might be better off just using Jedis.