Pipeline design question when using lettuce for Redis - redis

I'm using pipeline with lettuce, and I have a design question. When trying send a block of commands to redis using the 'sendBlock' method below, I'm thinking about 2 options:
(1) Having one instance of the connection already established in the class, and reuse it:
private void sendBlock()
{
this.conn.setAutoFlushCommands(false);
(...)
this.conn.flushCommands();
}
(2) Every time I send a block of commands get a connection from redis, perform the action and close it.
private void sendBlock()
{
StatefulRedisModulesConnection<String, String> conn = RedisClusterImpl.connect();
conn.setAutoFlushCommands(false);
(...)
conn.flushCommands();
conn.close();
}
Since established connections seem to be shared between all threads in lettuce, I'm not sure if point 1 is correct. If not, I have to go to point 2. And in this case I don't know how costly is to obtain a connection from Redis, so I'm wondering if I need to use pooling (thing that is not recommended in the lettuce docs). In our use case the 'sendBlock' method can be simultaneously called hundreds of times, so it's intensively used by a lot of different threads.
Any help would be really appreciated.
Joan.

Lettuce connections are thread-safe and can be shared if you don't use Redis-blocking commands (ex. BLPOP) and transactions.
Those should be performed on separate connections, as the transaction will apply to the entire connection, and blocking operations will block the connection until they're complete.
Whether or not you should share a manually-flushed connection depends only on the number of operations you perform between flushes. Ex. if each block is 10k commands, and you have 10 threads, you could queue 100k to send at once where you expected 10k. Whether or not this matters will depend on your application, and you should check the performance of your individual case.
If each block is not sending many commands you may not even need to flush manually as Lettuce pipelines with auto-flush enabled (see this answer).

Related

Getting Aerospike timeout with multiple java client in application

Currently I am using Aerospike in my application.
I faced lots of timeout issues as shown below when I was creating new java client for each transaction and I was not closing it so number of connection ramp up dramatically.
Aerospike Error: (9) Client timeout: timeout=1000 iterations=1 failedNodes=0 failedConns=0
so to resolve this timeout issue,I didn't made any changes to client, read and write policy, I just created only one client, stored it's instance in some variable and used this same client for all transaction (get or put requests).
now I want to understand how moving from multiple client to one client resolved my timeout issue.
how these connection were not closing automatically.
The AerospikeClient constructor requests peers, partition maps and racks for all nodes in the cluster and initializes connection pools and async eventloops. This is an expensive process that is only meant to be performed once per cluster at application startup. AerospikeClient is thread-safe, so instances can be shared between threads.
If AerospikeClient close() is not called, connections residing in the pools (at least one connection pool per node) will not be closed. There are no finalize() methods in AerospikeClient.
The first transaction(s) usually need to create new connections. This adds to the latency and can cause timeouts.
The client does more than just the application's transactions. It also monitors the cluster for changes so that it can maintain one hop per transaction. Also, I believe when we initialize the client, we create an initial pool of sockets.
It is expected that most apps would only need one global client.

Can disque handle RPC?

I have implemented a RPC which is disqueue-node on npm. My concern is that I have achieved this using 3 tcp connections for request, response and replyQueue. In the reason that get jobs waits until it gets jobs then hangs up the rest of the commands. Do you think it can be achieved using 1 tcp connection?
If you want to use both GETJOB and ADDJOB in the context of the same connection, you may want to use either short timeouts or the NOHANG option of GETJOB in order to avoid blocking if there are no messages to be fetched, and retry from time to time.
Note that TCP connections when idle are mostly for free, so I would not fight too much with that if you don't have a connections scalability problem yet. Moreover given the distributed nature of Disque you can distribute connections to multiple nodes in the future very easily, if you'll ever need that.
Btw in the case you have a pretty trivial RCP protocol: request, response, there should be no problem in blocking, like in:
ADDJOB ... your RCP request
reply = GETJOB ...
You still need a reasonable GETJOB timeout in order to inform the caller that the
re was a problem receiving the reply in time. And maybe it is worth to return the
original job ID to the caller, so that it can decide to wait more for the reply
(in case the RCP is non cheap to retry or alike).

