delayed_job 4.0.2 → 4.0.3
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.
- checksums.yaml +5 -13
 - data/CHANGELOG.md +7 -0
 - data/README.md +12 -7
 - data/Rakefile +6 -1
 - data/delayed_job.gemspec +5 -5
 - data/lib/delayed/backend/base.rb +18 -20
 - data/lib/delayed/backend/shared_spec.rb +139 -138
 - data/lib/delayed/command.rb +75 -40
 - data/lib/delayed/exceptions.rb +2 -1
 - data/lib/delayed/lifecycle.rb +12 -11
 - data/lib/delayed/message_sending.rb +9 -10
 - data/lib/delayed/performable_mailer.rb +2 -2
 - data/lib/delayed/performable_method.rb +2 -2
 - data/lib/delayed/psych_ext.rb +29 -93
 - data/lib/delayed/recipes.rb +5 -5
 - data/lib/delayed/serialization/active_record.rb +11 -9
 - data/lib/delayed/syck_ext.rb +3 -3
 - data/lib/delayed/tasks.rb +5 -5
 - data/lib/delayed/worker.rb +42 -42
 - data/lib/generators/delayed_job/delayed_job_generator.rb +2 -3
 - data/spec/delayed/backend/test.rb +22 -17
 - data/spec/delayed/command_spec.rb +57 -0
 - data/spec/helper.rb +25 -12
 - data/spec/lifecycle_spec.rb +23 -15
 - data/spec/message_sending_spec.rb +34 -34
 - data/spec/performable_mailer_spec.rb +11 -11
 - data/spec/performable_method_spec.rb +24 -26
 - data/spec/psych_ext_spec.rb +12 -0
 - data/spec/sample_jobs.rb +46 -18
 - data/spec/test_backend_spec.rb +3 -3
 - data/spec/worker_spec.rb +27 -27
 - data/spec/yaml_ext_spec.rb +16 -16
 - metadata +12 -8
 
    
        data/lib/delayed/recipes.rb
    CHANGED
    
    | 
         @@ -25,7 +25,7 @@ Capistrano::Configuration.instance.load do 
     | 
|
| 
       25 
25 
     | 
    
         
             
                end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                def args
         
     | 
| 
       28 
     | 
    
         
            -
                  fetch(:delayed_job_args,  
     | 
| 
      
 28 
     | 
    
         
            +
                  fetch(:delayed_job_args, '')
         
     | 
| 
       29 
29 
     | 
    
         
             
                end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                def roles
         
     | 
| 
         @@ -33,20 +33,20 @@ Capistrano::Configuration.instance.load do 
     | 
|
| 
       33 
33 
     | 
    
         
             
                end
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                def delayed_job_command
         
     | 
| 
       36 
     | 
    
         
            -
                  fetch(:delayed_job_command,  
     | 
| 
      
 36 
     | 
    
         
            +
                  fetch(:delayed_job_command, 'script/delayed_job')
         
     | 
| 
       37 
37 
     | 
    
         
             
                end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                desc  
     | 
| 
      
 39 
     | 
    
         
            +
                desc 'Stop the delayed_job process'
         
     | 
| 
       40 
40 
     | 
    
         
             
                task :stop, :roles => lambda { roles } do
         
     | 
| 
       41 
41 
     | 
    
         
             
                  run "cd #{current_path};#{rails_env} #{delayed_job_command} stop"
         
     | 
| 
       42 
42 
     | 
    
         
             
                end
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                desc  
     | 
| 
      
 44 
     | 
    
         
            +
                desc 'Start the delayed_job process'
         
     | 
| 
       45 
45 
     | 
    
         
             
                task :start, :roles => lambda { roles } do
         
     | 
| 
       46 
46 
     | 
    
         
             
                  run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}"
         
     | 
| 
       47 
47 
     | 
    
         
             
                end
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                desc  
     | 
| 
      
 49 
     | 
    
         
            +
                desc 'Restart the delayed_job process'
         
     | 
| 
       50 
50 
     | 
    
         
             
                task :restart, :roles => lambda { roles } do
         
     | 
| 
       51 
51 
     | 
    
         
             
                  run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}"
         
     | 
| 
       52 
52 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,15 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            if defined?(ActiveRecord)
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
      
 2 
     | 
    
         
            +
              module ActiveRecord
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 4 
     | 
    
         
            +
                  yaml_as 'tag:ruby.yaml.org,2002:ActiveRecord'
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.yaml_new(klass, _tag, val)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    klass.unscoped.find(val['attributes'][klass.primary_key])
         
     | 
