resqued 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +66 -0
 - data/exe/resqued +49 -0
 - data/exe/resqued-listener +4 -0
 - data/lib/resqued/backoff.rb +46 -0
 - data/lib/resqued/config.rb +88 -0
 - data/lib/resqued/daemon.rb +30 -0
 - data/lib/resqued/listener.rb +221 -0
 - data/lib/resqued/listener_proxy.rb +98 -0
 - data/lib/resqued/logging.rb +37 -0
 - data/lib/resqued/master.rb +195 -0
 - data/lib/resqued/pidfile.rb +27 -0
 - data/lib/resqued/sleepy.rb +20 -0
 - data/lib/resqued/version.rb +3 -0
 - data/lib/resqued/worker.rb +96 -0
 - data/lib/resqued.rb +2 -0
 - metadata +173 -0
 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # resqued - a long-running daemon for resque workers.
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            [image of a ninja rescuing an ear of corn]
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            resqued provides a resque worker that works well with
         
     | 
| 
      
 6 
     | 
    
         
            +
            slow jobs and continuous delivery.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            Install by adding resqued to your Gemfile
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                gem 'resqued'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ## Set up
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            Let's say you were running workers like this:
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                rake resque:work QUEUE=high        &
         
     | 
| 
      
 19 
     | 
    
         
            +
                rake resque:work QUEUE=high        &
         
     | 
| 
      
 20 
     | 
    
         
            +
                rake resque:work QUEUE=slow        &
         
     | 
| 
      
 21 
     | 
    
         
            +
                rake resque:work QUEUE=medium      &
         
     | 
| 
      
 22 
     | 
    
         
            +
                rake resque:work QUEUE=medium,low  &
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            To run the same fleet of workers with resqued, create a config file
         
     | 
| 
      
 25 
     | 
    
         
            +
            `config/resqued.rb` like this:
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                base = File.expand_path('..', File.dirname(__FILE__))
         
     | 
| 
      
 28 
     | 
    
         
            +
                pidfile File.join(base, 'tmp/pids/resqued-listener.pid')
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                worker do
         
     | 
| 
      
 31 
     | 
    
         
            +
                  workers 2
         
     | 
| 
      
 32 
     | 
    
         
            +
                  queue 'high'
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                worker do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  queue 'slow'
         
     | 
| 
      
 37 
     | 
    
         
            +
                  timeout -1 # never time out
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                worker do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  queue 'medium'
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                worker do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  queues 'medium', 'low'
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            Run it like this:
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                resqued config/resqued.rb
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            Or like this to daemonize it:
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                resqued -p tmp/pids/resqued-master.pid -D config/resqued.rb
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            When resqued is running, it has the following processes:
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            * master - brokers signals to child processes.
         
     | 
| 
      
 59 
     | 
    
         
            +
            * queue reader - retrieves jobs from queues and forks worker processes.
         
     | 
| 
      
 60 
     | 
    
         
            +
            * worker - runs a single job.
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            The following signals are handled by the resqued master process:
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            * HUP - reread config file and gracefully restart all workers.
         
     | 
| 
      
 65 
     | 
    
         
            +
            * INT / TERM - immediately kill all workers and shut down.
         
     | 
| 
      
 66 
     | 
    
         
            +
            * QUIT - graceful shutdown. Waits for workers to finish.
         
     | 
    
        data/exe/resqued
    ADDED
    
    | 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            options = {}
         
     | 
| 
      
 6 
     | 
    
         
            +
            daemonize = false
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            opts = OptionParser.new do |opts|
         
     | 
| 
      
 9 
     | 
    
         
            +
              opts.banner = "Usage: resqued [options] resqued-config-file"
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              opts.on '-h', '--help', 'Show this message' do
         
     | 
| 
      
 12 
     | 
    
         
            +
                puts opts
         
     | 
| 
      
 13 
     | 
    
         
            +
                exit
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              opts.on '-v', '--version', 'Show the version' do
         
     | 
| 
      
 17 
     | 
    
         
            +
                require 'resqued/version'
         
     | 
| 
      
 18 
     | 
    
         
            +
                puts Resqued::VERSION
         
     | 
| 
      
 19 
     | 
    
         
            +
                exit
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              opts.on '-p', '--pidfile PIDFILE', 'Store the pid of the master process in PIDFILE' do |v|
         
     | 
| 
      
 23 
     | 
    
         
            +
                options[:master_pidfile] = v
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              opts.on '-l', '--logfile LOGFILE', 'Write output to LOGFILE instead of stdout' do |v|
         
     | 
| 
      
 27 
     | 
    
         
            +
                require 'resqued/logging'
         
     | 
| 
      
 28 
     | 
    
         
            +
                Resqued::Logging.log_file = v
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              opts.on '-D', '--daemonize', 'Run daemonized in the background' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                daemonize = true
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            opts.parse!
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            unless options[:config_path] = ARGV[0]
         
     | 
| 
      
 39 
     | 
    
         
            +
              puts opts
         
     | 
| 
      
 40 
     | 
    
         
            +
              exit 1
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            require 'resqued/master'
         
     | 
| 
      
 44 
     | 
    
         
            +
            resqued = Resqued::Master.new(options)
         
     | 
| 
      
 45 
     | 
    
         
            +
            if daemonize
         
     | 
| 
      
 46 
     | 
    
         
            +
              require 'resqued/daemon'
         
     | 
| 
      
 47 
     | 
    
         
            +
              resqued = Resqued::Daemon.new(resqued)
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
            resqued.run
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Backoff
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(options = {})
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @time = options.fetch(:time) { Time }
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @min  = options.fetch(:min) { 1.0 }
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @max  = options.fetch(:max) { 16.0 }
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                # Public: Tell backoff that the thing we might want to back off from just started.
         
     | 
| 
      
 10 
     | 
    
         
            +
                def started
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @last_started_at = now
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @backoff_duration = @backoff_duration ? [@backoff_duration * 2.0, @max].min : @min
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def finished
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @backoff_duration = nil if ok?
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # Public: Check if we should wait before starting again.
         
     | 
