create a redis client each time or reuse a global redis client? - redis

I am trying to find the best practice to use redis client with express server, for somehow I don't know whether to create a redis client each time or reuse the global redis client.
The first method is to create only one redis client, reuse it in each request.
const redis = require("redis").createClient();
function createExample(req, res){
try {
await redis.connect();
... to do more
} catch(err) {
console.log("Err:", err);
}
await redis.disconnect();
req.json({'msg':'success'});
}
The second example is to create redis client every time the request comes.
function createExample2(req, res){
const redis = require("redis").createClient();
try {
await redis.connect();
... to do more
} catch(err) {
console.log("Err:", err);
}
await redis.disconnect();
req.json({'msg':'success'});
}
which method is more reasonable, any idea?

I always create a single connection. I have two reasons:
Redis connections are not free to create. Creating one for each call is significantly slower. Keeping one around makes for faster responses.
Both Node.js and Redis are single-threaded. Given Redis' quick response times, there typically isn't more than one thing at a time in a Node.js process trying to get to Redis. And if there is—say because your code is waiting on a response from a long-running Redis command, the type of commands that should be avoid anyhow—it couldn't do anything anyhow as it would have to wait on the single thread in Redis.
There are certainly exceptions to this, but I think for most cases, this is the way to go. I wouldn't go so far as to say that this is the official stance of Redis, but I work for them as a developer advocate and this is how I build all my sample code.

which method is more reasonable, any idea?
It depends. I would suggest re-using the same single connection to Redis if the code obeys these rules:
it is using non-blocking commands only;
it is not changing the connection state by issuing commands which change it (e.g. SELECT and everything else mentioned here);
it is not using pub/sub in that specific connection (which you may however duplicate just for that, if needed).
In all the other cases, using dedicated connections may be a better option.

Related

Pipeline design question when using lettuce for 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).

Azure service bus multiple instances for the same subscriber

