How to make Bluepill restart Resque workers only after reaching a safe status - resque

Let's say this is my worker:
class FooWorker
#queue = :foo
def self.perform
User.all.each do |u|
...
Do Long Operations (Unsafe to kill)
...
# Here it's safe to break the worker and restart
end
end
end
I'm enqueing this with Resque Scheduler and this is my Bluepill conf:
...
app.process(process_name) do |process|
process.group = "resque"
process.start_command = "rake environment resque:work QUEUE=foo RAILS_ENV=production"
...
process.stop_signals = [:quit, 5.seconds, :term, 1.minute, :kill]
process.daemonize = true
process.start_grace_time = 30.seconds
process.stop_grace_time = 80.seconds
process.monitor_children do |child_process|
child_process.stop_command = "kill -QUIT {{PID}}"
child_process.checks :mem_usage, :every => 30.seconds, :below => 500.megabytes, :times => [3,4], :fires => :stop
end
end
....
I'd like to make Bluepill or Resque wait until it reaches the "safe" block to restart or shut down. How to achieve this?

Try it this way:
1) Set resque to kill children gracefully on TERM/INT with new_kill_child method by setting TERM_CHILD and RESQUE_TERM_TIMEOUT env variables on start:
process.start_command = "rake environment resque:work QUEUE=foo RAILS_ENV=production TERM_CHILD=1 RESQUE_TERM_TIMEOUT=20.0"
Default value for RESQUE_TERM_TIMEOUT is 4 seconds.
This will make resque send TERM signal to child, wait for RESQUE_TERM_TIMEOUT and if child is still running, kill it. Be sure to
a) set this timeout large enough for your critical section to end,
b) configure Bluepill TERM timeout in process.stop_signals to be a bit larger than RESQUE_TERM_TIMEOUT not to kill worker while it waits for child process to end critical section.
2) Handle TERM signal in child process to stop gracefully:
class FooWorker
class << self
attr_accessor :stop
end
#queue = :foo
def self.perform
User.all.each do |u|
...
Do Long Operations (Unsafe to kill)
...
# Here it's safe to break the worker and restart
return if FooWorker.stop
end
end
end
trap('TERM') do
FooWorker.stop = true
end

Related

How to gracefully shutdown a thread in Ruby