| 
      
 20 
     | 
    
         
            +
                def wait?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @last_started_at && next_start_at > now
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Public: Check if we are ok to start (i.e. we don't need to back off).
         
     | 
| 
      
 25 
     | 
    
         
            +
                def ok?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  ! wait?
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                # Public: How much longer until `ok?` will be true?
         
     | 
| 
      
 30 
     | 
    
         
            +
                def how_long?
         
     | 
| 
      
 31 
     | 
    
         
            +
                  ok? ? nil : next_start_at - now
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                private
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                # Private: The next time when you're allowed to start a process.
         
     | 
| 
      
 37 
     | 
    
         
            +
                def next_start_at
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @last_started_at && @backoff_duration ? @last_started_at + @backoff_duration : now
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 42 
     | 
    
         
            +
                def now
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @time.now
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Public: Build a new config instance from the given file.
         
     | 
| 
      
 4 
     | 
    
         
            +
                def self.load_file(filename)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  new.load_file(filename)
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                # Public: Build a new config instance from the given `config` script.
         
     | 
| 
      
 9 
     | 
    
         
            +
                def self.load_string(config, filename = nil)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  new.load_string(config, filename)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Public: Build a new config instance.
         
     | 
| 
      
 14 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @workers = []
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                # Public: The configured pidfile path, or nil.
         
     | 
| 
      
 19 
     | 
    
         
            +
                attr_reader :pidfile
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Public: An array of configured workers.
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_reader :workers
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Public: Add to this config using the script `config`.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def load_string(config, filename = nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  DSL.new(self)._apply(config, filename)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  self
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                # Public: Add to this config using the script in the given file.
         
     | 
| 
      
 31 
     | 
    
         
            +
                def load_file(filename)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  load_string(File.read(filename), filename)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 36 
     | 
    
         
            +
                class DSL
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def initialize(config)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @config = config
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # Internal.
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def _apply(script, filename)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    if filename.nil?
         
     | 
| 
      
 44 
     | 
    
         
            +
                      instance_eval(script)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    else
         
     | 
| 
      
 46 
     | 
    
         
            +
                      instance_eval(script, filename)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # Public: Set the pidfile path.
         
     | 
| 
      
 51 
     | 
    
         
            +
                  def pidfile(path)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    raise ArgumentError unless path.is_a?(String)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    _set(:pidfile, path)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  # Public: Define a worker.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  def worker
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @current_worker = {:size => 1, :queues => []}
         
     | 
| 
      
 59 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 60 
     | 
    
         
            +
                    _push(:workers, @current_worker)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    @current_worker = nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # Public: Add queues to a worker
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def queues(*queues)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    queues = [queues].flatten.map { |q| q.to_s }
         
     | 
| 
      
 67 
     | 
    
         
            +
                    @current_worker[:queues] += queues
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  alias queue queues
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  # Public: Set the number of workers
         
     | 
| 
      
 72 
     | 
    
         
            +
                  def workers(count)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    raise ArgumentError unless count.is_a?(Fixnum)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    @current_worker[:size] = count
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  # Private.
         
     | 
| 
      
 78 
     | 
    
         
            +
                  def _set(instance_variable, value)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @config.instance_variable_set("@#{instance_variable}", value)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  # Private.
         
     | 
| 
      
 83 
     | 
    
         
            +
                  def _push(instance_variable, value)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    @config.instance_variable_get("@#{instance_variable}").push(value)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Daemon
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(master)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @master = master
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # Public: daemonize and run the master process.
         
     | 
| 
      
 8 
     | 
    
         
            +
                def run
         
     | 
| 
      
 9 
     | 
    
         
            +
                  rd, wr = IO.pipe
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if fork
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # grandparent
         
     | 
| 
      
 12 
     | 
    
         
            +
                    wr.close
         
     | 
| 
      
 13 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 14 
     | 
    
         
            +
                      master_pid = rd.readpartial(16).to_i
         
     | 
| 
      
 15 
     | 
    
         
            +
                      exit
         
     | 
| 
      
 16 
     | 
    
         
            +
                    rescue EOFError
         
     | 
| 
      
 17 
     | 
    
         
            +
                      puts "Master process failed to start!"
         
     | 
| 
      
 18 
     | 
    
         
            +
                      exit! 1
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  elsif fork
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # parent
         
     | 
| 
      
 22 
     | 
    
         
            +
                    Process.setsid
         
     | 
| 
      
 23 
     | 
    
         
            +
                    exit
         
     | 
| 
      
 24 
     | 
    
         
            +
                  else
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # master
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @master.run(wr)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,221 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'resqued/config'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'resqued/logging'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'resqued/pidfile'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'resqued/sleepy'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'resqued/worker'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 10 
     | 
    
         
            +
              # A listener process. Watches resque queues and forks workers.
         
     | 
| 
      
 11 
     | 
    
         
            +
              class Listener
         
     | 
| 
      
 12 
     | 
    
         
            +
                include Resqued::Logging
         
     | 
| 
      
 13 
     | 
    
         
            +
                include Resqued::Pidfile
         
     | 
| 
      
 14 
     | 
    
         
            +
                include Resqued::Sleepy
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # Configure a new listener object.
         
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @config_path     = options.fetch(:config_path)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @running_workers = options.fetch(:running_workers) { [] }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @socket          = options.fetch(:socket)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @listener_id     = options.fetch(:listener_id) { nil }
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Public: As an alternative to #run, exec a new ruby instance for this listener.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def exec
         
     | 
| 
      
 26 
     | 
    
         
            +
                  ENV['RESQUED_SOCKET']      = @socket.fileno.to_s
         
     | 
| 
      
 27 
     | 
    
         
            +
                  ENV['RESQUED_CONFIG_PATH'] = @config_path
         
     | 
| 
      
 28 
     | 
    
         
            +
                  ENV['RESQUED_STATE']       = (@running_workers.map { |r| "#{r[:pid]}|#{r[:queue]}" }.join('||'))
         
     | 
| 
      
 29 
     | 
    
         
            +
                  ENV['RESQUED_LISTENER_ID'] = @listener_id.to_s
         
     | 
| 
      
 30 
     | 
    
         
            +
                  Kernel.exec('resqued-listener')
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # Public: Given args from #exec, start this listener.
         
     | 
| 
      
 34 
     | 
    
         
            +
                def self.exec!
         
     | 
| 
      
 35 
     | 
    
         
            +
                  options = {}
         
     | 
| 
      
 36 
     | 
    
         
            +
                  if socket = ENV['RESQUED_SOCKET']
         
     | 
| 
      
 37 
     | 
    
         
            +
                    options[:socket] = Socket.for_fd(socket.to_i)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  if path = ENV['RESQUED_CONFIG_PATH']
         
     | 
| 
      
 40 
     | 
    
         
            +
                    options[:config_path] = path
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if state = ENV['RESQUED_STATE']
         
     | 
| 
      
 43 
     | 
    
         
            +
                    options[:running_workers] = state.split('||').map { |s| Hash[[:pid,:queue].zip(s.split('|'))] }
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  if listener_id = ENV['RESQUED_LISTENER_ID']
         
     | 
| 
      
 46 
     | 
    
         
            +
                    options[:listener_id] = listener_id
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  new(options).run
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                # Private: memoizes the worker configuration.
         
     | 
| 
      
 52 
     | 
    
         
            +
                def config
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @config ||= Config.load_file(@config_path)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                SIGNALS = [ :QUIT ]
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                SIGNAL_QUEUE = []
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                # Public: Run the main loop.
         
     | 
| 
      
 61 
     | 
    
         
            +
                def run
         
     | 
| 
      
 62 
     | 
    
         
            +
                  trap(:CHLD) { awake }
         
     | 
| 
      
 63 
     | 
    
         
            +
                  SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal ; awake } }
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @socket.close_on_exec = true
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  with_pidfile(config.pidfile) do
         
     | 
