Handle Redis requirement in project a bit more elegant? - ruby-on-rails

Im using redis in on of my projects and have a initialiser with redis = Redis.new and using the redis gem. Problem is that if Redis is not running I cannot do anything like a simple database migration.
Is there a more elegant way to handle using redis so that my application throws an error instead of just not working when redis is not running?
Im using process monitoring to keep redis running correctly and monitor its memory/cpu but still if it does not run all things break and look for a more elegant way. if possible
EDit:
This is my initialiser
$redis = Redis.new
heartbeat_thread = Thread.new do
while true
$redis.publish("heartbeat","thump")
sleep 30.seconds
end
end
at_exit do
# not sure this is needed, but just in case
heartbeat_thread.kill
$redis.quit
end

Why do you create a connection to redis before doing any request ?
It seems that you dont need that and I suggest you do connect to redis only when you actually have to send some request to it.
I am not very familiar with redis ruby client but I am pretty sure you can achieve this easily.

Related

Getting Internal Error when trying Resque in Heroku

I have followed the following tutorial: https://devcenter.heroku.com/articles/queuing-ruby-resque and it doesn't state anything about AUTH with Redis.
When I try to open the resque-web to manage the workers, I get an Internal Error, also when trying this, inside the Heroku rails console:
irb(main):001:0> Resque.queues
I get this:
Redis::CannotConnectError: Error connecting to Redis on 127.0.0.1:6379 (ECONNREFUSED)
So, I am wondering what should I do to make this work correctly. DO I need to set up any kind of auth? If so, where? I am using Redis Cloud addon.
Thanks
It looks like you're not initializing resque. To do so, you need something like the following:
# config/initiazlizers/resque.rb
uri = URI.parse ENV["REDISCLOUD_URL"]
Resque.redis = Redis.new host:uri.host, port:uri.port, password:uri.password
This initializer reads your URL from the Heroku environment variable that was set by the rediscould add-on, parses it, and passes it to Resque so it connects and authenticates with the redis server.

Redis with rails. You need to reconnect to Redis after forking error

I am using Redis with my Rails app. I have sidekiq gem installed too. My redis server runs in the same machine in default port.
I created a initializer which initalizes a redis lient.
config/initializers/redis.rb
$redis = Redis.new(:host => 'localhost', :port => 6379)
I have another initalizer that sets the number of accounts currently active in the system.
config/initializers/z_account_list.rb
$redis.set('accounts',Account.count);
In one of my views i am using this piece of code.
<div class="more-text">and <%= "#{$redis.get('accounts')}" %> more...</div>
When i set the value for accounts manually in redis without using the initializer, everything works fine. But when i add the initializer, i get
ActionView::Template::Error (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.):
I searched for the error. But most solutions are for resque and has something to do with after_fork. Since i am new to Rails and Redis and since i am not using Resque i am getting a little confused. Please help me out on this.
In forked environments like pushion passenger, we have to reconnect to redis whenever a worker is forked. My biggest confusion was where to put the reconnection statements. In many blogs it was suggested to put it in config/environments.rb. But it didn't work for me.
I added
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
if forked
$redis.client.disconnect
$redis = Redis.new(:host => 'localhost', :port => 6379)
Rails.logger.info "Reconnecting to redis"
else
# We're in conservative spawning mode. We don't need to do anything.
end
end
end
to config/initializers/redis.rb
and everything started working fine.

How to detect if a rails app is running under Unicorn?

