How to tell if sidekiq is connected to redis server? - ruby-on-rails

Using the console, how can I tell if sidekiq is connected to a redis server? I want to be able to do something like this:
if (sidekiq is connected to redis) # psuedo code
MrWorker.perform_async('do_work', user.id)
else
MrWorker.new.perform('do_work', user.id)
end

You can use Redis info provided by Sidekiq:
redis_info = Sidekiq.redis { |conn| conn.info }
redis_info['connected_clients'] # => "16"
Took it from Sidekiq's Sinatra status app.

I make this method to Rails whit the obove answer, return true if connected and false if not.
def redis_connected?
!!Sidekiq.redis(&:info) rescue false
end

It sounds like you want to know if there is a Sidekiq process up and running to process jobs at a given point in time. With Sidekiq 3.0, you can do this:
require 'sidekiq/api'
ps = Sidekiq::ProcessSet.new
if ps.size > 0
MyWorker.perform_async(1,2,3)
else
MyWorker.new.perform(1,2,3)
end
Sidekiq::ProcessSet gives you almost real-time (updated every 5 sec) info about any running Sidekiq processes.

jumping off #overallduka's answer, for those using the okcomputer gem, this is the custom check i set up:
class SidekiqCheck < OkComputer::Check
def check
if sidekiq_accessible?
mark_message "ok"
else
mark_failure
end
end
private
def sidekiq_accessible?
begin
Sidekiq.redis { |conn| conn.info }
rescue Redis::CannotConnectError
end.present?
end
end
OkComputer::Registry.register "sidekiq", SidekiqCheck.new

begin
MrWorker.perform_async('do_work', user.id)
rescue Redis::CannotConnectError => e
MrWorker.new.perform('do_work', user.id)
end

Related

Sidekiq logs show JobWrapper instead of Job class name

I have a Rails application that runs some background jobs via ActiveJob and Sidekiq. The sidekiq logs in both the terminal and the log file show the following:
2016-10-18T06:17:01.911Z 3252 TID-oukzs4q3k ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-97318b38b1391672d21feb93 INFO: start
Is there some way to show the class names of the jobs here similar to how logs work for a regular Sidekiq Worker?
Update:
Here is how a Sidekiq worker logs:
2016-10-18T11:05:39.690Z 13678 TID-or4o9w2o4 ClientJob JID-b3c71c9c63fe0c6d29fd2f21 INFO: start
Update 2:
My sidekiq version is 3.4.2
I'd like to replace ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper with Client Job
So I managed to do this by removing Sidekiq::Middleware::Server::Logging from the middleware configuration and adding a modified class that displays the arguments in the logs. The arguments themself contain the job and action names as well.
For latest version, currently 4.2.3, in sidekiq.rb
require 'sidekiq'
require 'sidekiq/middleware/server/logging'
class ParamsLogging < Sidekiq::Middleware::Server::Logging
def log_context(worker, item)
klass = item['wrapped'.freeze] || worker.class.to_s
"#{klass} (#{item['args'].try(:join, ' ')}) JID-#{item['jid'.freeze]}"
end
end
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.remove Sidekiq::Middleware::Server::Logging
chain.add ParamsLogging
end
end
For version 3.4.2, or similar, override the call method instead:
class ParamsLogging < Sidekiq::Middleware::Server::Logging
def call(worker, item, queue)
klass = item['wrapped'.freeze] || worker.class.to_s
Sidekiq::Logging.with_context("#{klass} (#{item['args'].try(:join, ' ')}) JID-#{item['jid'.freeze]}") do
begin
start = Time.now
logger.info { "start" }
yield
logger.info { "done: #{elapsed(start)} sec" }
rescue Exception
logger.info { "fail: #{elapsed(start)} sec" }
raise
end
end
end
end
You must be running some ancient version. Upgrade.
Sorry, looks like that's a Rails 5+ feature only. You'll need to upgrade Rails. https://github.com/rails/rails/commit/8d2b1406bc201d8705e931b6f043441930f2e8ac

How to run background jobs during cucumber tests?