| 
      
 67 
     | 
    
         
            +
                    write_procline('running')
         
     | 
| 
      
 68 
     | 
    
         
            +
                    load_environment
         
     | 
| 
      
 69 
     | 
    
         
            +
                    init_workers
         
     | 
| 
      
 70 
     | 
    
         
            +
                    run_workers_run
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  write_procline('shutdown')
         
     | 
| 
      
 74 
     | 
    
         
            +
                  burn_down_workers(:QUIT)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 78 
     | 
    
         
            +
                def run_workers_run
         
     | 
| 
      
 79 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 80 
     | 
    
         
            +
                    reap_workers(Process::WNOHANG)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    check_for_expired_workers
         
     | 
| 
      
 82 
     | 
    
         
            +
                    start_idle_workers
         
     | 
| 
      
 83 
     | 
    
         
            +
                    case signal = SIGNAL_QUEUE.shift
         
     | 
| 
      
 84 
     | 
    
         
            +
                    when nil
         
     | 
| 
      
 85 
     | 
    
         
            +
                      yawn
         
     | 
| 
      
 86 
     | 
    
         
            +
                    when :QUIT
         
     | 
| 
      
 87 
     | 
    
         
            +
                      return
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                # Private: make sure all the workers stop.
         
     | 
| 
      
 93 
     | 
    
         
            +
                #
         
     | 
| 
      
 94 
     | 
    
         
            +
                # Resque workers have gaps in their signal-handling ability.
         
     | 
| 
      
 95 
     | 
    
         
            +
                def burn_down_workers(signal)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 97 
     | 
    
         
            +
                    check_for_expired_workers
         
     | 
| 
      
 98 
     | 
    
         
            +
                    SIGNAL_QUEUE.clear
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    break if :no_child == reap_workers(Process::WNOHANG)
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    log "kill -#{signal} #{running_workers.map { |r| r.pid }.inspect}"
         
     | 
| 
      
 103 
     | 
    
         
            +
                    running_workers.each { |worker| worker.kill(signal) }
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    sleep 1 # Don't kill any more often than every 1s.
         
     | 
| 
      
 106 
     | 
    
         
            +
                    yawn 5
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                  # One last time.
         
     | 
| 
      
 109 
     | 
    
         
            +
                  reap_workers
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                # Private: all available workers
         
     | 
| 
      
 113 
     | 
    
         
            +
                attr_reader :workers
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                # Private: just the running workers.
         
     | 
| 
      
 116 
     | 
    
         
            +
                def running_workers
         
     | 
| 
      
 117 
     | 
    
         
            +
                  workers.select { |worker| ! worker.idle? }
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 121 
     | 
    
         
            +
                def yawn(sleep_time = nil)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  sleep_time ||=
         
     | 
| 
      
 123 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 124 
     | 
    
         
            +
                      sleep_times = [60.0] + workers.map { |worker| worker.backing_off_for }
         
     | 
| 
      
 125 
     | 
    
         
            +
                      [sleep_times.compact.min, 0.0].max
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
                  super(sleep_time, @socket)
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                # Private: Check for workers that have stopped running
         
     | 
| 
      
 131 
     | 
    
         
            +
                def reap_workers(waitpidflags = 0)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 133 
     | 
    
         
            +
                    worker_pid, status = Process.waitpid2(-1, waitpidflags)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    return :none_ready if worker_pid.nil?
         
     | 
| 
      
 135 
     | 
    
         
            +
                    finish_worker(worker_pid, status)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    report_to_master("-#{worker_pid}")
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                rescue Errno::ECHILD
         
     | 
| 
      
 139 
     | 
    
         
            +
                  # All done
         
     | 
| 
      
 140 
     | 
    
         
            +
                  :no_child
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                # Private: Check if master reports any dead workers.
         
     | 
| 
      
 144 
     | 
    
         
            +
                def check_for_expired_workers
         
     | 
| 
      
 145 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 146 
     | 
    
         
            +
                    IO.select([@socket], nil, nil, 0) or return
         
     | 
| 
      
 147 
     | 
    
         
            +
                    line = @socket.readline
         
     | 
| 
      
 148 
     | 
    
         
            +
                    finish_worker(line.to_i, nil)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
                rescue EOFError
         
     | 
| 
      
 151 
     | 
    
         
            +
                  log "eof from master"
         
     | 
| 
      
 152 
     | 
    
         
            +
                  Process.kill(:QUIT, $$)
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 156 
     | 
    
         
            +
                def finish_worker(worker_pid, status)
         
     | 
| 
      
 157 
     | 
    
         
            +
                  workers.each do |worker|
         
     | 
| 
      
 158 
     | 
    
         
            +
                    if worker.pid == worker_pid
         
     | 
| 
      
 159 
     | 
    
         
            +
                      worker.finished!(status)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
                end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 165 
     | 
    
         
            +
                def start_idle_workers
         
     | 