| 
      
 8 
     | 
    
         
            +
                  rescue ActiveRecord::RecordNotFound
         
     | 
| 
      
 9 
     | 
    
         
            +
                    raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]}"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def to_yaml_properties
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ['@attributes']
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
       13 
15 
     | 
    
         
             
                end
         
     | 
| 
       14 
16 
     | 
    
         
             
              end
         
     | 
| 
       15 
17 
     | 
    
         
             
            end
         
     | 
    
        data/lib/delayed/syck_ext.rb
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Module
         
     | 
| 
       2 
     | 
    
         
            -
              yaml_as  
     | 
| 
      
 2 
     | 
    
         
            +
              yaml_as 'tag:ruby.yaml.org,2002:module'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
              def self.yaml_new( 
     | 
| 
      
 4 
     | 
    
         
            +
              def self.yaml_new(_klass, _tag, val)
         
     | 
| 
       5 
5 
     | 
    
         
             
                val.constantize
         
     | 
| 
       6 
6 
     | 
    
         
             
              end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
         @@ -20,7 +20,7 @@ class Module 
     | 
|
| 
       20 
20 
     | 
    
         
             
            end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
            class Class
         
     | 
| 
       23 
     | 
    
         
            -
              yaml_as  
     | 
| 
      
 23 
     | 
    
         
            +
              yaml_as 'tag:ruby.yaml.org,2002:class'
         
     | 
| 
       24 
24 
     | 
    
         
             
              remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml
         
     | 
| 
       25 
25 
     | 
    
         
             
            end
         
     | 
| 
       26 
26 
     | 
    
         | 
    
        data/lib/delayed/tasks.rb
    CHANGED
    
    | 
         @@ -1,17 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            namespace :jobs do
         
     | 
| 
       2 
     | 
    
         
            -
              desc  
     | 
| 
      
 2 
     | 
    
         
            +
              desc 'Clear the delayed_job queue.'
         
     | 
| 
       3 
3 
     | 
    
         
             
              task :clear => :environment do
         
     | 
| 
       4 
4 
     | 
    
         
             
                Delayed::Job.delete_all
         
     | 
| 
       5 
5 
     | 
    
         
             
              end
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
              desc  
     | 
| 
      
 7 
     | 
    
         
            +
              desc 'Start a delayed_job worker.'
         
     | 
| 
       8 
8 
     | 
    
         
             
              task :work => :environment_options do
         
     | 
| 
       9 
9 
     | 
    
         
             
                Delayed::Worker.new(@worker_options).start
         
     | 
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              desc  
     | 
| 
      
 12 
     | 
    
         
            +
              desc 'Start a delayed_job worker and exit when all available jobs are complete.'
         
     | 
| 
       13 
13 
     | 
    
         
             
              task :workoff => :environment_options do
         
     | 
| 
       14 
     | 
    
         
            -
                Delayed::Worker.new(@worker_options.merge( 
     | 
| 
      
 14 
     | 
    
         
            +
                Delayed::Worker.new(@worker_options.merge(:exit_on_complete => true)).start
         
     | 
| 
       15 
15 
     | 
    
         
             
              end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
              task :environment_options => :environment do
         
     | 
| 
         @@ -30,7 +30,7 @@ namespace :jobs do 
     | 
|
| 
       30 
30 
     | 
    
         
             
                unprocessed_jobs = Delayed::Job.where('attempts = 0 AND created_at < ?', Time.now - args[:max_age].to_i).count
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
32 
     | 
    
         
             
                if unprocessed_jobs > 0
         
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
      
 33 
     | 
    
         
            +
                  raise "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
         
     | 
| 
       34 
34 
     | 
    
         
             
                end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
              end
         
     | 
    
        data/lib/delayed/worker.rb
    CHANGED
    
    | 
         @@ -7,8 +7,7 @@ require 'logger' 
     | 
|
| 
       7 
7 
     | 
    
         
             
            require 'benchmark'
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            module Delayed
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
              class Worker
         
     | 
| 
      
 10 
     | 
    
         
            +
              class Worker # rubocop:disable ClassLength
         
     | 
| 
       12 
11 
     | 
    
         
             
                DEFAULT_LOG_LEVEL        = 'info'
         
     | 
| 
       13 
12 
     | 
    
         
             
                DEFAULT_SLEEP_DELAY      = 5
         
     | 
| 
       14 
13 
     | 
    
         
             
                DEFAULT_MAX_ATTEMPTS     = 25
         
     | 
| 
         @@ -19,8 +18,8 @@ module Delayed 
     | 
|
| 
       19 
18 
     | 
    
         
             
                DEFAULT_READ_AHEAD       = 5
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
20 
     | 
    
         
             
                cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                               :default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
         
     | 
| 
      
 22 
     | 
    
         
            +
                               :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
         
     | 
| 
       24 
23 
     | 
    
         | 
| 
       25 
24 
     | 
    
         
             
                # Named queue into which jobs are enqueued by default
         
     | 
| 
       26 
25 
     | 
    
         
             
                cattr_accessor :default_queue_name
         
     | 
| 
         @@ -70,12 +69,12 @@ module Delayed 
     | 
|
| 
       70 
69 
     | 
    
         
             
                    require "delayed/backend/#{backend}"
         
     | 
| 
       71 
70 
     | 
    
         
             
                    backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize
         
     | 
| 
       72 
71 
     | 
    
         
             
                  end
         
     | 
| 
       73 
     | 
    
         
            -
                  @@backend = backend
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @@backend = backend # rubocop:disable ClassVars
         
     | 
| 
       74 
73 
     | 
    
         
             
                  silence_warnings { ::Delayed.const_set(:Job, backend) }
         
     | 
| 
       75 
74 
     | 
    
         
             
                end
         
     | 
| 
       76 
75 
     | 
    
         | 
| 
       77 
76 
     | 
    
         
             
                def self.guess_backend
         
     | 
| 
       78 
     | 
    
         
            -
                  warn  
     | 
| 
      
 77 
     | 
    
         
            +
                  warn '[DEPRECATION] guess_backend is deprecated. Please remove it from your code.'
         
     | 
| 
       79 
78 
     | 
    
         
             
                end
         
     | 
| 
       80 
79 
     | 
    
         | 
| 
       81 
80 
     | 
    
         
             
                def self.before_fork
         
     | 
| 
         @@ -93,12 +92,11 @@ module Delayed 
     | 
|
| 
       93 
92 
     | 
    
         
             
                  # Re-open file handles
         
     | 
| 
       94 
93 
     | 
    
         
             
                  @files_to_reopen.each do |file|
         
     | 
| 
       95 
94 
     | 
    
         
             
                    begin
         
     | 
| 
       96 
     | 
    
         
            -
                      file.reopen file.path,  
     | 
| 
      
 95 
     | 
    
         
            +
                      file.reopen file.path, 'a+'
         
     | 
| 
       97 
96 
     | 
    
         
             
                      file.sync = true
         
     | 
| 
       98 
     | 
    
         
            -
                    rescue ::Exception
         
     | 
| 
      
 97 
     | 
    
         
            +
                    rescue ::Exception # rubocop:disable HandleExceptions, RescueException
         
     | 
| 
       99 
98 
     | 
    
         
             
                    end
         
     | 
| 
       100 
99 
     | 
    
         
             
                  end
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
100 
     | 
    
         
             
                  backend.after_fork
         
     | 
| 
       103 
101 
     | 
    
         
             
                end
         
     | 
| 
       104 
102 
     | 
    
         | 
| 
         @@ -106,15 +104,15 @@ module Delayed 
     | 
|
| 
       106 
104 
     | 
    
         
             
                  @lifecycle ||= Delayed::Lifecycle.new
         
     | 
| 
       107 
105 
     | 
    
         
             
                end
         
     | 
| 
       108 
106 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
                def initialize(options={})
         
     | 
| 
       110 
     | 
    
         
            -
                  @quiet = options. 
     | 
| 
      
 107 
     | 
    
         
            +
                def initialize(options = {})
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @quiet = options.key?(:quiet) ? options[:quiet] : true
         
     | 
| 
       111 
109 
     | 
    
         
             
                  @failed_reserve_count = 0
         
     | 
| 
       112 
110 
     | 
    
         | 
| 
       113 
111 
     | 
    
         
             
                  [:min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete].each do |option|
         
     | 
| 
       114 
     | 
    
         
            -
                    self.class.send("#{option}=", options[option]) if options. 
     | 
| 
      
 112 
     | 
    
         
            +
                    self.class.send("#{option}=", options[option]) if options.key?(option)
         
     | 
| 
       115 
113 
     | 
    
         
             
                  end
         
     | 
| 
       116 
114 
     | 
    
         | 
| 
       117 
     | 
    
         
            -
                   
     | 
| 
      
 115 
     | 
    
         
            +
                  plugins.each { |klass| klass.new }
         
     | 
| 
       118 
116 
     | 
    
         
             
                end
         
     | 
| 
       119 
117 
     | 
    
         | 
| 
       120 
118 
     | 
    
         
             
                # Every worker has a unique name which by default is the pid of the process. There are some
         
     | 
| 
         @@ -123,29 +121,27 @@ module Delayed 
     | 
|
| 
       123 
121 
     | 
    
         
             
                # it crashed before.
         
     | 
| 
       124 
122 
     | 
    
         
             
                def name
         
     | 
| 
       125 
123 
     | 
    
         
             
                  return @name unless @name.nil?
         
     | 
| 
       126 
     | 
    
         
            -
                  "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}"
         
     | 
| 
      
 124 
     | 
    
         
            +
                  "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}" # rubocop:disable RescueModifier
         
     | 
| 
       127 
125 
     | 
    
         
             
                end
         
     | 
| 
       128 
126 
     | 
    
         | 
| 
       129 
127 
     | 
    
         
             
                # Sets the name of the worker.
         
     | 
| 
       130 
128 
     | 
    
         
             
                # Setting the name to nil will reset the default worker name
         
     | 
| 
       131 
     | 
    
         
            -
                 
     | 
| 
       132 
     | 
    
         
            -
                  @name = val
         
     | 
| 
       133 
     | 
    
         
            -
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
                attr_writer :name
         
     | 
| 
       134 
130 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
                def start
         
     | 
| 
      
 131 
     | 
    
         
            +
                def start # rubocop:disable CyclomaticComplexity, PerceivedComplexity
         
     | 
| 
       136 
132 
     | 
    
         
             
                  trap('TERM') do
         
     | 
| 
       137 
133 
     | 
    
         
             
                    say 'Exiting...'
         
     | 
| 
       138 
134 
     | 
    
         
             
                    stop
         
     | 
| 
       139 
     | 
    
         
            -
                    raise SignalException 
     | 
| 
      
 135 
     | 
    
         
            +
                    raise SignalException, 'TERM' if self.class.raise_signal_exceptions
         
     | 
| 
       140 
136 
     | 
    
         
             
                  end
         
     | 
| 
       141 
137 
     | 
    
         | 
| 
       142 
138 
     | 
    
         
             
                  trap('INT') do
         
     | 
| 
       143 
139 
     | 
    
         
             
                    say 'Exiting...'
         
     | 
| 
       144 
140 
     | 
    
         
             
                    stop
         
     | 
| 
       145 
     | 
    
         
            -
                    raise SignalException 
     | 
| 
      
 141 
     | 
    
         
            +
                    raise SignalException, 'INT' if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
         
     | 
| 
       146 
142 
     | 
    
         
             
                  end
         
     | 
| 
       147 
143 
     | 
    
         | 
| 
       148 
     | 
    
         
            -
                  say  
     | 
| 
      
 144 
     | 
    
         
            +
                  say 'Starting job worker'
         
     | 
| 
       149 
145 
     | 
    
         | 
| 
       150 
146 
     | 
    
         
             
                  self.class.lifecycle.run_callbacks(:execute, self) do
         
     | 
| 
       151 
147 
     | 
    
         
             
                    loop do
         
     | 
| 
         @@ -159,13 +155,13 @@ module Delayed 
     | 
|
| 
       159 
155 
     | 
    
         | 
| 
       160 
156 
     | 
    
         
             
                      if count.zero?
         
     | 
| 
       161 
157 
     | 
    
         
             
                        if self.class.exit_on_complete
         
     | 
| 
       162 
     | 
    
         
            -
                          say  
     | 
| 
      
 158 
     | 
    
         
            +
                          say 'No more jobs available. Exiting'
         
     | 
| 
       163 
159 
     | 
    
         
             
                          break
         
     | 
| 
       164 
     | 
    
         
            -
                         
     | 
| 
       165 
     | 
    
         
            -
                          sleep(self.class.sleep_delay) 
     | 
| 
      
 160 
     | 
    
         
            +
                        elsif !stop?
         
     | 
| 
      
 161 
     | 
    
         
            +
                          sleep(self.class.sleep_delay)
         
     | 
| 
       166 
162 
     | 
    
         
             
                        end
         
     | 
| 
       167 
163 
     | 
    
         
             
                      else
         
     | 
| 
       168 
     | 
    
         
            -
                        say "#{count} jobs processed at %.4f j/s, %d failed"  
     | 
| 
      
 164 
     | 
    
         
            +
                        say format("#{count} jobs processed at %.4f j/s, %d failed", count / @realtime, @result.last)
         
     | 
| 
       169 
165 
     | 
    
         
             
                      end
         
     | 
| 
       170 
166 
     | 
    
         | 
| 
       171 
167 
     | 
    
         
             
                      break if stop?
         
     | 
| 
         @@ -189,16 +185,16 @@ module Delayed 
     | 
|
| 
       189 
185 
     | 
    
         
             
                  num.times do
         
     | 
| 
       190 
186 
     | 
    
         
             
                    case reserve_and_run_one_job
         
     | 
| 
       191 
187 
     | 
    
         
             
                    when true
         
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
      
 188 
     | 
    
         
            +
                      success += 1
         
     | 
| 
       193 
189 
     | 
    
         
             
                    when false
         
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
      
 190 
     | 
    
         
            +
                      failure += 1
         
     | 
| 
       195 
191 
     | 
    
         
             
                    else
         
     | 
| 
       196 
192 
     | 
    
         
             
                      break  # leave if no work could be done
         
     | 
| 
       197 
193 
     | 
    
         
             
                    end
         
     | 
| 
       198 
194 
     | 
    
         
             
                    break if stop? # leave if we're exiting
         
     | 
| 
       199 
195 
     | 
    
         
             
                  end
         
     | 
| 
       200 
196 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                   
     | 
| 
      
 197 
     | 
    
         
            +
                  [success, failure]
         
     | 
| 
       202 
198 
     | 
    
         
             
                end
         
     | 
| 
       203 
199 
     | 
    
         | 
| 
       204 
200 
     | 
    
         
             
                def run(job)
         
     | 
| 
         @@ -207,13 +203,13 @@ module Delayed 
     | 
|
| 
       207 
203 
     | 
    
         
             
                    Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
         
     | 
| 
       208 
204 
     | 
    
         
             
                    job.destroy
         
     | 
| 
       209 
205 
     | 
    
         
             
                  end
         
     | 
| 
       210 
     | 
    
         
            -
                  job_say job, 'COMPLETED after %.4f'  
     | 
| 
      
 206 
     | 
    
         
            +
                  job_say job, format('COMPLETED after %.4f', runtime)
         
     | 
| 
       211 
207 
     | 
    
         
             
                  return true  # did work
         
     | 
| 
       212 
208 
     | 
    
         
             
                rescue DeserializationError => error
         
     | 
| 
       213 
209 
     | 
    
         
             
                  job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
         
     | 
| 
       214 
210 
     | 
    
         
             
                  failed(job)
         
     | 
| 
       215 
     | 
    
         
            -
                rescue  
     | 
| 
       216 
     | 
    
         
            -
                  self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
         
     | 
| 
      
 211 
     | 
    
         
            +
                rescue => error
         
     | 
| 
      
 212 
     | 
    
         
            +
                  self.class.lifecycle.run_callbacks(:error, self, job) { handle_failed_job(job, error) }
         
     | 
| 
       217 
213 
     | 
    
         
             
                  return false  # work failed
         
     | 
| 
       218 
214 
     | 
    
         
             
                end
         
     | 
| 
       219 
215 
     | 
    
         | 
| 
         @@ -233,8 +229,14 @@ module Delayed 
     | 
|
| 
       233 
229 
     | 
    
         | 
| 
       234 
230 
     | 
    
         
             
                def failed(job)
         
     | 
| 
       235 
231 
     | 
    
         
             
                  self.class.lifecycle.run_callbacks(:failure, self, job) do
         
     | 
| 
       236 
     | 
    
         
            -
                     
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
      
 232 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 233 
     | 
    
         
            +
                      job.hook(:failure)
         
     | 
| 
      
 234 
     | 
    
         
            +
                    rescue => error
         
     | 
| 
      
 235 
     | 
    
         
            +
                      say "Error when running failure callback: #{error}", 'error'
         
     | 
| 
      
 236 
     | 
    
         
            +
                      say error.backtrace.join("\n"), 'error'
         
     | 
| 
      
 237 
     | 
    
         
            +
                    ensure
         
     | 
| 
      
 238 
     | 
    
         
            +
                      self.class.destroy_failed_jobs ? job.destroy : job.fail!
         
     | 
| 
      
 239 
     | 
    
         
            +
                    end
         
     | 
| 
       238 
240 
     | 
    
         
             
                  end
         
     | 
| 
       239 
241 
     | 
    
         
             
                end
         
     | 
| 
       240 
242 
     | 
    
         | 
| 
         @@ -246,13 +248,12 @@ module Delayed 
     | 
|
| 
       246 
248 
     | 
    
         
             
                def say(text, level = DEFAULT_LOG_LEVEL)
         
     | 
| 
       247 
249 
     | 
    
         
             
                  text = "[Worker(#{name})] #{text}"
         
     | 
| 
       248 
250 
     | 
    
         
             
                  puts text unless @quiet
         
     | 
| 
       249 
     | 
    
         
            -
                   
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
                    end
         
     | 
| 
       254 
     | 
    
         
            -
                    logger.send(level, "#{Time.now.strftime('%FT%T%z')}: #{text}")
         
     | 
| 
      
 251 
     | 
    
         
            +
                  return unless logger
         
     | 
| 
      
 252 
     | 
    
         
            +
                  # TODO: Deprecate use of Fixnum log levels
         
     | 
| 
      
 253 
     | 
    
         
            +
                  unless level.is_a?(String)
         
     | 
| 
      
 254 
     | 
    
         
            +
                    level = Logger::Severity.constants.detect { |i| Logger::Severity.const_get(i) == level }.to_s.downcase
         
     | 
| 
       255 
255 
     | 
    
         
             
                  end
         
     | 
| 
      
 256 
     | 
    
         
            +
                  logger.send(level, "#{Time.now.strftime('%FT%T%z')}: #{text}")
         
     | 
| 
       256 
257 
     | 
    
         
             
                end
         
     | 
| 
       257 
258 
     | 
    
         | 
| 
       258 
259 
     | 
    
         
             
                def max_attempts(job)
         
     | 
| 
         @@ -271,14 +272,14 @@ module Delayed 
     | 
|
| 
       271 
272 
     | 
    
         
             
                # If no jobs are left we return nil
         
     | 
| 
       272 
273 
     | 
    
         
             
                def reserve_and_run_one_job
         
     | 
| 
       273 
274 
     | 
    
         
             
                  job = reserve_job
         
     | 
| 
       274 
     | 
    
         
            -
                  self.class.lifecycle.run_callbacks(:perform, self, job){ run(job) } if job
         
     | 
| 
      
 275 
     | 
    
         
            +
                  self.class.lifecycle.run_callbacks(:perform, self, job) { run(job) } if job
         
     | 
| 
       275 
276 
     | 
    
         
             
                end
         
     | 
| 
       276 
277 
     | 
    
         | 
| 
       277 
278 
     | 
    
         
             
                def reserve_job
         
     | 
| 
       278 
279 
     | 
    
         
             
                  job = Delayed::Job.reserve(self)
         
     | 
| 
       279 
280 
     | 
    
         
             
                  @failed_reserve_count = 0
         
     | 
| 
       280 
281 
     | 
    
         
             
                  job
         
     | 
| 
       281 
     | 
    
         
            -
                rescue Exception => error
         
     | 
| 
      
 282 
     | 
    
         
            +
                rescue ::Exception => error # rubocop:disable RescueException
         
     | 
| 
       282 
283 
     | 
    
         
             
                  say "Error while reserving job: #{error}"
         
     | 
| 
       283 
284 
     | 
    
         
             
                  Delayed::Job.recover_from(error)
         
     | 
| 
       284 
285 
     | 
    
         
             
                  @failed_reserve_count += 1
         
     | 
| 
         @@ -286,5 +287,4 @@ module Delayed 
     | 
|
| 
       286 
287 
     | 
    
         
             
                  nil
         
     | 
| 
       287 
288 
     | 
    
         
             
                end
         
     | 
| 
       288 
289 
     | 
    
         
             
              end
         
     | 
| 
       289 
     | 
    
         
            -
             
     | 
| 
       290 
290 
     | 
    
         
             
            end
         
     | 
| 
         @@ -2,11 +2,10 @@ require 'rails/generators' 
     | 
|
| 
       2 
2 
     | 
    
         
             
            require 'delayed/compatibility'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            class DelayedJobGenerator < Rails::Generators::Base
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
              self.source_paths << File.join(File.dirname(__FILE__), 'templates')
         
     | 
| 
      
 5 
     | 
    
         
            +
              source_paths << File.join(File.dirname(__FILE__), 'templates')
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
7 
     | 
    
         
             
              def create_executable_file
         
     | 
| 
       9 
     | 
    
         
            -
                template  
     | 
| 
      
 8 
     | 
    
         
            +
                template 'script', "#{Delayed::Compatibility.executable_prefix}/delayed_job"
         
     | 
| 
       10 
9 
     | 
    
         
             
                chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755
         
     | 
| 
       11 
10 
     | 
    
         
             
              end
         
     | 
| 
       12 
11 
     | 
    
         
             
            end
         
     | 
| 
         @@ -25,12 +25,11 @@ module Delayed 
     | 
|
| 
       25 
25 
     | 
    
         
             
                      self.attempts = 0
         
     | 
| 
       26 
26 
     | 
    
         
             
                      self.priority = 0
         
     | 
| 
       27 
27 
     | 
    
         
             
                      self.id = (self.class.id += 1)
         
     | 
| 
       28 
     | 
    
         
            -
                      hash.each{|k,v| send(:"#{k}=", v)}
         
     | 
| 
      
 28 
     | 
    
         
            +
                      hash.each { |k, v| send(:"#{k}=", v) }
         
     | 
| 
       29 
29 
     | 
    
         
             
                    end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
                    @jobs = []
         
     | 
| 
       32 
31 
     | 
    
         
             
                    def self.all
         
     | 
| 
       33 
     | 
    
         
            -
                      @jobs
         
     | 
| 
      
 32 
     | 
    
         
            +
                      @jobs ||= []
         
     | 
| 
       34 
33 
     | 
    
         
             
                    end
         
     | 
| 
       35 
34 
     | 
    
         | 
| 
       36 
35 
     | 
    
         
             
                    def self.count
         
     | 
| 
         @@ -47,29 +46,33 @@ module Delayed 
     | 
|
| 
       47 
46 
     | 
    
         
             
                      end
         
     | 
| 
       48 
47 
     | 
    
         
             
                    end
         
     | 
| 
       49 
48 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
                    def self.create!(*args) 
     | 
| 
      
 49 
     | 
    
         
            +
                    def self.create!(*args)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      create(*args)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
       51 
52 
     | 
    
         | 
| 
       52 
53 
     | 
    
         
             
                    def self.clear_locks!(worker_name)
         
     | 
| 
       53 
     | 
    
         
            -
                      all.select{|j| j.locked_by == worker_name}.each  
     | 
| 
      
 54 
     | 
    
         
            +
                      all.select { |j| j.locked_by == worker_name }.each do |j|
         
     | 
| 
      
 55 
     | 
    
         
            +
                        j.locked_by = nil
         
     | 
| 
      
 56 
     | 
    
         
            +
                        j.locked_at = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                      end
         
     | 
| 
       54 
58 
     | 
    
         
             
                    end
         
     | 
| 
       55 
59 
     | 
    
         | 
| 
       56 
60 
     | 
    
         
             
                    # Find a few candidate jobs to run (in case some immediately get locked by others).
         
     | 
| 
       57 
     | 
    
         
            -
                    def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) # rubocop:disable CyclomaticComplexity, PerceivedComplexity
         
     | 
| 
       58 
62 
     | 
    
         
             
                      jobs = all.select do |j|
         
     | 
| 
       59 
63 
     | 
    
         
             
                        j.run_at <= db_time_now &&
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
      
 64 
     | 
    
         
            +
                          (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
         
     | 
| 
      
 65 
     | 
    
         
            +
                          !j.failed?
         
     | 
| 
       62 
66 
     | 
    
         
             
                      end
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                      jobs 
     | 
| 
       65 
     | 
    
         
            -
                      jobs 
     | 
| 
       66 
     | 
    
         
            -
                      jobs  
     | 
| 
       67 
     | 
    
         
            -
                      jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1]
         
     | 
| 
      
 67 
     | 
    
         
            +
                      jobs.select! { |j| j.priority <= Worker.max_priority } if Worker.max_priority
         
     | 
| 
      
 68 
     | 
    
         
            +
                      jobs.select! { |j| j.priority >= Worker.min_priority } if Worker.min_priority
         
     | 
| 
      
 69 
     | 
    
         
            +
                      jobs.select! { |j| Worker.queues.include?(j.queue) } if Worker.queues.any?
         
     | 
| 
      
 70 
     | 
    
         
            +
                      jobs.sort_by! { |j| [j.priority, j.run_at] }[0..limit - 1]
         
     | 
| 
       68 
71 
     | 
    
         
             
                    end
         
     | 
| 
       69 
72 
     | 
    
         | 
| 
       70 
73 
     | 
    
         
             
                    # Lock this job for this worker.
         
     | 
| 
       71 
74 
     | 
    
         
             
                    # Returns true if we have the lock, false otherwise.
         
     | 
| 
       72 
     | 
    
         
            -
                    def lock_exclusively!( 
     | 
| 
      
 75 
     | 
    
         
            +
                    def lock_exclusively!(_max_run_time, worker)
         
     | 
| 
       73 
76 
     | 
    
         
             
                      now = self.class.db_time_now
         
     | 
| 
       74 
77 
     | 
    
         
             
                      if locked_by != worker
         
     | 
| 
       75 
78 
     | 
    
         
             
                        # We don't own this job so we will update the locked_by name and the locked_at
         
     | 
| 
         @@ -77,7 +80,7 @@ module Delayed 
     | 
|
| 
       77 
80 
     | 
    
         
             
                        self.locked_by = worker
         
     | 
| 
       78 
81 
     | 
    
         
             
                      end
         
     | 
| 
       79 
82 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                       
     | 
| 
      
 83 
     | 
    
         
            +
                      true
         
     | 
| 
       81 
84 
     | 
    
         
             
                    end
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
       83 
86 
     | 
    
         
             
                    def self.db_time_now
         
     | 
| 
         @@ -85,7 +88,7 @@ module Delayed 
     | 
|
| 
       85 
88 
     | 
    
         
             
                    end
         
     | 
| 
       86 
89 
     | 
    
         | 
| 
       87 
90 
     | 
    
         
             
                    def update_attributes(attrs = {})
         
     | 
| 
       88 
     | 
    
         
            -
                      attrs.each{|k,v| send(:"#{k}=", v)}
         
     | 
| 
      
 91 
     | 
    
         
            +
                      attrs.each { |k, v| send(:"#{k}=", v) }
         
     | 
| 
       89 
92 
     | 
    
         
             
                      save
         
     | 
| 
       90 
93 
     | 
    
         
             
                    end
         
     | 
| 
       91 
94 
     | 
    
         | 
| 
         @@ -100,7 +103,9 @@ module Delayed 
     | 
|
| 
       100 
103 
     | 
    
         
             
                      true
         
     | 
| 
       101 
104 
     | 
    
         
             
                    end
         
     | 
| 
       102 
105 
     | 
    
         | 
| 
       103 
     | 
    
         
            -
                    def save 
     | 
| 
      
 106 
     | 
    
         
            +
                    def save!
         
     | 
| 
      
 107 
     | 
    
         
            +
                      save
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
       104 
109 
     | 
    
         | 
| 
       105 
110 
     | 
    
         
             
                    def reload
         
     | 
| 
       106 
111 
     | 
    
         
             
                      reset
         
     | 
| 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'delayed/command'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Delayed::Command do
         
     | 
| 
      
 5 
     | 
    
         
            +
              describe 'parsing --pool argument' do
         
     | 
| 
      
 6 
     | 
    
         
            +
                it 'should parse --pool correctly' do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  expect(command.worker_pools).to eq [
         
     | 
| 
      
 10 
     | 
    
         
            +
                    [[], 1],
         
     | 
| 
      
 11 
     | 
    
         
            +
                    [['test_queue'], 4],
         
     | 
| 
      
 12 
     | 
    
         
            +
                    [%w[mailers misc], 2]
         
     | 
| 
      
 13 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                it 'should allow * or blank to specify any pools' do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  command = Delayed::Command.new(['--pool=*:4'])
         
     | 
| 
      
 18 
     | 
    
         
            +
                  expect(command.worker_pools).to eq [
         
     | 
| 
      
 19 
     | 
    
         
            +
                    [[], 4],
         
     | 
| 
      
 20 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  command = Delayed::Command.new(['--pool=:4'])
         
     | 
| 
      
 23 
     | 
    
         
            +
                  expect(command.worker_pools).to eq [
         
     | 
| 
      
 24 
     | 
    
         
            +
                    [[], 4],
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                it 'should default to one worker if not specified' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  command = Delayed::Command.new(['--pool=mailers'])
         
     | 
| 
      
 30 
     | 
    
         
            +
                  expect(command.worker_pools).to eq [
         
     | 
| 
      
 31 
     | 
    
         
            +
                    [['mailers'], 1],
         
     | 
| 
      
 32 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              describe 'running worker pools defined by multiple --pool arguments' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                it 'should run the correct worker processes' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  expect(Dir).to receive(:mkdir).with('./tmp/pids').once
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  [
         
     | 
| 
      
 43 
     | 
    
         
            +
                    ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues => []}],
         
     | 
| 
      
 44 
     | 
    
         
            +
                    ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
         
     | 
| 
      
 45 
     | 
    
         
            +
                    ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
         
     | 
| 
      
 46 
     | 
    
         
            +
                    ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
         
     | 
| 
      
 47 
     | 
    
         
            +
                    ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
         
     | 
| 
      
 48 
     | 
    
         
            +
                    ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}],
         
     | 
| 
      
 49 
     | 
    
         
            +
                    ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}]
         
     | 
| 
      
 50 
     | 
    
         
            +
                  ].each do |args|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    expect(command).to receive(:run_process).with(*args).once
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  command.daemonize
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     |