question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to use Redis pub/sub with Quarkus and ReactiveRedisClient?

See original GitHub issue

I am trying to use Redis pub/sub with Quarkus, but I cannot find any documentation about it. I managed to use the Quarkus Redis Extension with Redis Streams xadd and xread, but cannot make Redis pub/sub work. I am using ReactiveRedisClient and when I try to subscribe to a redis channel in a Quarkus test I receive following error:

Error received: io.vertx.core.impl.NoStackTraceThrowable: PubSub command in connection-less mode not allowed

I found the error is thrown in vertx redis extension BaseRedisClient class:

@Override
  public Future<@Nullable Response> send(Request command) {
    final Promise<Response> promise = vertx.promise();

    if (command.command().isPubSub()) {
      // mixing pubSub cannot be used on a one-shot operation
      promise.fail("PubSub command in connection-less mode not allowed");
      return promise.future();
    }
  }

Generally I find Quarkus would benefit of documenting how to use Redis Pub/Sub cause I found other people are also confused how to use it exactly.

Thanks in advance for any help!

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:16 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
petarminchevcommented, Nov 23, 2021

Today I had same time to dig deeper and found the issue.

Basically the vertx redis client doesn’t support pub/sub in connection pooled mode.

https://vertx.io/docs/vertx-redis-client/java/#_connection_pooling Pooling is not compatible with SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE or PUNSUBSCRIBE because these commands will modify the way the connection operates and the connection cannot be reused.

https://vertx.io/docs/vertx-redis-client/java/#_pubsub_mode

And since all Quarkus producers inside RedisClientsProducer class are using the BaseRedisClient which has a connection manager of a connection pool, then ReactiveRedisClient is not supporting pub/sub.

In my opinion Quarkus should support a setting boolean flag to switch between connection pool mode and single connection mode for vertx redis extension. You can see in RedisAPIImpl class of Vertx:

public RedisAPIImpl(RedisConnection connection) {
    this.connection = connection;
    this.redis = null;
  }

  public RedisAPIImpl(Redis redis) {
    this.connection = null;
    this.redis = redis;
  }
  
if (redis != null) {
      // operating in pooled mode
      redis.send(req, promise);
    } else if (connection != null) {
      // operating on connection mode
      connection.send(req, promise);
    }

In my opinion Quarkus needs a config flag which of the two constructors to use. Currently Quarkus uses only the 2nd constructor which calls the connection pooling of vertx and vertx doesn’t support pub/sub in connection pooling mode.

If Quarkus doesn’t plan to fix this, then at least it should be mentioned in the doc.

The workaround currently is to directly use the Vertx redis extension and bypass the Quarkus injectors. I managed to make it work with Mutiny by manually instantiating the Quarkus Redis Mutiny wrappers, but in my opinion it is much simpler to use directly what Vertx provides for pub/sub. Otherwise we need to subscribe to event bus, etc…

Here is a simple example to use only Vertx:

Redis redisClient = Redis.createClient(vertx, "redis://localhost:1234");
    	redisClient
    		.connect()
    	    .onSuccess(conn -> {    
    	    	System.out.println("Successfully connected = " + conn + " " + Thread.currentThread().getName());	
    	    	
    	    	//subscribe(conn);
    	    	
    	    	conn.handler(message -> {
    	    		// do whatever you need to do with your message
    	    		System.out.println("Message = " + message + " " + Thread.currentThread().getName());
	    	    }); 
    	    	
    	    	conn.send(Request.cmd(Command.SUBSCRIBE).arg("CHANNEL1").arg("CHANNEL2"))
    	    	  .onSuccess(res -> {
    	    		  System.out.println("Subscribed");
    	    		        	    		
      	    		  publish(conn);
    	    	  });    	    	    	    	  
    	    })
    	    .onFailure(ex -> {
    	    	System.out.print("Failed to connect " + ex.getMessage());
    		    ex.printStackTrace();
    	    });
    	    