I have a situation where I have an asp.net core application which registers a subscription client to a topic on startup (IHostedService), this subscription client essentially has a dictionary of callbacks that need to be fired whenever it detects a new message in a topic with an id (this id is stored on the message properties). This dictionary lives throughout the lifetime of the application, and is in memory.
Everything works fine on a single instance of the asp.net core app service on azure, as soon as I scale up to 2, I notice that sometimes the callbacks in the subscription are not firing. This makes sense, as we have two instances now, each with its own dictionary store of callbacks.
So I updated the code to check if the id of the subscription exists, if not, abandon message, if yes, get the callback and invoke it.
public async Task HandleMessage(Microsoft.Azure.ServiceBus.Message message, CancellationToken cancellationToken)
{
var queueItem = this.converter.DeserializeItem(message);
var sessionId = // get the session id from the message
if (string.IsNullOrEmpty(sessionId))
{
await this.subscriptionClient.AbandonAsync(message.SystemProperties.LockToken);
return;
}
if (!this.subscriptions.TryGetValue(sessionId, out var subscription))
{
await this.subscriptionClient.AbandonAsync(message.SystemProperties.LockToken);
return;
}
await subscription.Call(queueItem);
// subscription was found and executed. Complete message
await this.subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
However, the problem still occurs. My only guess is that when calling AbandonAsync, the same instance is picking up the message again?
I guess what I am really trying to ask is, if I have multiple instances of a topic subscription client all pointing to the same subscriber for the topic, is it possible for all the instances to get a copy of the message? Or is that not guaranteed.
if I have multiple instances of a topic subscription client all pointing to the same subscriber for the topic, is it possible for all the instances to get a copy of the message? Or is that not guaranteed.
No. If it's the same subscription all clients are pointing to, only one will be receiving that message.
You're running into an issue of scaling out with competing consumers. If you're scaling out, you never know what instance will pick the message. And since your state is local (in memory of each instance), this will fail from time to time. Additional downside is the cost. By fetching messages on the "wrong" instance and abandoning, you're going to pay higher cost on the messaging side.
To address this issue you either need to have a shared/centralized or change your architecture around this.
I managed to solve the issue by making use of service bus sessions. What I was trying to do with the dictionary of callbacks is basically a session manager anyway!
Service bus sessions allow me to have multiple instances of a session client all pointing to the same subscription. However, each instance will only know or care about the sessions it is currently dealing with.

JMSXGroupID/correlation-id to queue messages on stomp client doesn't seem to work

I was trying to queue messages to the same consumer using stomp-js on a node server.
Producer:
producer.send({'JMSXGroupID':JMSXGroupID, 'destination':confMgr.getConfig("jmsqueue.destination"), 'body':JSON.stringify(msg), 'persistent':'true'}, false);
Consumer:
client.on('message', function(message) {
client.ack(message.headers['message-id']);
})
I was sending two messages using the same JMSXGroupID and it seems that the the client processess both the messages in parallel rather than processing message1 and ack'ing it and going ahead to process message2 and then ack'ing message2. I tried using 'correlation-id' and it doesn't seem to work either. Can anyone suggest a better way?
Thank you in advance,
Chandra.
I guess you are using this stomp-js lib (correct me if I'm wrong): https://github.com/benjaminws/stomp-js
Message groups are supported by ActiveMQ using Stomp, so you are most likely getting the messages in order. Processing them in order requires you to somehow process each message synchronously on the client, which is rather simple when you can controll how many threads that the listener will run in. This might not be as easy with java script. which is not
From what I can see, the lib you are using is not the most well documented, the only setting you could tweak that might (I have not tried it!), is to alter the prefetch size to one.
var headers = {
destination: '/queue/test_stomp',
ack: 'client',
'activemq.prefetchSize': '1'
};
It might be the case that this lib still starts eagerly directly to fetch the next message, but you might want to test it.
On the other hand, you might as well want to re design the application to be sequence independent, since you are running node.js and java script. It's always better to have a sequence independence with messaging, since you are able to optimize performance a lot better and can avoid synchronous behaviours.
I don't know what you did try to achieve with correlation id, but that header is used to correlate a request with a reply, which is not the case here.

How to implement ServiceStack Redis Client with timeout

We are implementing a pattern where our client checks to see if a document exists in Redis, and if it does not, we then fetch the data from the database.
We are trying to handle a case where the Redis server is down or unreachable so we can then immediately fetch from the database.
However, when we test our code by intentionally taking down the Redis server, the call to Redis via the ServiceStack client does not timeout for approximately 20 seconds.
We tried using the RedisClient .SendTimeout property to various values (1000, 100, 1), but the timeout always happens after approx 20 seconds. We also tried using the .Ping() method but have the same problem.
Question: how can we handle the scenario where the Redis server is down and we want to switch to a DB fetch more quickly?
I had a similar problem sending e-mail: sometimes there's no answer and the build-in timeout (of SmtpClient) does nothing. Eventually I'd get a timeout which I believe comes from the underlying TCP/IP layer. I'd set the timeout in the client a little shorter than the "brutal timeout" on Task.Wait.
My solution was to wrap the call in a Task, and use a timeout on that:
// this special construct is to set a timeout (the SmtpClient timeout does not seem to work)
var task = Task.Factory.StartNew(() => SendEmail(request));
if (!task.Wait(6000))
Log.Error("Could not send mail to {0}. Timeout (probably on TCP layer).".Fmt(request.To));
Maybe something similar would work for you, just replace the SendEmail with a method that does the Redis thing.
You should not rely on the redis server to tell you how long the request should wait before flipping to plan B. Put this logic in the code actioning the request so that it is independent of how the redis server is set up

Scoping transactions and sessions in NHibernate for long running tasks

When using NHibernate in web applications, I will usually let my IoC container take care of opening and closing an ISession per request and commit/rollback the transaction. The nature of HTTP makes it very easy to define a clear Unit-of-Work in such applications.
Now, I have been tasked with putting together a small program, which will be invoked regularly by a task scheduler, for sending out newsletters. The concepts of both newsletters and subscribers are already well defined entities in our domain model, and sending a newsletter to all subscribers would involve doing something similar to this:
var subscribers = _session
.QueryOver<Subscription>()
.Where(s => !s.HasReceivedNewsletter)
.List();
foreach (var subscriber in subscribers)
{
SendNewsletterTo(subscriber);
subscriber.HasReceivedNewsletter = true;
}
Notice how each Subscriber object is updated within the loop, recording that she has now received the newsletter. The idea is, that if the mail sending program should crash, it can be restarted and continue sending newsletters from where it left off.
The problem I am facing, is in defining and implementing the Unit-of-Work pattern here. I will probably need to commit changes to the database by the end of each iteration of the loop. Simply wrapping the loop body with a using (var trans = _session.BeginTransaction()) block seems to be extremely expensive in running time, and I also seem to experience locking issues between this long running process and other (web) applications using the same database.
After reading some articles and documentation on NHibernate transactions, I have come to think, that I might need to detach the list of subscribers from the session to avoid the locking issues, and reattach each to a fresh session in the loop body. I am not sure how this will work for performance, though.
So, NHibernate experts, how would you design and implement a long running job like this?
Don't you want to use asynchronous durable messaging here? Something like NServiceBus, Rhino Service Bus or MassTransit. It seems you don't have to send a lot of messages as soon as possible, so I think you should do it asynchronously with 1 durable message per user basis
Don't you think that Stateless session with no transaction will do better here?
There's no problem having multiple transactions in a session. It's appropriate here to scope the transaction to updating a single subscriber because it's an independent operation. Depending on the number of subscribers and the likelihood of failure, it might be best to grab a small number of subscribers at a time.
foreach (var subscriber in subscribers)
{
using (var txn = _session.BeginTransaction())
{
try
{
SendNewsletterTo(subscriber);
subscriber.HasReceivedNewsletter = true;
txn.Commit();
}
catch (Exception ex)
{
txn.Rollback();
// log exception, clean up any actions SendNewsletterTo has taken if needed
// Dispose of session and start over
}
}
}