Skip to content

V6.5.3 Redis[Clustered] - "CROSSSLOT Keys in request" when unlock #10546

@florent-lb-pro

Description

@florent-lb-pro

Hello SB-integration !

In what version(s) of Spring Integration are you seeing this issue?

Since 6.5.3.RELEASE (SpringBoot BOM parent : >=3.5.7)
6.5.2 tested and ok (SpringBoot BOM parent : <3.5.7)

Describe the bug

With a clustered redis when call the method Lock.unlock()
Using implementation RedisLockRegistry.obtain("myKey") to obtain the lock

To Reproduce

Start a clustred Redis.
Obtain a lock by calling RedisLockRegistry.obtain("myKey")
Call unlock method on obtained previous Lock

Expected behavior

The lock is created and visible with redis client (Like Redis insight).
When call unlock method.
The name of the lock is sent on the dedicated redis channel.
The lock is removed

Sample

For now I can propose you this sample

class Example(private val redisLockRegistry: RedisLockRegistry) {
   fun handle(): Void {
        val lock = redisLockRegistry.obtain("my-key-for-lock")
        val hasLock = lock.tryLock(35, TimeUnit.SECONDS)
        if (!hasLock) {
            throw NullPointerException("Unable to obtain lock")
        }
        val result = try {
          lock.unlock()
        }
    }
}

Start with a project with Springboot 3.5.7 (minimum)

I'm working to found a solution to push a repository with a sample. But get in mind the issue can be reproduced only with a redis cluster.

Stacktrace :

Caused by: io.lettuce.core.RedisCommandExecutionException: CROSSSLOT Keys in request don't hash to the same slot
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:151)
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:120)
at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:124)
at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:115)
at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:67)
at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:67)
at io.lettuce.core.cluster.ClusterCommand.complete(ClusterCommand.java:50)
at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:67)
at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:769)
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:704)
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:621)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799)
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501)
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
... 1 common frames omitted\n"

A first investigation on our own :

The stack trace come from here :

return Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute(
redisScript, List.of(this.lockKey, unLockChannelKeyToUse),
RedisLockRegistry.this.clientId));

in 6.5.2
The channel name is pass throught ARGV (arguments array) in 6.5.3 the channel name for unlock is concat with the lock key name and sent as a key .

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions