vertx-redis-client
vertx-redis-client copied to clipboard
Getting RuntimeException "java.lang.RuntimeException: Multi is a Map" while HGETALL with redis-server version 6.2.5.
Questions
Getting RuntimeException "java.lang.RuntimeException: Multi is a Map" while HGETALL with redis-server version 6.2.5. Same is code is working with redis-server version "3.2.6".
4.3.2
Which version(s) did you encounter this bug ?
`
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-redis-client</artifactId>
<version>4.3.2</version>
</dependency>
`
Context
Getting RuntimeException "java.lang.RuntimeException: Multi is a Map" while HGETALL with redis-server version 6.2.5. Same is code is working with redis-server version "3.2.6".
java.lang.RuntimeException: Multi is a Map at io.vertx.redis.client.impl.types.MultiType.get(MultiType.java:114) at com.example.starter.MainVerticle.lambda$start$0(MainVerticle.java:25) at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141) at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) at io.vertx.core.impl.future.Composition$1.onSuccess(Composition.java:62) at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) at io.vertx.core.impl.future.Eventually$1.onSuccess(Eventually.java:44) at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) at io.vertx.core.impl.future.SucceededFuture.addListener(SucceededFuture.java:88) at io.vertx.core.impl.future.Eventually.onSuccess(Eventually.java:41) at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) at io.vertx.redis.client.impl.RedisStandaloneConnection.handle(RedisStandaloneConnection.java:409) at io.vertx.redis.client.impl.RESPParser.handleResponse(RESPParser.java:296) at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:128) at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:24) at io.vertx.core.net.impl.NetSocketImpl.lambda$new$1(NetSocketImpl.java:98) at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:239) at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:129) at io.vertx.core.net.impl.NetSocketImpl$DataMessageHandler.handle(NetSocketImpl.java:418) at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:55) at io.vertx.core.impl.ContextBase.emit(ContextBase.java:239) at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:394) at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:155) at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:833)
Do you have a reproducer?
package com.example.starter;
import io.vertx.core.*; import io.vertx.redis.client.Redis; import io.vertx.redis.client.Request; import io.vertx.redis.client.Response;
import static io.vertx.redis.client.Command.HGETALL;
public class MainVerticle extends AbstractVerticle {
@Override public void start(Promise<Void> startPromise) throws Exception {
io.vertx.redis.client.RedisOptions publisherRedisConfig = new io.vertx.redis.client.RedisOptions();
String connectionString = "redis://localhost:6379";
publisherRedisConfig.setConnectionString(connectionString);
Redis publisherRedis = Redis.createClient(Vertx.currentContext().owner(), publisherRedisConfig);
final Request req = Request.cmd(HGETALL);
req.arg("PROD-SUPPORT.Interface.PROVIDERS_7773");
publisherRedis.send(req, handler->{
if(handler.succeeded()){
Response response = handler.result();
Object getResult = response.get(1);
System.out.println(getResult.toString());
}
});
}
}
Steps to reproduce
- Add hash in redis having redis-server version 6.2.5.
- try to access this hash with hgetall with the above reproducer
- You will get the exception java.lang.RuntimeException: Multi is a Map
- downgrade redis-server version to 3.2.6 then you will not get exception.
Extra
- Java version amazon corretto 17 (Issue is not specific to java version)
- Mac os monterey 12.5.1
Hi @navi2589 this is not a bug IMHO.
I've recreated your issue:
package io.vertx.test.redis;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import io.vertx.redis.client.Redis;
import io.vertx.redis.client.RedisOptions;
import org.junit.*;
import org.junit.runner.RunWith;
import org.testcontainers.containers.GenericContainer;
import java.util.Arrays;
import java.util.UUID;
import static io.vertx.redis.client.Command.*;
import static io.vertx.redis.client.Request.cmd;
@RunWith(VertxUnitRunner.class)
public class RedisClient6HGETALLTest {
@Rule
public final RunTestOnContext rule = new RunTestOnContext();
@ClassRule
public static final GenericContainer<?> redis = new GenericContainer<>("redis:6.2.5")
.withExposedPorts(6379);
private Redis client;
@Before
public void before(TestContext should) {
final Async before = should.async();
client = Redis.createClient(
rule.vertx(),
new RedisOptions().setConnectionString("redis://" + redis.getHost() + ":" + redis.getFirstMappedPort() + "?client=tester"));
client.batch(Arrays.asList(
cmd(HSET).arg("PROD-SUPPORT.Interface.PROVIDERS_7773").arg("field1").arg("Hello"),
cmd(HSET).arg("PROD-SUPPORT.Interface.PROVIDERS_7773").arg("field2").arg("World")
))
.onFailure(should::fail)
.onSuccess(ok -> before.complete());
}
@After
public void after() {
client.close();
}
private static String makeKey() {
return UUID.randomUUID().toString();
}
@Test(timeout = 10_000L)
public void testBasicInterop(TestContext should) {
final Async test = should.async();
client.send(cmd(HGETALL).arg("PROD-SUPPORT.Interface.PROVIDERS_7773"))
.onFailure(should::fail)
.onSuccess(response -> {
Object getResult = response.get(1);
System.out.println(getResult.toString());
test.complete();
});
}
}
What is happening is that the redis server is returning a message as:
%2
$6
field1
$5
Hello
$6
field2
$5
World
The % is telling that the response is of type Map:
https://github.com/antirez/RESP3/blob/master/spec.md#map-type
Which means you cannot look up data using a position, you can locate data using the key, for example:
Object getResult = response.get("field1");
While this used to work form redis <6.0.0 (the old protocol didn't support Maps only Arrays) the vert.x client always had a way to emulate the behavior.
You should use the right getter in this case.
@pmlopes Thanks for clarifying . As of today we are using redis-server version 3.2.6 and code is working with this version. We would need to update our code when move our redis-server version > 6.0.0.
We could have the multi store a linked hash map and compute the integer index by looping the entries. Yet if I'm not mistaken Redis 3 is EOL by July 2023.
I'm not sure if it's worth the work for less than 1 year?
If could try a PR with the idea listed above and we can see...