What is the best way to test something that requires background jobs with Cucumber? I need to run DelayedJob and Sneakers workers in background while tests are running.
You can run any application in the background:
#pid = Process.spawn "C:/Apps/whatever.exe"
Process.detach(#pid)
And even kill it after tests are done:
Process.kill('KILL', #pid) unless #pid.nil?
You can create your own step definition in features/step_definitions/whatever_steps.rb (hopefully with a better name)
When /^I wait for background jobs to complete$/ do
Delayed::Worker.new.work_off
end
That can be extended for any other scripts you'd like to run with that step. Then in the test, it goes something like:
Then I should see the text "..."
When I wait for background jobs to complete
And I refresh the page
Then I should see the text "..."
If anyone has similar problem I ended up writing this (thanks to Square blog post):
require "timeout"
class CucumberExternalWorker
attr_accessor :worker_pid, :start_command
def initialize(start_command)
raise ArgumentError, "start_command was expected" if start_command.nil?
self.start_command = start_command
end
def start
puts "Trying to start #{start_command}..."
self.worker_pid = fork do
start_child
end
at_exit do
stop_child
end
end
private
def start_child
exec({ "RAILS_ENV" => Rails.env }, start_command)
end
def stop_child
puts "Trying to stop #{start_command}, pid: #{worker_pid}"
# send TERM and wait for exit
Process.kill("TERM", worker_pid)
begin
Timeout.timeout(10) do
Process.waitpid(worker_pid)
puts "Process #{start_command} stopped successfully"
end
rescue Timeout::Error
# Kill process if could not exit in 10 seconds
puts "Sending KILL signal to #{start_command}, pid: #{worker_pid}"
Process.kill("KILL", worker_pid)
end
end
end
This can be called as following (added it to env.rb for cucumber):
# start delayed job
$delayed_job_worker = CucumberExternalWorker.new("rake jobs:work")
$delayed_job_worker.start

How to test concurrency locally?

Whats the best way to test concurrency locally? i.e. i want to test 10 concurrent hits. I am aware of services like Blitz. However, I am trying to find a simpler way of doing it locally to test against race conditions.
Any ideas? Via Curl maybe?
Check out Apache Bench (ab). Basic usage is dead simple:
ab -n 100 -c 10 http://your.application
For locally testing race conditions in the tests you can use helpers like this
# call block in a forked process
def fork_with_new_connection(config, object = nil, options={})
raise ArgumentError, "Missing block" unless block_given?
options = {
:stop => true, :index => 0
}.merge(options)
fork do
# stop the process after fork
Signal.trap('STOP') if options[:stop]
begin
ActiveRecord::Base.establish_connection(config)
yield(object)
ensure
ActiveRecord::Base.remove_connection
end
end
end
# call multiply times blocks
def multi_times_call_in_fork(count=3, &block)
raise ArgumentError, "Missing block" unless block_given?
config = ActiveRecord::Base.remove_connection
pids = []
count.times do |index|
pids << fork_with_new_connection(config, nil, :index=>index, &block)
end
# continue forked processes
Process.kill("CONT", *pids)
Process.waitall
ActiveRecord::Base.establish_connection(config)
end
# example
multi_times_call_in_fork(5) do
# do something with race conditions
# add asserts
end

Rails + TweetStream gem reconnecting

Hey, I just tested the TweetStream gem.
Example:
TweetStream::Client.new('myuser','mypass').track('ruby', 'rails') do |status|
puts "[#{status.user.screen_name}] #{status.text}"
end
This example works.
Questions:
I tried restarting my router (internet connection lost) and after that no new messages have arrived. Can someone explain this behavior to me?
I tested the daemon. What happens if no internet connection is available for a day or more? Will it reconnect automatically?
I like Rufus gem (for background processes). Can I somehow integrate this code with Rufus where I would check if the process is still active?
My reconnect solution (config/initializers/tweet_stream.rb):
client = nil
scheduler = Rufus::Scheduler.start_new
scheduler.every '30min', :first_in => '1s' do |job|
client.stop rescue nil
client = TweetStream::Client.new('user','pass').on_error do |message|
Rails.logger.info "[Rufus][#{Time.now}] TweetStream error: #{message}"
end.track('love') do |status|
Rails.logger.error "[TweetStream] Status: #{status.id}"
end
end
Thx!

Phusion Passenger + Workling + RabbitMQ

I am trying to deploy a RoR app that does some asynchronous task. I use workling for that and the message queue is RabbitMQ. This combination worked flawlessly with Starling but we decided to change the MQ for Rabbit.
I read somewhere that I should include the following code in my environment.rb
require 'mq'
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
if forked
if EM.reactor_running?
EM.stop_event_loop
EM.release_machine
EM.instance_variable_set( '#reactor_running', false )
end
Thread.current[:mq] = nil
AMQP.instance_variable_set('#conn', nil)
end
th = Thread.current
Thread.new{
AMQP.connect(:host => 'localhost'){
th.wakeup
}
}
Thread.stop
end
end
But that now Apache fails completely with message: The server encountered an internal error or misconfiguration and was unable to complete your request
EDIT: I've improved the code below somewhat since posting this. Available here: http://www.hiringthing.com/2011/11/04/eventmachine-with-rails.html
I just spent a milliioon years trying to get this to work, and finally did. Here is my code:
require 'amqp'
module HiringThingEM
def self.start
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
if forked && EM.reactor_running?
EM.stop
end
Thread.new {
EM.run do
AMQP.channel ||= AMQP::Channel.new(AMQP.connect(:host=> Q_SERVER, :user=> Q_USER, :pass => Q_PASS, :vhost => Q_VHOST ))
end
}
die_gracefully_on_signal
end
end
end
def self.die_gracefully_on_signal
Signal.trap("INT") { EM.stop }
Signal.trap("TERM") { EM.stop }
end
end
HiringThingEM.start
Now I can use:
EM.next_tick { AMQP.channel.queue(Q_Q).publish("hi mom") }
Inside the controllers of my Rails app.
Hope this helps someone.
Not really an answer, but unless you're committed to AMQP, I would recommend using https://github.com/defunkt/resque - it does the asynchronous job + fork gig very nicely.

Resources