I have been experimenting multi-threading concept in Ruby for the past a week.
For practising, I am designing a file downloader that makes parallel requests for a collection of URLs. Currently I need to safely shutdown threads when interrupt signal is triggered. I have read the theory of multi-threading and catching a signal at runtime. Yet despite the whole those theoretical knowledge, I still don't have any idea about how to use them in practice.
I am leaving my proof of concept work below, anyhow.
class MultiThread
attr_reader :limit, :threads, :queue
def initialize(limit)
#limit = limit
#threads = []
#queue = Queue.new
end
def add(*args, &block)
queue << [block, args]
end
def invoke
1.upto(limit).each { threads << spawn_thread }
threads.each(&:join)
end
private
def spawn_thread
Thread.new do
Thread.handle_interrupt(RuntimeError => :on_blocking) do
# Nothing to do
end
until queue.empty?
block, args = queue.pop
block&.call(*args)
end
end
end
end
urls = %w[https://example.com]
thread = MultiThread.new(2)
urls.each do |url|
thread.add do
puts "Downloading #{url}..."
sleep 1
end
end
thread.invoke
Yeah, the docs for handle_interrupt are confusing. Try this, which I based on the connection_pool gem used by e.g. puma.
$stdout.sync = true
threads = 3.times.map { |i|
Thread.new {
Thread.handle_interrupt(Exception => :never) do
begin
Thread.handle_interrupt(Exception => :immediate) do
puts "Thread #{i} doing work"
sleep 1000
end
ensure
puts "Thread #{i} cleaning up"
end
end
}
}
Signal.trap("INT") {
puts 'Exiting gracefully'
threads.each { |t|
puts 'killing thread'
t.kill
}
exit
}
threads.each { |t| t.join }
Output:
Thread 1 doing work
Thread 2 doing work
Thread 0 doing work
^CExiting gracefully
killing thread
killing thread
killing thread
Thread 0 cleaning up
Thread 1 cleaning up
Thread 2 cleaning up

How can I notify a Celluliod::Actor of mastered Celluloid::Pool actor died?

I got a test example.
Here MyBoss should accomplish hard, but granulated work, despite some of its workes have to die.
require 'celluloid'
require 'celluloid/autostart'
class MyActor
include Celluloid
attr_reader :name, :peace_of_work
def initialize
#name = "actor_#{self.object_id}"
print "Created actor '#{#name}'\n"
end
def run(peace_of_work)
#peace_of_work = peace_of_work
sleep 0.1
raise "Actor '#{#name}' is dying" if rand(0..1.0) < 0.1
print "Actor '#{#name}' has #{peace_of_work}-th peace of work done\n"
end
end
class MyBoss
def initialize
end
def run work_peaces
#work_peaces = work_peaces
#actor_pool = MyActor.pool(size: 10, args: [])
work_peaces.each do |peace_of_work|
#actor_pool.async.run(peace_of_work)
end
end
end
if __FILE__ == $PROGRAM_NAME
boss = MyBoss.new
work_peaces = (0..999).to_a
boss.run(work_peaces)
sleep
end
Actors sporadically die. Obiously I need to redo screwed up work peaces. How can I trap actors deaths in case they are in a pool? Notice MyActor.pool(size: 10, args: [])
This is a known issue being resolved at the link below:
https://github.com/celluloid/celluloid-pool/issues/8
The gist is:
trap_exit already works for the Pool manager itself. When an actor dies, it needs to be taken out of the idle and busy sets of the Pool and a new actor needs to be spawned as idle ... otherwise the Pool becomes unstable.
A secondary method needs to be injected to run after Pool itself can correct its situation with a dead actor.

Right way of stopping a Ruby service

I am using AirBnb Nerve service. It's service code looks like this:
require 'logger'
require 'json'
require 'timeout'
require 'nerve/version'
require 'nerve/utils'
require 'nerve/log'
require 'nerve/ring_buffer'
require 'nerve/reporter'
require 'nerve/service_watcher'
module Nerve
class Nerve
include Logging
def initialize(opts={})
log.info 'nerve: starting up!'
# set global variable for exit signal
$EXIT = false
...some code...
# Any exceptions in the watcher threads should wake the main thread so
# that we can fail fast.
Thread.abort_on_exception = true
log.debug 'nerve: completed init'
end
def run
log.info 'nerve: starting run'
#services.each do |name, config|
launch_watcher(name, config)
end
begin
sleep
rescue StandardError => e
log.error "nerve: encountered unexpected exception #{e.inspect} in main thread"
raise e
ensure
$EXIT = true
log.warn 'nerve: reaping all watchers'
#watchers.each do |name, watcher_thread|
reap_watcher(name)
end
end
log.info 'nerve: exiting'
ensure
$EXIT = true
end
def launch_watcher(name, config)
... some code ...
end
def reap_watcher(name)
... some code ...
end
end
end
I do not see any stop method. What is the right way of stopping such a service? I am using JRuby and intend to write a JSVC adapter for this service.
There is no way to do this via the current API, short of sending it a signal.
If sending a signal isn't going to work and you want to handle stop explicitly, it looks like you will need to change the following things:
Add a #stop method to Nerve that sets $EXIT = true.
Modify #run so that rather than sleeping forever (sleep) it wakes up and checks $EXIT.

Ruby: Synchronizing fork pool output

I am trying to create a generic way of iterating Enumerables using multiple processors. I am spawning a given number of workers using fork, and feeding them data to process reusing idle workers. However, I would like to synchronize the input and output order. If job 1 and job 2 are started simultaneously and job 2 is completed before job 1, then the result order is out of sync. I would like to cache the output on the fly somehow to synchronize the output order, but I fail to see how this can be done?
#!/usr/bin/env ruby
require 'pp'
DEBUG = false
CPUS = 2
module Enumerable
# Fork each (feach) creates a fork pool with a specified number of processes
# to iterate over the Enumerable object processing the specified block.
# Calling feach with :processes => 0 disables forking for debugging purposes.
# It is possible to disable synchronized output with :synchronize => false
# which will save some overhead.
#
# #example - process 10 elements using 4 processes:
#
# (0 ... 10).feach(:processes => 4) { |i| puts i; sleep 1 }
def feach(options = {}, &block)
$stderr.puts "Parent pid: #{Process.pid}" if DEBUG
procs = options[:processes] || 0
sync = options[:synchronize] || true
if procs > 0
workers = spawn_workers(procs, &block)
threads = []
self.each_with_index do |elem, index|
$stderr.puts "elem: #{elem} index: #{index}" if DEBUG
threads << Thread.new do
worker = workers[index % procs]
worker.process(elem)
end
if threads.size == procs
threads.each { |thread| thread.join }
threads = []
end
end
threads.each { |thread| thread.join }
workers.each { |worker| worker.terminate }
else
self.each do |elem|
block.call(elem)
end
end
end
def spawn_workers(procs, &block)
workers = []
procs.times do
child_read, parent_write = IO.pipe
parent_read, child_write = IO.pipe
pid = Process.fork do
begin
parent_write.close
parent_read.close
call(child_read, child_write, &block)
ensure
child_read.close
child_write.close
end
end
child_read.close
child_write.close
$stderr.puts "Spawning worker with pid: #{pid}" if DEBUG
workers << Worker.new(parent_read, parent_write, pid)
end
workers
end
def call(child_read, child_write, &block)
while not child_read.eof?
elem = Marshal.load(child_read)
$stderr.puts " call with Process.pid: #{Process.pid}" if DEBUG
result = block.call(elem)
Marshal.dump(result, child_write)
end
end
class Worker
attr_reader :parent_read, :parent_write, :pid
def initialize(parent_read, parent_write, pid)
#parent_read = parent_read
#parent_write = parent_write
#pid = pid
end
def process(elem)
Marshal.dump(elem, #parent_write)
$stderr.puts " process with worker pid: #{#pid} and parent pid: #{Process.pid}" if DEBUG
Marshal.load(#parent_read)
end
def terminate
$stderr.puts "Terminating worker with pid: #{#pid}" if DEBUG
Process.wait(#pid, Process::WNOHANG)
#parent_read.close
#parent_write.close
end
end
end
def fib(n) n < 2 ? n : fib(n-1)+fib(n-2); end # Lousy Fibonacci calculator <- heavy job
(0 ... 10).feach(processes: CPUS) { |i| puts "#{i}: #{fib(35)}" }
There is no way to sync the output unless you force all the child processes to send their output to the parent and have it sort the results, or you enforce some kind of I/O locking between processes.
Without knowing what your long term goal is it's difficult to suggest a solution. In general, you'll need a lot of work in each process to gain any signficant speedup using fork and there is not a simple way to get results back to the main program.
Native Threads( pthreads on linux) might make more sense to accomplish what you are trying to do, however not all versions of Ruby support threads at that level. See :
Does ruby have real multithreading?

Restarting Sidekiq

What is the correct way to restart sidekiq. It seems to cache my workers' code when I start it, so every time I make a change to my workers I need to restart it. I'm doing this with Ctrl/C, but the process takes a long time to wind down and return me to the prompt.
Is there a way to force a restart with immediate effect?
I'm using the latest version with Sinatra running via POW.
Sidekiq comes with the command sidekiqctl, which can stop the PID associated with your Sidekiq process. You pass in the PID file and the # of seconds to wait for all threads to finish.
Sample Usage:
sidekiqctl stop #{rails_root}/tmp/pids/sidekiq_website_crawler.pid 60
Here, 60 represents the number of seconds to wait until all Sidekiq threads are done processing. If 60 seconds pass, and all aren't done, they are killed automatically.
I also recommend using the God gem to monitor, stop, start and restart Sidekiq.
Once you do that, you can use bundle exec god stop to stop all sidekiq threads.
Here is my God file, as an example:
rails_env = ENV['RAILS_ENV'] || "development"
rails_root = ENV['RAILS_ROOT'] || "/home/hwc218/BuzzSumo"
God.watch do |w|
w.dir = "#{rails_root}"
w.name = "website_crawler"
w.interval = 30.seconds
w.env = {"RAILS_ENV" => rails_env}
w.interval = 30.seconds
w.start = "bundle exec sidekiq -C #{rails_root}/config/sidekiq_website_crawler.yml"
w.stop = "sidekiqctl stop #{rails_root}/tmp/pids/sidekiq_website_crawler.pid 60"
w.keepalive
# determine the state on startup
w.transition(:init, { true => :up, false => :start }) do |on|
on.condition(:process_running) do |c|
c.running = true
end
end
# determine when process has finished starting
w.transition([:start, :restart], :up) do |on|
on.condition(:process_running) do |c|
c.running = true
c.interval = 5.seconds
end
# failsafe
on.condition(:tries) do |c|
c.times = 5
c.transition = :start
c.interval = 5.seconds
end
end
# start if process is not running
w.transition(:up, :start) do |on|
on.condition(:process_running) do |c|
c.running = false
end
end
w.restart_if do |restart|
restart.condition(:restart_file_touched) do |c|
c.interval = 5.seconds
c.restart_file = File.join(rails_root, 'tmp', 'restart.txt')
end
end
end

Resources