| 
      
 166 
     | 
    
         
            +
                  workers.each do |worker|
         
     | 
| 
      
 167 
     | 
    
         
            +
                    if worker.idle?
         
     | 
| 
      
 168 
     | 
    
         
            +
                      worker.try_start
         
     | 
| 
      
 169 
     | 
    
         
            +
                      if pid = worker.pid
         
     | 
| 
      
 170 
     | 
    
         
            +
                        report_to_master("+#{pid},#{worker.queue_key}")
         
     | 
| 
      
 171 
     | 
    
         
            +
                      end
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
                end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 177 
     | 
    
         
            +
                def init_workers
         
     | 
| 
      
 178 
     | 
    
         
            +
                  workers = []
         
     | 
| 
      
 179 
     | 
    
         
            +
                  config.workers.each do |worker_config|
         
     | 
| 
      
 180 
     | 
    
         
            +
                    worker_config[:size].times do
         
     | 
| 
      
 181 
     | 
    
         
            +
                      workers << Worker.new(worker_config)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    end
         
     | 
| 
      
 183 
     | 
    
         
            +
                  end
         
     | 
| 
      
 184 
     | 
    
         
            +
                  @running_workers.each do |running_worker|
         
     | 
| 
      
 185 
     | 
    
         
            +
                    if blocked_worker = workers.detect { |worker| worker.idle? && worker.queue_key == running_worker[:queue] }
         
     | 
| 
      
 186 
     | 
    
         
            +
                      blocked_worker.wait_for(running_worker[:pid].to_i)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    end
         
     | 
| 
      
 188 
     | 
    
         
            +
                  end
         
     | 
| 
      
 189 
     | 
    
         
            +
                  @workers = workers
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                # Private: Report child process status.
         
     | 
| 
      
 193 
     | 
    
         
            +
                #
         
     | 
| 
      
 194 
     | 
    
         
            +
                # Examples:
         
     | 
| 
      
 195 
     | 
    
         
            +
                #
         
     | 
| 
      
 196 
     | 
    
         
            +
                #     report_to_master("+12345,queue")  # Worker process PID:12345 started, working on a job from "queue".
         
     | 
| 
      
 197 
     | 
    
         
            +
                #     report_to_master("-12345")        # Worker process PID:12345 exited.
         
     | 
| 
      
 198 
     | 
    
         
            +
                def report_to_master(status)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  @socket.puts(status)
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                # Private: load the application.
         
     | 
| 
      
 203 
     | 
    
         
            +
                #
         
     | 
| 
      
 204 
     | 
    
         
            +
                # To do:
         
     | 
| 
      
 205 
     | 
    
         
            +
                # * Does this reload correctly if the bundle changes and `bundle exec resqued config/resqued.rb`?
         
     | 
| 
      
 206 
     | 
    
         
            +
                # * Maybe make the specific app environment configurable (i.e. load rails, load rackup, load some custom thing)
         
     | 
| 
      
 207 
     | 
    
         
            +
                def load_environment
         
     | 
| 
      
 208 
     | 
    
         
            +
                  require File.expand_path('config/environment.rb')
         
     | 
| 
      
 209 
     | 
    
         
            +
                  Rails.application.eager_load!
         
     | 
| 
      
 210 
     | 
    
         
            +
                end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 213 
     | 
    
         
            +
                def write_procline(status)
         
     | 
| 
      
 214 
     | 
    
         
            +
                  procline = "resqued listener"
         
     | 
| 
      
 215 
     | 
    
         
            +
                  procline << " #{@listener_id}" if @listener_id
         
     | 
| 
      
 216 
     | 
    
         
            +
                  procline << " [#{status}]"
         
     | 
| 
      
 217 
     | 
    
         
            +
                  procline << " #{@config_path}"
         
     | 
| 
      
 218 
     | 
    
         
            +
                  $0 = procline
         
     | 
| 
      
 219 
     | 
    
         
            +
                end
         
     | 
| 
      
 220 
     | 
    
         
            +
              end
         
     | 
| 
      
 221 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'fcntl'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require 'resqued/listener'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'resqued/logging'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 8 
     | 
    
         
            +
              class ListenerProxy
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Resqued::Logging
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # Public.
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # Public: wrap up all the things, this object is going home.
         
     | 
| 
      
 17 
     | 
    
         
            +
                def dispose
         
     | 
| 
      
 18 
     | 
    
         
            +
                  if @master_socket
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @master_socket.close
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @master_socket = nil
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Public: An IO to select on to check if there is incoming data available.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def read_pipe
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @master_socket
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                # Public: The pid of the running listener process.
         
     | 
| 
      
 30 
     | 
    
         
            +
                attr_reader :pid
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                # Public: Start the listener process.
         
     | 
| 
      
 33 
     | 
    
         
            +
                def run
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return if pid
         
     | 
| 
      
 35 
     | 
    
         
            +
                  listener_socket, master_socket = UNIXSocket.pair
         
     | 
| 
      
 36 
     | 
    
         
            +
                  if @pid = fork
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # master
         
     | 
| 
      
 38 
     | 
    
         
            +
                    listener_socket.close
         
     | 
| 
      
 39 
     | 
    
         
            +
                    master_socket.close_on_exec = true
         
     | 
| 
      
 40 
     | 
    
         
            +
                    log "Started listener #{@pid}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @master_socket = master_socket
         
     | 
| 
      
 42 
     | 
    
         
            +
                  else
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # listener
         
     | 
| 
      
 44 
     | 
    
         
            +
                    master_socket.close
         
     | 
| 
      
 45 
     | 
    
         
            +
                    Master::SIGNALS.each { |signal| trap(signal, 'DEFAULT') }
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Listener.new(@options.merge(:socket => listener_socket)).exec
         
     | 
| 
      
 47 
     | 
    
         
            +
                    exit
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                # Public: Stop the listener process.
         
     | 
| 
      
 52 
     | 
    
         
            +
                def kill(signal)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  log "kill -#{signal} #{pid}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                  Process.kill(signal.to_s, pid)
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # Public: Get the list of workers running from this listener.
         
     | 
| 
      
 58 
     | 
    
         
            +
                def running_workers
         
     | 
| 
      
 59 
     | 
    
         
            +
                  worker_pids.map { |pid, queue| { :pid => pid, :queue => queue } }
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                # Private: Map worker pids to queue names
         
     | 
| 
      
 63 
     | 
    
         
            +
                def worker_pids
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @worker_pids ||= {}
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                # Public: Check for updates on running worker information.
         
     | 
| 
      
 68 
     | 
    
         
            +
                def read_worker_status(options)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  on_finished = options[:on_finished]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  until @master_socket.nil?
         
     | 
| 
      
 71 
     | 
    
         
            +
                    IO.select([@master_socket], nil, nil, 0) or return
         
     | 
| 
      
 72 
     | 
    
         
            +
                    line = @master_socket.readline
         
     | 
| 
      
 73 
     | 
    
         
            +
                    if line =~ /^\+(\d+),(.*)$/
         
     | 
| 
      
 74 
     | 
    
         
            +
                      worker_pids[$1] = $2
         
     | 
| 
      
 75 
     | 
    
         
            +
                    elsif line =~ /^-(\d+)$/
         
     | 
| 
      
 76 
     | 
    
         
            +
                      worker_pids.delete($1)
         
     | 
| 
      
 77 
     | 
    
         
            +
                      on_finished.worker_finished($1) if on_finished
         
     | 
| 
      
 78 
     | 
    
         
            +
                    elsif line == ''
         
     | 
| 
      
 79 
     | 
    
         
            +
                      break
         
     | 
| 
      
 80 
     | 
    
         
            +
                    else
         
     | 
| 
      
 81 
     | 
    
         
            +
                      log "Malformed data from listener: #{line.inspect}"
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                rescue EOFError
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @master_socket.close
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @master_socket = nil
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                # Public: Tell the listener process that a worker finished.
         
     | 
| 
      
 90 
     | 
    
         
            +
                def worker_finished(pid)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  return if @master_socket.nil?
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @master_socket.puts(pid)
         
     | 
| 
      
 93 
     | 
    
         
            +
                rescue Errno::EPIPE
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @master_socket.close
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @master_socket = nil
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Logging
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Public.
         
     | 
| 
      
 4 
     | 
    
         
            +
                def self.log_file=(path)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  ENV['RESQUED_LOGFILE'] = File.expand_path(path)
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                # Public.
         
     | 
| 
      
 9 
     | 
    
         
            +
                def self.log_file
         
     | 
| 
      
 10 
     | 
    
         
            +
                  ENV['RESQUED_LOGFILE']
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Public.
         
     | 
| 
      
 14 
     | 
    
         
            +
                def log_to_stdout?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Resqued::Logging.log_file.nil?
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                # Private (in classes that include this module)
         
     | 
| 
      
 19 
     | 
    
         
            +
                def log(message)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  logging_io.puts "[#{$$} #{Time.now.strftime('%H:%M:%S')}] #{message}"
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                # Private (may be overridden in classes that include this module to send output to a different IO)
         
     | 
| 
      
 24 
     | 
    
         
            +
                def logging_io
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @logging_io = nil if @logging_io && @logging_io.closed?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @logging_io ||=
         
     | 
| 
      
 27 
     | 
    
         
            +
                    if path = Resqued::Logging.log_file
         
     | 
| 
      
 28 
     | 
    
         
            +
                      File.open(path, 'a').tap do |f|
         
     | 
| 
      
 29 
     | 
    
         
            +
                        f.sync = true
         
     | 
| 
      
 30 
     | 
    
         
            +
                        f.close_on_exec = true
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    else
         
     | 
| 
      
 33 
     | 
    
         
            +
                      $stdout
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,195 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'resqued/backoff'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'resqued/listener_proxy'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'resqued/logging'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'resqued/pidfile'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'resqued/sleepy'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 8 
     | 
    
         
            +
              # The master process.
         
     | 
| 
      
 9 
     | 
    
         
            +
              # * Spawns a listener.
         
     | 
| 
      
 10 
     | 
    
         
            +
              # * Tracks all work. (IO pipe from listener.)
         
     | 
| 
      
 11 
     | 
    
         
            +
              # * Handles signals.
         
     | 
| 
      
 12 
     | 
    
         
            +
              class Master
         
     | 
| 
      
 13 
     | 
    
         
            +
                include Resqued::Logging
         
     | 
| 
      
 14 
     | 
    
         
            +
                include Resqued::Pidfile
         
     | 
| 
      
 15 
     | 
    
         
            +
                include Resqued::Sleepy
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @config_path = options.fetch(:config_path)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @pidfile     = options.fetch(:master_pidfile) { nil }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @listener_backoff = Backoff.new
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @listeners_created = 0
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # Public: Starts the master process.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def run(ready_pipe = nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  report_unexpected_exits
         
     | 
| 
      
 27 
     | 
    
         
            +
                  with_pidfile(@pidfile) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                    write_procline
         
     | 
| 
      
 29 
     | 
    
         
            +
                    install_signal_handlers
         
     | 
| 
      
 30 
     | 
    
         
            +
                    if ready_pipe
         
     | 
| 
      
 31 
     | 
    
         
            +
                      ready_pipe.syswrite($$.to_s)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      ready_pipe.close rescue nil
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                    go_ham
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  no_more_unexpected_exits
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                # Private: dat main loop.
         
     | 
| 
      
 40 
     | 
    
         
            +
                def go_ham
         
     | 
| 
      
 41 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 42 
     | 
    
         
            +
                    read_listeners
         
     | 
| 
      
 43 
     | 
    
         
            +
                    reap_all_listeners(Process::WNOHANG)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    start_listener
         
     | 
| 
      
 45 
     | 
    
         
            +
                    case signal = SIGNAL_QUEUE.shift
         
     | 
| 
      
 46 
     | 
    
         
            +
                    when nil
         
     | 
| 
      
 47 
     | 
    
         
            +
                      yawn(@listener_backoff.how_long? || 30.0)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    when :INFO
         
     | 
| 
      
 49 
     | 
    
         
            +
                      dump_object_counts
         
     | 
| 
      
 50 
     | 
    
         
            +
                    when :HUP
         
     | 
| 
      
 51 
     | 
    
         
            +
                      log "Restarting listener with new configuration and application."
         
     | 
| 
      
 52 
     | 
    
         
            +
                      kill_listener(:QUIT)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    when :INT, :TERM, :QUIT
         
     | 
| 
      
 54 
     | 
    
         
            +
                      log "Shutting down..."
         
     | 
| 
      
 55 
     | 
    
         
            +
                      kill_all_listeners(signal)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      wait_for_workers
         
     | 
| 
      
 57 
     | 
    
         
            +
                      break
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 63 
     | 
    
         
            +
                def dump_object_counts
         
     | 
| 
      
 64 
     | 
    
         
            +
                  log GC.stat.inspect
         
     | 
| 
      
 65 
     | 
    
         
            +
                  counts = {}
         
     | 
| 
      
 66 
     | 
    
         
            +
                  total = 0
         
     | 
| 
      
 67 
     | 
    
         
            +
                  ObjectSpace.each_object do |o|
         
     | 
| 
      
 68 
     | 
    
         
            +
                    count = counts[o.class.name] || 0
         
     | 
| 
      
 69 
     | 
    
         
            +
                    counts[o.class.name] = count + 1
         
     | 
| 
      
 70 
     | 
    
         
            +
                    total += 1
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  top = 10
         
     | 
| 
      
 73 
     | 
    
         
            +
                  log "#{total} objects. top #{top}:"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  counts.sort_by { |name, count| count }.reverse.each_with_index do |(name, count), i|
         
     | 
| 
      
 75 
     | 
    
         
            +
                    if i < top
         
     | 
| 
      
 76 
     | 
    
         
            +
                      diff = ""
         
     | 
| 
      
 77 
     | 
    
         
            +
                      if last = @last_counts && @last_counts[name]
         
     | 
| 
      
 78 
     | 
    
         
            +
                        diff = " (#{'%+d' % (count - last)})"
         
     | 
| 
      
 79 
     | 
    
         
            +
                      end
         
     | 
| 
      
 80 
     | 
    
         
            +
                      log "   #{count} #{name}#{diff}"
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @last_counts = counts
         
     | 
| 
      
 84 
     | 
    
         
            +
                  log GC.stat.inspect
         
     | 
| 
      
 85 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 86 
     | 
    
         
            +
                  log "Error while counting objects: #{e}"
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                # Private: Map listener pids to ListenerProxy objects.
         
     | 
| 
      
 90 
     | 
    
         
            +
                def listener_pids
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @listener_pids ||= {}
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                # Private: All the ListenerProxy objects.
         
     | 
| 
      
 95 
     | 
    
         
            +
                def all_listeners
         
     | 
| 
      
 96 
     | 
    
         
            +
                  listener_pids.values
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                attr_reader :config_path
         
     | 
| 
      
 100 
     | 
    
         
            +
                attr_reader :pidfile
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                def start_listener
         
     | 
| 
      
 103 
     | 
    
         
            +
                  return if @current_listener || @listener_backoff.wait?
         
     | 
| 
      
 104 
     | 
    
         
            +
                  @current_listener = ListenerProxy.new(:config_path => @config_path, :running_workers => all_listeners.map { |l| l.running_workers }.flatten, :listener_id => next_listener_id)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @current_listener.run
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @listener_backoff.started
         
     | 
| 
      
 107 
     | 
    
         
            +
                  listener_pids[@current_listener.pid] = @current_listener
         
     | 
| 
      
 108 
     | 
    
         
            +
                  write_procline
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                def next_listener_id
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @listeners_created += 1
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def read_listeners
         
     | 
| 
      
 116 
     | 
    
         
            +
                  all_listeners.each do |l|
         
     | 
| 
      
 117 
     | 
    
         
            +
                    l.read_worker_status(:on_finished => self)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def worker_finished(pid)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  all_listeners.each do |other|
         
     | 
| 
      
 123 
     | 
    
         
            +
                    other.worker_finished(pid)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                def kill_listener(signal)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  if @current_listener
         
     | 
| 
      
 129 
     | 
    
         
            +
                    @current_listener.kill(signal)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    @current_listener = nil
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                def kill_all_listeners(signal)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  all_listeners.each do |l|
         
     | 
| 
      
 136 
     | 
    
         
            +
                    l.kill(signal)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                def wait_for_workers
         
     | 
| 
      
 141 
     | 
    
         
            +
                  reap_all_listeners
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                def reap_all_listeners(waitpid_flags = 0)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 146 
     | 
    
         
            +
                    lpid, status = Process.waitpid2(-1, waitpid_flags)
         
     | 
| 
      
 147 
     | 
    
         
            +
                    if lpid
         
     | 
| 
      
 148 
     | 
    
         
            +
                      log "Listener exited #{status}"
         
     | 
| 
      
 149 
     | 
    
         
            +
                      if @current_listener && @current_listener.pid == lpid
         
     | 
| 
      
 150 
     | 
    
         
            +
                        @listener_backoff.finished
         
     | 
| 
      
 151 
     | 
    
         
            +
                        @current_listener = nil
         
     | 
| 
      
 152 
     | 
    
         
            +
                      end
         
     | 
| 
      
 153 
     | 
    
         
            +
                      listener_pids.delete(lpid).dispose # This may leak workers.
         
     | 
| 
      
 154 
     | 
    
         
            +
                      write_procline
         
     | 
| 
      
 155 
     | 
    
         
            +
                    else
         
     | 
| 
      
 156 
     | 
    
         
            +
                      return
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  rescue Errno::ECHILD
         
     | 
| 
      
 159 
     | 
    
         
            +
                    return
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end while true
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                SIGNALS = [ :HUP, :INT, :TERM, :QUIT, :INFO ]
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                SIGNAL_QUEUE = []
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                def install_signal_handlers
         
     | 
| 
      
 168 
     | 
    
         
            +
                  trap(:CHLD) { awake }
         
     | 
| 
      
 169 
     | 
    
         
            +
                  SIGNALS.each { |signal| trap(signal) { SIGNAL_QUEUE << signal ; awake } }
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                def report_unexpected_exits
         
     | 
| 
      
 173 
     | 
    
         
            +
                  trap('EXIT') do
         
     | 
| 
      
 174 
     | 
    
         
            +
                    log("EXIT #{$!.inspect}")
         
     | 
| 
      
 175 
     | 
    
         
            +
                    if $!
         
     | 
| 
      
 176 
     | 
    
         
            +
                      $!.backtrace.each do |line|
         
     | 
| 
      
 177 
     | 
    
         
            +
                        log(line)
         
     | 
| 
      
 178 
     | 
    
         
            +
                      end
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                def no_more_unexpected_exits
         
     | 
| 
      
 184 
     | 
    
         
            +
                  trap('EXIT', nil)
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                def yawn(duration)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  super(duration, all_listeners.map { |l| l.read_pipe })
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                def write_procline
         
     | 
| 
      
 192 
     | 
    
         
            +
                  $0 = "resqued master [gen #{@listeners_created}] [#{listener_pids.size} running] #{ARGV.join(' ')}"
         
     | 
| 
      
 193 
     | 
    
         
            +
                end
         
     | 
| 
      
 194 
     | 
    
         
            +
              end
         
     | 
| 
      
 195 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Pidfile
         
     | 
| 
      
 3 
     | 
    
         
            +
                def with_pidfile(filename)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  write_pidfile(filename) if filename
         
     | 
| 
      
 5 
     | 
    
         
            +
                  yield
         
     | 
| 
      
 6 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 7 
     | 
    
         
            +
                  remove_pidfile(filename) if filename
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def write_pidfile(filename)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  pf =
         
     | 
| 
      
 12 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 13 
     | 
    
         
            +
                      tmp = "#{filename}.#{rand}.#{$$}"
         
     | 
| 
      
 14 
     | 
    
         
            +
                      File.open(tmp, File::RDWR | File::CREAT | File::EXCL, 0644)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    rescue Errno::EEXIST
         
     | 
| 
      
 16 
     | 
    
         
            +
                      retry
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  pf.syswrite("#{$$}\n")
         
     | 
| 
      
 19 
     | 
    
         
            +
                  File.rename(pf.path, filename)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  pf.close
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def remove_pidfile(filename)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  (File.read(filename).to_i == $$) and File.unlink(filename) rescue nil
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'fcntl'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'kgio'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Sleepy
         
     | 
| 
      
 6 
     | 
    
         
            +
                def self_pipe
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @self_pipe ||= Kgio::Pipe.new.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def yawn(duration, *inputs)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  inputs = [self_pipe[0]] + [inputs].flatten.compact
         
     | 
| 
      
 12 
     | 
    
         
            +
                  IO.select(inputs, nil, nil, duration) or return
         
     | 
| 
      
 13 
     | 
    
         
            +
                  self_pipe[0].kgio_tryread(11)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def awake
         
     | 
| 
      
 17 
     | 
    
         
            +
                  self_pipe[1].kgio_trywrite('.')
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,96 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'resque'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'resqued/backoff'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'resqued/logging'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Resqued
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Models a worker process.
         
     | 
| 
      
 8 
     | 
    
         
            +
              class Worker
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Resqued::Logging
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @queues = options.fetch(:queues)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @backoff = Backoff.new
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # Public: The pid of the worker process.
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_reader :pid
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # Private.
         
     | 
| 
      
 20 
     | 
    
         
            +
                attr_reader :queues
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                # Public: True if there is no worker process mapped to this object.
         
     | 
| 
      
 23 
     | 
    
         
            +
                def idle?
         
     | 
| 
      
 24 
     | 
    
         
            +
                  pid.nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                # Public: Checks if this worker works on jobs from the queue.
         
     | 
| 
      
 28 
     | 
    
         
            +
                def queue_key
         
     | 
| 
      
 29 
     | 
    
         
            +
                  queues.sort.join(';')
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                # Public: Claim this worker for another listener's worker.
         
     | 
| 
      
 33 
     | 
    
         
            +
                def wait_for(pid)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  raise "Already running #{@pid} (can't wait for #{pid})" if @pid
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @self_started = nil
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @pid = pid
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                # Public: The old worker process finished!
         
     | 
| 
      
 40 
     | 
    
         
            +
                def finished!(process_status)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @pid = nil
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @backoff.finished
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # Public: The amount of time we need to wait before starting a new worker.
         
     | 
| 
      
 46 
     | 
    
         
            +
                def backing_off_for
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @pid ? nil : @backoff.how_long?
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # Public: Start a job, if there's one waiting in one of my queues.
         
     | 
| 
      
 51 
     | 
    
         
            +
                def try_start
         
     | 
| 
      
 52 
     | 
    
         
            +
                  return if @backoff.wait?
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @backoff.started
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @self_started = true
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if @pid = fork
         
     | 
| 
      
 56 
     | 
    
         
            +
                    # still in the listener
         
     | 
| 
      
 57 
     | 
    
         
            +
                  else
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # In case we get a signal before the process is all the way up.
         
     | 
| 
      
 59 
     | 
    
         
            +
                    [:QUIT, :TERM, :INT].each { |signal| trap(signal) { exit 1 } }
         
     | 
| 
      
 60 
     | 
    
         
            +
                    $0 = "STARTING RESQUE FOR #{queues.join(',')}"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    if ! log_to_stdout?
         
     | 
| 
      
 62 
     | 
    
         
            +
                      lf = logging_io
         
     | 
| 
      
 63 
     | 
    
         
            +
                      if Resque.respond_to?("logger=")
         
     | 
| 
      
 64 
     | 
    
         
            +
                        Resque.logger = Resque.logger.class.new(lf)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      else
         
     | 
| 
      
 66 
     | 
    
         
            +
                        $stdout.reopen(lf)
         
     | 
| 
      
 67 
     | 
    
         
            +
                        lf.close
         
     | 
| 
      
 68 
     | 
    
         
            +
                      end
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
                    resque_worker = Resque::Worker.new(*queues)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    resque_worker.log "Starting worker #{resque_worker}"
         
     | 
| 
      
 72 
     | 
    
         
            +
                    resque_worker.term_child = true # Hopefully do away with those warnings!
         
     | 
| 
      
 73 
     | 
    
         
            +
                    resque_worker.work(5)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    exit 0
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                # Public: Shut this worker down.
         
     | 
| 
      
 79 
     | 
    
         
            +
                #
         
     | 
| 
      
 80 
     | 
    
         
            +
                # We are using these signal semantics:
         
     | 
| 
      
 81 
     | 
    
         
            +
                # HUP: restart (QUIT workers)
         
     | 
| 
      
 82 
     | 
    
         
            +
                # INT/TERM: immediately exit
         
     | 