Why A single Jedis instance is not threadsafe?

https://github.com/xetorthio/jedis/wiki/Getting-started
using Jedis in a multithreaded environment
You shouldn't use the same instance from different threads because you'll have strange errors. And sometimes creating lots of Jedis instances is not good enough because it means lots of sockets and connections, which leads to strange errors as well.
A single Jedis instance is not threadsafe
! To avoid these problems, you should use JedisPool, which is a threadsafe pool of network connections. You can use the pool to reliably create several Jedis instances, given you return the Jedis instance to the pool when done. This way you can overcome those strange errors and achieve great performance.
=================================================
I want to know why? Can anyone help me please
A single Jedis instance is not threadsafe because it was implemented this way. That's the decision that the author of the library made.
You can check in the source code of BinaryJedis which is a super type of Jedis https://github.com/xetorthio/jedis/blob/master/src/main/java/redis/clients/jedis/BinaryJedis.java
For example these lines:
public Transaction multi() {
client.multi();
client.getOne(); // expected OK
transaction = new Transaction(client);
return transaction;
}
As you can see the transaction field is shared for all threads using Jedis instance and initialized in this method. Later this transaction can be used in other methods. Imagine two threads perform transactional operations at the same time. The result may be that a transaction created by one thread is unintentionally accessed by another thread. The transaction field in this case is shared state access to which is not synchronized. This makes Jedis non-threadsafe.
The reason why the author decided to make Jedis non-threadsafe and JedisPool threadsafe might be to provide flexibility for clients so that if you have a single-threaded environment you can use Jedis and get better performance or if you have a multithreaded environment you can use JedisPool and get thread safety.

Rabbitmq - Should multithreaded application use single or multi channels

My app has multiple threads that publish messages to a single RabbitMQ cluster.
Reading the rabbit docs: i read the following:
For applications that use multiple threads/processes for processing, it is very common to open a new channel per thread/process and not share channels between them.
And I understand that instead of opening multiple connection (expensive)
it is better to open multiple channels.
But why not use a single channel to all threads?
What are the benefits of using multiple channels over a single channel?
AMQP has the concept of Channel to provide more flexibility over reliable TCP connections. Opening a TCP connection per message would be extremely expensive, so they came up with the idea of logical Channels within a connection.
It is not a good idea to use a Channel for all the threads because if anything fails in a particular thread and the Channel dies, the rest of the threads will throw the exception AlreadyClosedException. A channel can die for multiple reasons: for example for trying to declare something that is already declared with other parameters or trying to cancel a consumer which doesn't exist, publishing to an exchange that doesn't exist, etc...
My best advice would be to have an object that holds a Channel in a local variable and also implements ShutdownListener interface, so every time the channel fails, it is able to recover and create a new one from a connection. So I would say that the main benefit is failure tolerance and scalability, since if a Channel dies it won't affect the rest.

Client Server Command Design pattern with variable delays

I am writing a client program to control a server which is in turn controlling some large hardware. The server needs to receive commands to initialize, start, stop and control the hardware.
The connection from the client to the server is via a TCP or UDP socket. Each command is encapsulated in an appropriate message using a SCADA protocol (e.g. Modbus or DNP3).
Part of the initialization phase involves sending a sequence of commands from the client to the server. In some cases there must be a delay in seconds between the commands to prevent multiple sub-systems being initialized at the same time. The value of the delay depends on the type of command.
I'm thinking that the Command Design Pattern is a good approach to follow here. The client instantiates ConcreteCommands and the Invoker places it in a queue. I'm not sure how to incorporate the variable delay and whether there's a better pattern which involves a timer and a queue to handle sending
messages with variable delays.
I'm using C# but this is probably irrelevant since it's more of a design pattern question.
It sounds like you need to store a mapping of types to delay. When your server starts, could you cache those delay times? Then call a method that processes the command after a specified delay?
When the server starts:
Dictionary<Type, int> typeToDelayMapping = GetTypeToDelayMapping();
When a command reaches the server, the server can call this:
InvokeCommand(ICommand command, int delayTimeInMilliseconds)
Like so:
InvokeCommand(command, typeToDelayMapping[type]);