Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting RuntimeException "java.lang.RuntimeException: Multi is a Map" while HGETALL with redis-server version 6.2.5. #347

Open
navi2589 opened this issue Sep 14, 2022 · 3 comments

Comments

@navi2589
Copy link

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 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

  1. Add hash in redis having redis-server version 6.2.5.
  2. try to access this hash with hgetall with the above reproducer
  3. You will get the exception java.lang.RuntimeException: Multi is a Map
  4. 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
@pmlopes
Copy link
Member

pmlopes commented Sep 14, 2022

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 pmlopes removed the bug label Sep 14, 2022
@navi2589
Copy link
Author

@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.

@pmlopes
Copy link
Member

pmlopes commented Sep 16, 2022

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants