Redis connection refused through ruby daemon worker on Heroku - ruby-on-rails

A Rails 3.2.6 application running as a 'web' Heroku process is connecting to Redis using the ENV["REDISTOGO_URL"] environment variable:
irb(main):002:0> Redis.current
=> #<Redis client v2.2.2 connected to redis://xxx.redistogo.com:1234/0 (Redis v2.4.11)>
----- /initializers/redis.rb
if Rails.env.development?
Redis.current = Redis.new
elsif Rails.env.test?
Redis.current = Redis.new
elsif Rails.env.production?
uri = URI.parse(ENV["REDISTOGO_URL"])
Redis.current = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
end
A ruby daemon 'streaming' process runs as a secondary worker:
----- Procfile
web: bundle exec rails server thin -p $PORT -e $RACK_ENV
worker: bundle exec rake jobs:work
streaming: RAILS_ENV=production ruby bin/streaming.rb start
However, the streaming process is crashing when it calls methods in the main Rails application that connect with Redis - even though the streaming process supposed to be loading the same redis.rb initializer as the 'web' Rails application process.
----- /bin/streaming_ctl.rb
# encoding: UTF-8
require "rubygems"
require "bundler/setup"
require "daemons"
Daemons.run(File.expand_path("../streaming.rb", __FILE__))
----- /bin/streaming.rb
# encoding: UTF-8
# TODO: set rails env in init script
ENV["RAILS_ENV"] ||= "production"
# load rails environment
require File.expand_path('../../config/environment', __FILE__)
logger = ActiveSupport::BufferedLogger.new(File.expand_path("./../../log/streaming.log", __FILE__))
Streaming.start
logger.info("\nStarting streaming in #{Rails.env.to_s} mode.")
Why is the streaming process/worker using the default Redis host and port?
[streaming.1]: /app/vendor/bundle/ruby/1.9.1/gems/redis-2.2.2/lib/redis/client.rb:236:in `rescue in establish_connection': Connection refused - Unable to connect to Redis on 127.0.0.1:6379 (Errno::ECONNREFUSED)

Switching from "Redis.current" in /initializer/redis.rb and using a "$redis" variable convention instead (as illustrated here http://jimneath.org/2011/03/24/using-redis-with-ruby-on-rails.html) fixed the connection problem.
The app was using redis gem version 2.2.2 at the time. It appears as though the Redis.current in this gem version won't be consistent across 'web' and 'worker' processes because Heroku runs these processes on separate threads. The documentation on the gem repo suggests that updating to gem to >= version 3 will enable Redis.current to run in multi-threaded environments:
https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300

Related

Background jobs with Resque on Heroku

I am having a really strange problem on Heroku that I have been spinning my wheels to figure out for a while now.
My app has a few external API calls and mailers which I have set up ActiveJob to run in the background. On Heroku I have two workers set up as and I am using a Resque/Redis combo for the jobs as per the below snippets. I am using the Redis Cloud add-on on Heroku.
Config / setup
Procfile
web: bundle exec puma -C config/puma.rb
resque: env TERM_CHILD=1 QUEUE=* bundle exec rake resque:work
lib/tasks/resque.rake
require "resque/tasks"
require "resque/scheduler/tasks"
task "resque:setup": :environment do
Resque.before_fork = proc { ActiveRecord::Base.connection.disconnect! }
Resque.after_fork = proc { ActiveRecord::Base.establish_connection }
end
config/initializers/active_job.rb
Rails.application.config.active_job.queue_adapter = :resque
config/initializers/redis.rb
if ENV["REDISCLOUD_URL"]
$redis = Redis.new(url: ENV["REDISCLOUD_URL"])
end
config/initializers/resque.rb
if Rails.env.production?
uri = URI.parse ENV["REDISCLOUD_URL"]
Resque.redis = Redis.new(host: uri.host, port: uri.port,
password: uri.password)
else
Resque.redis = "localhost:6379"
end
The problem
The problem I am having is when a user is using the app in browser (i.e., interfacing with the web worker) and performs an action which triggers one of the ActiveJob jobs the job is run "inline" using the web worker and not the resque worker. When I run the specific model method that queues the job in my Heroku app console (opened by running heroku run rails console) it adds the job to Redis and runs it using the resque worker as expected.
Why would one way work properly and the other way not work? I have looked at almost every tutorial / SO question on the topic and have tried everything so any help getting the jobs to be run but the right worker would be amazing!
Thanks in advance!
I managed to solve the problem by playing with my config a little. It seems that actions were being tunnelled through ActiveJob's "Inline" default rather than via Resque. To get things working I just had to direct Resque.redis to be equal to the $redis variable set in config/initializers/redis.rb so everything was pointing to the same Redis instance and then move the config set in config/initializers/active_job.rb to application.rb.
For reference, the new & improved config that all works is:
Config / setup
Procfile
web: bundle exec puma -C config/puma.rb
resque: env TERM_CHILD=1 RESQUE_TERM_TIMEOUT=7 QUEUE=* bundle exec rake resque:work
lib/tasks/resque.rake
require "resque/tasks"
task "resque:setup" => :environment
config/application.rb
module App
class Application < Rails::Application
...
# Set Resque as ActiveJob queue adapter.
config.active_job.queue_adapter = :resque
end
end
config/initializers/redis.rb
if ENV["REDISCLOUD_URL"]
$redis = Redis.new(url: ENV["REDISCLOUD_URL"])
end
config/initializers/resque.rb
Resque.redis = Rails.env.production? ? $redis : "localhost:6379"
thanks a lot for providing the answer. It saved me a lot of time.
You have one typo inside your Procfile.
It should be resque instead of rescue.
resque: env TERM_CHILD=1 RESQUE_TERM_TIMEOUT=7 QUEUE=* bundle exec rake resque:work
Also, I had to type in one more command to get this all to work in production. Hopefully this helps someone.
heroku ps:scale resque=1 --app appname
This command scales the resque process to 1 dyno(free). You can also do this from the dashboard on heroku.
You can read more about it on the heroku docs https://devcenter.heroku.com/articles/scaling

How to change the default binding ip of Rails 4.2 development server?

After upgrading our team's rails application to 4.2, as the release note mentioned, the default ip rails server binds to is changed to localhost from 0.0.0.0.
We develop with Vagrant, and want the development server to be accessible directly from browser on the host machine.
Instead of typing rails s -b 0.0.0.0 every time from now on, I wonder if there's any more elegant solution, so that we can still use sth as simple as rails s to start the server. Perhaps:
a config file rails s reads where I can modify the default binding ip (without using -c)
port forward with vagrant (tried but failed, see problem encountered below)
a monkey patch to rack, that changes the default binding ip
The real goal behind this is that I want the upgrade to be smooth among our team, avoiding the glitch that people will have to constantly restarting their rails server due to the missing -b 0.0.0.0 part.
I tried vagrant port forwarding, but still get Connection Refused when I visit localhost:3000 on the host machine. The two configuration lines I tried was:
config.vm.network "forwarded_port", guest: 3000, host: 3000
config.vm.network "forwarded_port", guest: 3000, guest_ip: '127.0.0.1', host: 3000
Didn't find any relevant instructions in the official docs. Any help will be appreciated.
I'm having the same issue here and I found today a better solution. Just append this code to your config/boot.rb and it should work with vagrant.
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0', Port: 3000)
end
end
end
ps: Its based on: this answer
You can use foreman to run a Procfile with your custom commands:
# Procfile in Rails application root
web: bundle exec rails s -b 0.0.0.0
Now start your Rails application with:
foreman start
The good thing about foreman is that you can add other applications to the Procfile (like sidekiq, mailcatcher).
The bad thing about foreman is that you have to train your team to run foreman start instead of rails s.
Met the same problem. Found the blog Make Rails 4.2 server listens to all interfaces.
Add the following to config/boot.rb
require 'rails/commands/server'
module Rails
class Server
alias :default_options_bk :default_options
def default_options
default_options_bk.merge!(Host: '0.0.0.0')
end
end
end
For Rails 5.1.7 with Puma 3.12.1 the selected answer does not work, but I accomplished it by adding the following to my config/puma.rb file:
set_default_host '0.0.0.0' # Note: Must come BEFORE defining the port
port ENV.fetch('PORT') { 3000 }
I determined this by inspecting the dsl file. It uses instance_eval on that file, so there are probably other ways to do it, but this seemed the most reasonable to me.
If you put the default options on config/boot.rb then all command attributes for rake and rails fails (example: rake -T or rails g model user)! So, append this to bin/rails after line require_relative '../config/boot' and the code is executed only for the rails server command:
if ARGV.first == 's' || ARGV.first == 'server'
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0', Port: 3000)
end
end
end
end
The bin/rails file loks like this:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
# Set default host and port to rails server
if ARGV.first == 's' || ARGV.first == 'server'
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0', Port: 3000)
end
end
end
end
require 'rails/commands'
If you use docker or another tool to manage the environment variables, you can set the HOST environment variable to the IP you need to bind.
Example:
HOST=0.0.0.0
Add it to docker.env file if you use Docker or .env if you use foreman.
Here's a simpler solution that I'm using. I already like/need dotenv and puma-heroku, so if using those doesn't work for you then this might not be for you.
/config/puma.rb
plugin :heroku
Gemfile
gem 'dotenv-rails', groups: [:development, :test]
.env
PORT=8080
Now I can start both dev and production with rails s.
For Rails 5 with Puma the selected answer does not work. You may get such error: cannot load such file -- rails/commands/server
For proper solution add following to config/puma.rb:
bind 'tcp://0.0.0.0:3000'
Switch to Puma and specify port in config/puma.rb, e.g.:
port ENV.fetch("PORT") { 3000 }
Apparently it will bind to 0.0.0.0 for the specified port: https://github.com/puma/puma/issues/896

Redis to Go - Heroku - Rails

I'm trying to get Sidekiq playing in Heroku. Without Luck.. My configs look like that:
Procfile
web: bundle exec passenger start -p $PORT --max-pool-size 5
worker: bundle exec sidekiq
Initializers/redis.rb
uri = URI.parse(ENV["REDISTOGO_URL"] || "redis://localhost:6379/")
REDIS = Redis.new(:url => ENV['REDISTOGO_URL'])
but when i do heroku ps only the web instance is shown. Not Sidekiq.
However i can manually run heroku run sidekiq and run my workers. What am i missing so that Heroku doesn't start that on it's own ?
The problem is that you didn't configure Sidekiq in the initializer so that Sidekiq knows exactly how to connect to redis. Create Initializers/sidekiq.rb and add the following code
Sidekiq.configure_server do |config|
config.redis = { :url => ENV["REDISTOGO_URL"] }
end
Also you can remove
uri = URI.parse(ENV["REDISTOGO_URL"] || "redis://localhost:6379/")
You don't need it as you are using the redis url from the environment.
Don't forget to restart your server.

Want to use Redis on Heroku (Redis::CannotConnectError ( Error connecting to Redis on 127.0.0.1:6379 (ECONNREFUSED)) )

I want to use Redis on Heroku but I got this error Redis::CannotConnectError (Error connecting to Redis on 127.0.0.1:6379 (ECONNREFUSED)) .
I checked these , but didn't get helped.
Redis connection to 127.0.0.1:6379 failed - connect ECONNREFUSED, deploying redis to heroku unable to connect, How to get Redis to start on Heroku? .
I use Ruby 2.0.0p247 and Rails4. I'm using puma.
I use RedisToGo(nano) and, in /config/initializers/redis.rb I write this.
uri = URI.parse(ENV["REDISTOGO_URL"] || "redis://localhost:6379/" )
$redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
I checked "REDISTOGO_URL" is the same URL that I can see when $ heroku config .
I did Redis restart on Heroku GUI, but it doesn't work.
Please tell me anything I need to do.
Thank you for your help.
Check which ENV variable should contain redis configuration
For example if you are going to use Redis for Sidekiq you need to map ENV["REDISTOGO_URL"] to ENV["REDIS_PROVIDER"]
because Sidekiq is expecting to get redis configuration from ENV["REDIS_PROVIDER"]
You can do it this way
heroku config:set REDIS_PROVIDER=REDISTOGO_URL
Also you don't need to URI.parse, Redis will do it instead of you in case it is needed.
$redis = Redis.new(url: ENV['REDISTOGO_URL' || "redis://localhost:6379/"])
I suggest to use the gem figaro to set ENV variables for your local development. Thus you remove OR statement
$redis = Redis.new(url: ENV['REDISTOGO_URL'])
and set alternative value in your config/application.yml
REDISTOGO_URL: redis://127.0.0.1:6379
You're trying to connect to your own computer (note 127.0.0.1 == localhost). I'm guessing that's not the Redis server you're looking for :)
Looks like ENV["REDISTOGO_URL"] isn't properly set.
Also, just a side note, look up Redis To Go if you haven't already, which is what most people use as a Redis server in conjunction with Heroku.

How to change default port of a Rails 4 app?

I know that I can start a rails server on another port via -p option. But I'd like to setup another port per application as long as I start webrick.
Any ideas?
Regards
Felix
Append this to config/boot.rb:
require 'rails/commands/server'
module DefaultOptions
def default_options
super.merge!(Port: 3001)
end
end
Rails::Server.send(:prepend, DefaultOptions)
Note: ruby >= 2.0 required.
Quick solution: Append to Rakefile
task :server do
`bundle exec rails s -p 8080`
end
Then run rake server
Option 1:
You can launch WEBrick like so:
rails server -p 8080
Where 8080 is your port. If you like, you can throw this in a bash script for convenience.
Option 2:
You could install $ gem install foreman, and use foreman to start your production webserver (e.g. unicorn) as defined in your Procfile like so: $ foreman run web. If unicorn is your web server you can specify the port in your unicorn config file (as with most server choices). The benefit of this approach is not only can you set the port in the config, but you're using an environment which is closer to production.
If you put the default options on config/boot.rb then all command attributes for rake and rails fails (example: rake -T or rails g model user)! So, append this to bin/rails after line require_relative '../config/boot' and the code is executed only for the rails server command:
if ARGV.first == 's' || ARGV.first == 'server'
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0', Port: 3000)
end
end
end
end
The bin/rails file loks like this:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
# Set default host and port to rails server
if ARGV.first == 's' || ARGV.first == 'server'
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0', Port: 3000)
end
end
end
end
require 'rails/commands'
For Rails 5.1:
# config/boot.rb
# ... existing code
require 'rails/command'
require 'rails/commands/server/server_command'
Rails::Command::ServerCommand.send(:remove_const, 'DEFAULT_PORT')
Rails::Command::ServerCommand.const_set('DEFAULT_PORT', 3333)

Resources