| 
      
 83 
     | 
    
         
            +
                # QUIT: graceful shutdown
         
     | 
| 
      
 84 
     | 
    
         
            +
                #
         
     | 
| 
      
 85 
     | 
    
         
            +
                # Resque uses these (compatible) signal semantics:
         
     | 
| 
      
 86 
     | 
    
         
            +
                # TERM: Shutdown immediately, stop processing jobs.
         
     | 
| 
      
 87 
     | 
    
         
            +
                #  INT: Shutdown immediately, stop processing jobs.
         
     | 
| 
      
 88 
     | 
    
         
            +
                # QUIT: Shutdown after the current job has finished processing.
         
     | 
| 
      
 89 
     | 
    
         
            +
                # USR1: Kill the forked child immediately, continue processing jobs.
         
     | 
| 
      
 90 
     | 
    
         
            +
                # USR2: Don't process any new jobs
         
     | 
| 
      
 91 
     | 
    
         
            +
                # CONT: Start processing jobs again after a USR2
         
     | 
| 
      
 92 
     | 
    
         
            +
                def kill(signal)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  Process.kill(signal.to_s, pid) if pid && @self_started
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/resqued.rb
    ADDED
    
    
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,173 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: resqued
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
      
 6 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 7 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Matt Burke
         
     | 
| 
      
 9 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 10 
     | 
    
         
            +
            bindir: exe
         
     | 
| 
      
 11 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2013-08-20 00:00:00.000000000 Z
         
     | 
| 
      
 13 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 14 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 15 
     | 
    
         
            +
              name: kgio
         
     | 
| 
      
 16 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 17 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 18 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 19 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 20 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 21 
     | 
    
         
            +
                    version: '2.6'
         
     | 
| 
      
 22 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 23 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 24 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 25 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 26 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 27 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 28 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 29 
     | 
    
         
            +
                    version: '2.6'
         
     | 
| 
      
 30 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 31 
     | 
    
         
            +
              name: resque
         
     | 
| 
      
 32 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 33 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 34 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 35 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 36 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 37 
     | 
    
         
            +
                    version: 1.22.0
         
     | 
| 
      
 38 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 39 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 40 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 41 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 42 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 43 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 44 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 45 
     | 
    
         
            +
                    version: 1.22.0
         
     | 
| 
      
 46 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 47 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 48 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 49 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 50 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 51 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 52 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 53 
     | 
    
         
            +
                    version: '2.0'
         
     | 
| 
      
 54 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 55 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 56 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 57 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '2.0'
         
     | 
| 
      
 62 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 63 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 64 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 66 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 67 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 68 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 69 
     | 
    
         
            +
                    version: 0.9.0
         
     | 
| 
      
 70 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 71 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 72 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 73 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 74 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 75 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 76 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 77 
     | 
    
         
            +
                    version: 0.9.0
         
     | 
| 
      
 78 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 79 
     | 
    
         
            +
              name: guard-rspec
         
     | 
| 
      
 80 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 81 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 82 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 83 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 84 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 85 
     | 
    
         
            +
                    version: 2.4.1
         
     | 
| 
      
 86 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 87 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 88 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 89 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 90 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 91 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 92 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 93 
     | 
    
         
            +
                    version: 2.4.1
         
     | 
| 
      
 94 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 95 
     | 
    
         
            +
              name: guard-bundler
         
     | 
| 
      
 96 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 97 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 98 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 99 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 100 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 101 
     | 
    
         
            +
                    version: 1.0.0
         
     | 
| 
      
 102 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 103 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 104 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 105 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 106 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 107 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 108 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 109 
     | 
    
         
            +
                    version: 1.0.0
         
     | 
| 
      
 110 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 111 
     | 
    
         
            +
              name: rb-fsevent
         
     | 
| 
      
 112 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 113 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 114 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 116 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 118 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 119 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 120 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 121 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 122 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 123 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 124 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 125 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 126 
     | 
    
         
            +
            description: Daemon of resque workers
         
     | 
| 
      
 127 
     | 
    
         
            +
            email: spraints@gmail.com
         
     | 
| 
      
 128 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 129 
     | 
    
         
            +
            - resqued
         
     | 
| 
      
 130 
     | 
    
         
            +
            - resqued-listener
         
     | 
| 
      
 131 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 132 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 133 
     | 
    
         
            +
            files:
         
     | 
| 
      
 134 
     | 
    
         
            +
            - lib/resqued/backoff.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - lib/resqued/config.rb
         
     | 
| 
      
 136 
     | 
    
         
            +
            - lib/resqued/daemon.rb
         
     | 
| 
      
 137 
     | 
    
         
            +
            - lib/resqued/listener.rb
         
     | 
| 
      
 138 
     | 
    
         
            +
            - lib/resqued/listener_proxy.rb
         
     | 
| 
      
 139 
     | 
    
         
            +
            - lib/resqued/logging.rb
         
     | 
| 
      
 140 
     | 
    
         
            +
            - lib/resqued/master.rb
         
     | 
| 
      
 141 
     | 
    
         
            +
            - lib/resqued/pidfile.rb
         
     | 
| 
      
 142 
     | 
    
         
            +
            - lib/resqued/sleepy.rb
         
     | 
| 
      
 143 
     | 
    
         
            +
            - lib/resqued/version.rb
         
     | 
| 
      
 144 
     | 
    
         
            +
            - lib/resqued/worker.rb
         
     | 
| 
      
 145 
     | 
    
         
            +
            - lib/resqued.rb
         
     | 
| 
      
 146 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 147 
     | 
    
         
            +
            - exe/resqued
         
     | 
| 
      
 148 
     | 
    
         
            +
            - exe/resqued-listener
         
     | 
| 
      
 149 
     | 
    
         
            +
            homepage: https://github.com
         
     | 
| 
      
 150 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 151 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 152 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 153 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 154 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 155 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 156 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 157 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 158 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 159 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 160 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 161 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 162 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 163 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 164 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 165 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 166 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 167 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 168 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 169 
     | 
    
         
            +
            rubygems_version: 1.8.23
         
     | 
| 
      
 170 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 171 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 172 
     | 
    
         
            +
            summary: Daemon of resque workers
         
     | 
| 
      
 173 
     | 
    
         
            +
            test_files: []
         
     |