Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to use Redis in a Multi-threaded Rails environment? (Puma / Sidekiq)

I'm using Redis in my application, both for Sidekiq queues, and for model caching.

What is the best way to have a Redis connection available to my models, considering that the models that will be hitting Redis will be called both from my Web application (ran via Puma), and from background jobs inside Sidekiq?

I'm currently doing this in my initializers:

Redis.current = Redis.new(host: 'localhost', port: 6379) 

And then simply use Redis.current.get / Redis.current.set (and similar) throughout the code...

This should be thread-safe, as far as I understand, since the Redis Client only runs one command at a time, using a Monitor.

Now, Sidekiq has its own connection pool to Redis, and recommends doing

Sidekiq.redis do |conn|    conn.get    conn.set end 

As I understand it, this would be better than the approach of just using Redis.current because you don't have multiple workers on multiple threads waiting on each other on a single connection when they hit Redis.

However, how can I make this connection that I get from Sidekiq.redis available to my models? (without having to pass it around as a parameter in every method call)

I can't set Redis.current inside that block, since it's global, and I'm back to everyone using the same connection (plus switching between them randomly, which might even be non-thread-safe)

Should I store the connection that I get from Sidekiq.Redis into a Thread-local variable, and use that thread-local variable everywhere?

In that case, what do I do in the "Puma" context? How do I set the thread-local variable?

Any thoughts on this are greatly appreciated.

Thank you!

like image 548
Daniel Magliola Avatar asked Jan 23 '15 16:01

Daniel Magliola


People also ask

How many Redis connections does Sidekiq use?

Note: Sidekiq needs 2 connections by default for a bunch of stuff like a heartbeat, maintain sentinel, etc. Read here. That's why if you have max threads or concurrency of for ex. 5 then you need to have at least 7 connections in your pool.

What is Redis used for in rails?

There are multiple reasons to use Redis in Ruby on Rails application. First of all, Redis offers the most popular in-memory data store. Data kept in the memory, as opposed to tools that write to disks, is going to be read and written faster.

How use Redis server in Rails?

Using Redis as a Rails Cache Since Rails 5.2 you can use Redis as your cache store. You only need the redis gem & server. Then Rails will use Redis for all its caching needs.

What is Redis connection pool?

Connection pooling means that connections are reused rather than created each time when the connection is requested. To facilitate connection reuse, a memory cache of database connections, called a connection pool, is maintained by a connection pooling module as a layer.


1 Answers

You use a separate global connection pool for your application code. Put something like this in your redis.rb initializer:

require 'connection_pool' REDIS = ConnectionPool.new(size: 10) { Redis.new } 

Now in your application code anywhere, you can do this:

REDIS.with do |conn|   # some redis operations end 

You'll have up to 10 connections to share amongst your puma/sidekiq workers. This will lead to better performance since, as you correctly note, you won't have all the threads fighting over a single Redis connection.

All of this is documented here: https://github.com/mperham/sidekiq/wiki/Advanced-Options#connection-pooling

like image 181
Mike Perham Avatar answered Oct 02 '22 12:10

Mike Perham