I need to setup a connection to an external service in my Rails app. I do this in an initializer. The problem is that the service library uses threaded delivery (which I need, because I can't have it bogging down requests), but the Unicorn life cycle causes the thread to be killed and the workers never see it. One solution is to invoke a new connection on every request, but that is unnecessarily wasteful.
The optimal solution is to setup the up the connection in an after_fork block in the unicorn config. The problem there is that doesn't get invoked outside of unicorn, which means we can't test it in development/testing environments.
So the question is, what is the best way to determine whether a Rails app is running under Unicorn (either master or worker process)?
There is an environment variable that is accessible in Rails (I know it exists in 3.0 and 3.1), check the value of env['SERVER_SOFTWARE']. You could just put a regex or string compare against that value to determine what server you are running under.
I have a template in my admin that goes through the env variable and spits out its content.
Unicorn 4.0.1
env['SERVER_SOFTWARE'] => "Unicorn 4.0.1"
rails server (webrick)
env['SERVER_SOFTWARE'] => "WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30)"
You can check for defined?(Unicorn) and in your Gemfile set: gem :unicorn, require: false
In fact you don't need Unicorn library loaded in you rails application.
Server is started by unicorn command from shell
Checking for Unicorn constant seems a good solution, BUT it depends very much on whether require: false is provided in the Gemfile. If it isn't (which is quite probable), the check might give a false positive.
I've solved it in a very straightforward manner:
# `config/unicorn.rb` (or alike):
ENV["UNICORN"] = 1
...
# `config/environments/development.rb` (or alike):
...
# Log to stdout if Web server is Unicorn.
if ENV["UNICORN"].to_i > 0
config.logger = Logger.new(STDOUT)
end
Cheers!
You could check to see if the Unicorn module has been defined with Object.constants.include?('Unicorn').
This is very specific to Unicorn, of course. A more general approach would be to have a method which sets up your connection and remembers it's already done so. If it gets called multiple times, it just returns doing nothing on subsequent calls. Then you call the method in after_fork and in a before_filter in your application controller. If it's been run in the after_fork it does nothing in the before_filter, if it hasn't been run yet it does its thing on the first request and nothing on subsequent requests.
Inside config/unicorn.rb
Define ENV variable as
ENV['RAILS_STDOUT_LOG']='1'
worker_processes 3
timeout 90
and then this variable ENV['RAILS_STDOUT_LOG'] will be accessible anywhere in your Rails app worker thread.
my issue:
I wanted to output all the logs(SQL queries) when on the Unicorn workers and not on any other workers on Heroku, so what I did is adding env variable in the unicorn configuration file
If you use unicorn_rails, below code will help
defined?(::Unicorn::Launcher)

Can you use Redis alongside Resque?

I'm using Redis To Go on Heroku, and I'd like to use it to store some data alongside Resque jobs, much like I can store DelayedJobs in the same Postgres database as the rest of my model data.
Is this possible? Care to explain briefly how Resque stores jobs in Redis? I'm new to Redis, and though I understand on a higher level how it works, I'm unfamiliar with the implementation details.
Yes, you can. According to the Resque documentation:
Resque has a redis setter which can be given a string or a Redis object. This means if you're already using Redis in your app, Resque can re-use the existing connection.
Also, the documentation for Resque.redis= states that a namespace is accepted, to prevent clashes with your other apps running on the same instance:
A 'hostname:port/namespace' String (to set the Redis namespace)
Check out the "Configuration" section of the Resque readme for more information: https://github.com/resque/resque/blob/master/README.markdown
Redis is an advanced key value store which is used by Resque to store data about jobs. Exact details depend on your application however the two should be fine working together. As long as your code stays away from using the lists Resque uses then you will be golden.
yes, completely agree with other answers, the Redis can be used for other purposes, not only for background processing. By the way, Rescue provides easy interface to setup/config/use/browse Redis.
Setup/Run
$ git clone --depth=1 git://github.com/defunkt/resque.git
$ cd resque
$ rake redis:install dtach:install
$ vim config/resque.yml
"development: localhost:6379"
$ vim config/initializers/rescue.rb
"rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
resque_config = YAML.load_file(rails_root + '/config/resque.yml')
Resque.redis = resque_config[rails_env]"
$ rake redis:start
Use
# model
def user_activity
a = Resque.redis.lrange "log_" + self.id.to_s, 0, -1
a.map{|i| JSON.parse(i)}
end
Browse
$ resque-web
It allows you to browse Redis content, not only background processing

Is a redis instance can only work for 1 project?

I'm using Rails with redis.
From the introduction of Redis, I found such information:
start redis server:
redis-server
use redis client:
> redis-cli
redis> set key value
OK
redis> get key
"value"
From the sample, I have a question:
Is a redis instance can only work for 1 project? You can see, there is no "database" or "collection" or similar things. If two different projects use the same redis, they may change the same key to invalid value.
So, do I need to create different instances with different ports for different Rails projects?
Keep in mind that redis also has databases (16 of them if I remember correctly) – they're just not named, they're numbers. So for example, if you're using redis-rb to connect, you'll get a snippet like this:
$redis = Redis.new(:host => 'localhost', :port => 6379, :db => 5)
That'll connect to database 5. I use this a lot to run tests as well so my tests don't interfere with my development database.
If sharing a single Redis instance (or cluster) between two or more applications then you should probably namespace your keys to partition them rationally between those applications for the reasons you've observed. Take a look at the redis-namespace gem which provides a nice Ruby interface for doing this.

Resources