38

I am using redis as a read cache. I have created an initializer

config/initializer/redis.rb

$redis = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

I am using this global in my unicorn.rb to create a new connection whenever a new worker is created.

before_fork do |server, worker|
  # clear redis connection
  $redis.quit unless $redis.blank?
end

# Give each child process its own Redis connection
after_fork do |server, worker|
  $redis = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])
end

I am also using this global variable whenever I need to access my redis servers. But I am not comfortable using this global variable. Are there any better options than using global variable?

Anirudhan J
  • 2,072
  • 6
  • 27
  • 45

6 Answers6

99

There is Redis.current, which you can use to store your one-and-only Redis instance.

So instead of using $redis, you can assign your instance as follows:

Redis.current = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

Redis.current was introduced to redis-rb in 2010 as a standard way to grab a redis connection, so I was surprised that no other answer mentioned it.

Update: Starting with version 4.6.0 Redis.current has been deprecated. The author notes that typical multi-threaded applications will find a lot of locking around a shared redis client. They recommend to define an own place to get a redis client, but also suggest to use a connection pool.

The accepted answer is therefore the simplest solution to achieve something comparable to Redis.current, but may not perform optimal in multi-threaded environments.

NobodysNightmare
  • 2,992
  • 2
  • 22
  • 33
32

expanding further on mestachs suggestion, namespacing a module in your initializer as below

config/initializers/redis.rb

module ReadCache
  class << self
    def redis
      @redis ||= Redis.new(:url => (ENV["REDIS_URL"] || 'redis://127.0.0.1:6379'))
    end
  end
end

then in unicorn.rb

 before_fork do |server, worker|
    ...
   if defined?(ReadCache.redis)
    ReadCache.redis.quit
   end
    ...
 end

 after_fork do |server, worker|
    ...
   if defined?(ReadCache.redis)
    ReadCache.redis.client.reconnect
   end
    ...
 end
amit_saxena
  • 7,450
  • 5
  • 49
  • 64
blotto
  • 3,387
  • 1
  • 19
  • 19
  • So, if I have thousand unique requests simultaneously, I would also need thousand connexions to the redis server, right? – yeyo Feb 13 '14 at 01:11
  • @Kira Can you explain a bit more. I dont think it will need thousand connections. If I understand correctly, This module will be common to the whole app. – Anirudhan J Feb 14 '14 at 14:28
  • 1
    this article https://devcenter.heroku.com/articles/concurrency-and-database-connections helps alot with understanding connection pooling weather or not your using Heroku. with a bit of understanding from that you can them tune your sidekiq concurrency see: https://github.com/mperham/sidekiq/wiki/Advanced-Options#wiki-concurrency. – blotto Feb 14 '14 at 18:03
  • @blotto that was a nice reading, thank you. Although, your sample code it's not using ActiveRecord (correct me if I'm wrong), so the pooling technique it's not being used and still we have a redis connection per client/worker. – yeyo Feb 16 '14 at 03:44
  • @Kira, it really depends on your setup. my example presumes ActiveRecord (Rails), and a forking server such as Unicorn. there would actually be more than one connection per dyno/worker. Calculating concurrency, and ultimately defining such settings, is a balance between your allowed db connections, and how much you want to scale. – blotto Feb 16 '14 at 05:32
  • 1
    Use `Redis.current` as detailed by @NobodysNightmare below. – Paul Danelli Nov 22 '18 at 17:19
  • since it is still the accepted answer, cannot delete this post in order to promote the more accurate answer below. – blotto Nov 29 '18 at 00:24
0

if you don't already use another Rails.cache I advise you to just use that mechanism with redis.

The gem redis-store makes this realy easy (https://github.com/redis-store/redis-store)

This way you can just do Rails.cache.reconnect and all is dandy

https://github.com/redis-store/redis-store/issues/21#issuecomment-948569

It also allows you to use the awesome Rails.cache API, which has some neat features: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html

Sascha Kaestle
  • 1,293
  • 12
  • 15
0

A more namespaced option to replace your global variable, you can create a method in a module

module Caching
  def self.redis
    ... initialize/memoize/reconnect here...
  end
end

You than then call it with:

Caching.redis

mestachs
  • 1,889
  • 15
  • 17
0

try this out:-

you can use constant instead of global variable.like in config/initializer/redis.rb

REDIS = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

and in unicorn.rb

before_fork do |server, worker|
  # clear redis connection
  REDIS.quit if defined?(REDIS)
end

# Give each child process its own Redis connection

after_fork do |server, worker|
  REDIS ||= Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])
end
Sachin Singh
  • 7,107
  • 6
  • 40
  • 80
  • 1
    sounds interesting, what is the role of this constant? I mean, it's not a global variable but it behaves similar as if it were a global variable. – yeyo Feb 13 '14 at 01:05
0

According to this Heroku, you don't need to add $redis to your Unicorn:

No special setup is required when using Redis Cloud with a Unicorn server. Users running Rails apps on Unicorn should follow the instructions in the Configuring Redis from Rails section and users...

Here's all the "Configuring Redis from Rails section" has for before Rails 4 (besides the Gemfile and some other pre-Rails 3 stuff):

# config/initalizers/redis.rb

if ENV["REDISCLOUD_URL"]
  uri = URI.parse(ENV["REDISCLOUD_URL"])
  $redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
end

It doesn't really give an explanation as to why "no special setup is required".

gabe
  • 1,873
  • 2
  • 20
  • 36
  • 1
    I know that you do need to do something special. I have maxed out 512 connection redis cloud instances on production heroku servers using unicorn. – Aeramor Sep 25 '14 at 23:42