private void publish(RedisConnection conn) {
    	conn.send(Request.cmd(Command.PUBLISH).arg("CHANNEL1").arg("Hello World!"))
	  	  .onSuccess(res -> {
	  		    System.out.println("Published Hello World, res = " + res);
	  		  });
	  	
    	conn.send(Request.cmd(Command.PUBLISH).arg("CHANNEL2").arg("How are you!"))
		  	  .onSuccess(res -> {
		  		    System.out.println("Published How are you!, res = " + res);
		  		  });
  	
    }

To conclude - I think Quarkus should take some action - at least mention that in the doc and maybe provide that boolean flag config to be able to configure the injected client NOT to use pooling.

And here is the workaround to use the Mutiny wrappers after manually getting the vertx connection from the callback, so the pooling is bypassed:

private void subscribe(RedisConnection connection) {
    	RedisAPI vertxRedisAPI = RedisAPI.api(connection);
    	io.vertx.mutiny.redis.client.RedisAPI redisAPI = new io.vertx.mutiny.redis.client.RedisAPI(vertxRedisAPI);

    	redisAPI.subscribe(List.of("CHANNEL1", "CHANNEL2"))
	    	.subscribe().with(
					response -> { System.out.println("Subscribed"); publish(redisAPI); },
					error -> System.out.println("Error received: " + error));
    }
    
    private void publish(io.vertx.mutiny.redis.client.RedisAPI redisAPI) {
    	redisAPI.publish("CHANNEL1", "message1").subscribe().with(
				response -> System.out.println("Published 1st Message Response: " + response),
				error -> System.out.println("Error received: " + error));
    	
    	redisAPI.publish("CHANNEL2", "message1").subscribe().with(
				response -> System.out.println("Published 2nd Message Response: " + response),
				error -> System.out.println("Error received: " + error));
    }
    
    public void initializeConsumer() {
    	eventBus.<JsonObject>consumer("io.vertx.redis.CHANNEL1", message -> {
    		System.out.println("Consumed event = " + message.body());
    	});
    	
    	eventBus.<JsonObject>consumer("io.vertx.redis.CHANNEL2", message -> {
    		System.out.println("Consumed event = " + message.body());
    	});
    }

1reaction
machi1990commented, Nov 2, 2021

Error received: io.vertx.core.impl.NoStackTraceThrowable: PubSub command in connection-less mode not allowed

The error means you are trying to use the same client to publish and subscribe which is not allowed by the upstream redis client. /cc @pmlopes

The issue has been reported in the past (I cannot find the reference to it) and our recommendation has been to create two separate clients using the multiple redis client feature. One create will perform the subscription and the other to publish.

quarkus.redis.hosts=redis://localhost:6379
quarkus.redis.publish.hosts=redis://localhost:6379

@Inject
ReactiveRedisClient defaultClient; // you can use the default client for subscription


@Inject
@RedisClientName("publish")
ReactiveRedisClient publishingClient; //  you can use this client for publishing
Read more comments on GitHub >

github_iconTop Results From Across the Web

Redis Extension Reference Guide - Quarkus
Using pub/sub. Redis allows sending messages to channels and listening for these messages. These features are available from the the pubsub group.
Read more >
Pub/Sub with the reactive redis client : r/quarkus - Reddit
Where are you initializing your code ? It should be done by observing a startup event. I've an example code of Redis Streams...
Read more >
Is it possible to subscribe to a topic in redis using quarkus ...
I created a small project to showcase a redis pub/sub using quarkus https://github.com/omarkad2/quarkus-redis-example.
Read more >
ReactiveRedisClient (Quarkus - Redis Client - javadoc.io
public interface ReactiveRedisClient. A Redis client offering reactive Redis commands. For more information about how each individual command visit the ...
Read more >
Redis - Jerome Boyer's Personal Site
Redis ¶ · Getting started / concepts¶ · Run it in cluster¶ · Redis for distributed database¶ · Redis client¶ · Using redis...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found