commit rubygem-delayed_job for openSUSE:Factory
Hello community, here is the log from the commit of package rubygem-delayed_job for openSUSE:Factory checked in at 2015-09-27 08:40:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-delayed_job (Old) and /work/SRC/openSUSE:Factory/.rubygem-delayed_job.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "rubygem-delayed_job" Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-delayed_job/rubygem-delayed_job.changes 2015-02-11 16:45:19.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-delayed_job.new/rubygem-delayed_job.changes 2015-09-27 08:39:42.000000000 +0200 @@ -1,0 +2,26 @@ +Fri Sep 25 04:28:48 UTC 2015 - coolo@suse.com + +- updated to version 4.1.1 + see installed CHANGELOG.md + + 4.1.1 - 2015-09-24 + ================== + * Fix shared specs for back-ends that reload objects + +------------------------------------------------------------------- +Wed Sep 23 04:29:11 UTC 2015 - coolo@suse.com + +- updated to version 4.1.0 + see installed CHANGELOG.md + + 4.1.0 - 2015-09-22 + ================== + * Alter `Delayed::Command` to work with or without Rails + * Allow `Delayed::Worker.delay_jobs` configuration to be a proc + * Add ability to set destroy failed jobs on a per job basis + * Make `Delayed::Worker.new` idempotent + * Set quiet from the environment + * Rescue `Exception` instead of `StandardError` in worker + * Fix worker crash on serialization error + +------------------------------------------------------------------- Old: ---- delayed_job-4.0.6.gem New: ---- delayed_job-4.1.1.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-delayed_job.spec ++++++ --- /var/tmp/diff_new_pack.uv6hMd/_old 2015-09-27 08:39:42.000000000 +0200 +++ /var/tmp/diff_new_pack.uv6hMd/_new 2015-09-27 08:39:42.000000000 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-delayed_job -Version: 4.0.6 +Version: 4.1.1 Release: 0 %define mod_name delayed_job %define mod_full_name %{mod_name}-%{version} ++++++ delayed_job-4.0.6.gem -> delayed_job-4.1.1.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md --- old/CHANGELOG.md 2014-12-22 14:22:31.000000000 +0100 +++ new/CHANGELOG.md 2015-09-24 23:40:11.000000000 +0200 @@ -1,3 +1,17 @@ +4.1.1 - 2015-09-24 +================== +* Fix shared specs for back-ends that reload objects + +4.1.0 - 2015-09-22 +================== +* Alter `Delayed::Command` to work with or without Rails +* Allow `Delayed::Worker.delay_jobs` configuration to be a proc +* Add ability to set destroy failed jobs on a per job basis +* Make `Delayed::Worker.new` idempotent +* Set quiet from the environment +* Rescue `Exception` instead of `StandardError` in worker +* Fix worker crash on serialization error + 4.0.6 - 2014-12-22 ================== * Revert removing test files from the gem diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2014-12-22 14:22:31.000000000 +0100 +++ new/README.md 2015-09-24 23:40:11.000000000 +0200 @@ -1,3 +1,8 @@ +**If you're viewing this at https://github.com/collectiveidea/delayed_job, +you're reading the documentation for the master branch. +[View documentation for the latest release +(4.0.6).](https://github.com/collectiveidea/delayed_job/tree/v4.0.6)** + Delayed::Job ============ [![Gem Version](https://badge.fury.io/rb/delayed_job.png)][gem] @@ -58,14 +63,30 @@ rails generate delayed_job:active_record rake db:migrate +For Rails 4.2, see [below](#rails-42) + Development =========== In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes. You no longer need to restart Delayed Job every time you update your code in development. -Rails 4 -======= -If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. +Rails 4.2 +========= +Set the queue_adapter in config/application.rb + +```ruby +config.active_job.queue_adapter = :delayed_job +``` + +See the [rails guide](http://guides.rubyonrails.org/active_job_basics.html#setting-the-backend) for more details. + +Rails 4.x +========= +If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. If your jobs are failing with: + + ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column "handler" violates not-null constraint + +then this is the fix you're looking for. Upgrading from 2.x to 3.0.0 on Active Record ============================================ @@ -103,9 +124,17 @@ device.deliver ``` -handle_asynchronously can take as options anything you can pass to delay. In -addition, the values can be Proc objects allowing call time evaluation of the -value. For some examples: +## Parameters + +`#handle_asynchronously` and `#delay` take these parameters: + +- `:priority` (number): lower numbers run first; default is 0 but can be reconfigured (see below) +- `:run_at` (Time): run the job after this time (probably in the future) +- `:queue` (string): named queue to put this job in, an alternative to priorities (see below) + +These params can be Proc objects, allowing call-time evaluation of the value. + +For example: ```ruby class LongTasks @@ -282,6 +311,20 @@ end ``` +To set a per-job default for destroying failed jobs that overrides the Delayed::Worker.destroy_failed_jobs you can define a destroy_failed_jobs? method on the job + +```ruby +NewsletterJob = Struct.new(:text, :emails) do + def perform + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) } + end + + def destroy_failed_jobs? + false + end +end +``` + To set a default queue name for a custom job that overrides Delayed::Worker.default_queue_name, you can define a queue_name method on the job ```ruby @@ -384,6 +427,14 @@ It is possible to disable delayed jobs for testing purposes. Set `Delayed::Worker.delay_jobs = false` to execute all jobs realtime. +Or `Delayed::Worker.delay_jobs` can be a Proc that decides whether to execute jobs inline on a per-job basis: + +```ruby +Delayed::Worker.delay_jobs = ->(job) { + job.queue != 'inline' +} +``` + You may need to raise exceptions on SIGTERM signals, `Delayed::Worker.raise_signal_exceptions = :term` will cause the worker to raise a `SignalException` causing the running job to abort and be unlocked, which makes the job available to other workers. The default for this option is false. Here is an example of changing job parameters in Rails: Files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/delayed_job.gemspec new/delayed_job.gemspec --- old/delayed_job.gemspec 2014-12-22 14:22:31.000000000 +0100 +++ new/delayed_job.gemspec 2015-09-24 23:40:11.000000000 +0200 @@ -4,12 +4,12 @@ spec.description = 'Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.' spec.email = ['brian@collectiveidea.com'] spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec] - spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*') + spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*') spec.homepage = 'http://github.com/collectiveidea/delayed_job' spec.licenses = ['MIT'] spec.name = 'delayed_job' spec.require_paths = ['lib'] spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify' spec.test_files = Dir.glob('spec/**/*') - spec.version = '4.0.6' + spec.version = '4.1.1' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/backend/base.rb new/lib/delayed/backend/base.rb --- old/lib/delayed/backend/base.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/backend/base.rb 2015-09-24 23:40:11.000000000 +0200 @@ -10,7 +10,7 @@ def enqueue(*args) # rubocop:disable CyclomaticComplexity options = args.extract_options! options[:payload_object] ||= args.shift - options[:priority] ||= Delayed::Worker.default_priority + options[:priority] ||= Delayed::Worker.default_priority if options[:queue].nil? if options[:payload_object].respond_to?(:queue_name) @@ -32,7 +32,7 @@ new(options).tap do |job| Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do job.hook(:enqueue) - Delayed::Worker.delay_jobs ? job.save : job.invoke_job + Delayed::Worker.delay_job?(job) ? job.save : job.invoke_job end end end @@ -63,6 +63,12 @@ end end + attr_reader :error + def error=(error) + @error = error + self.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" if self.respond_to?(:last_error=) + end + def failed? !!failed_at end @@ -93,7 +99,7 @@ hook :before payload_object.perform hook :success - rescue => e + rescue Exception => e # rubocop:disable RescueException hook :error, e raise e ensure @@ -139,6 +145,12 @@ end end + def destroy_failed_jobs? + payload_object.respond_to?(:destroy_failed_jobs?) ? payload_object.destroy_failed_jobs? : Delayed::Worker.destroy_failed_jobs + rescue DeserializationError + Delayed::Worker.destroy_failed_jobs + end + def fail! update_attributes(:failed_at => self.class.db_time_now) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/backend/shared_spec.rb new/lib/delayed/backend/shared_spec.rb --- old/lib/delayed/backend/shared_spec.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/backend/shared_spec.rb 2015-09-24 23:40:11.000000000 +0200 @@ -161,7 +161,7 @@ job = described_class.enqueue(CallbackJob.new) expect(job.payload_object).to receive(:perform).and_raise(RuntimeError.new('fail')) - expect { job.invoke_job }.to raise_error + expect { job.invoke_job }.to raise_error(RuntimeError) expect(CallbackJob.messages).to eq(['enqueue', 'before', 'error: RuntimeError', 'after']) end @@ -450,6 +450,34 @@ end end + describe 'destroy_failed_jobs' do + context 'with a SimpleJob' do + before(:each) do + @job = described_class.enqueue SimpleJob.new + end + + it 'is not defined' do + expect(@job.destroy_failed_jobs?).to be true + end + + it 'uses the destroy failed jobs value on the payload when defined' do + expect(@job.payload_object).to receive(:destroy_failed_jobs?).and_return(false) + expect(@job.destroy_failed_jobs?).to be false + end + end + + context 'with a job that raises DserializationError' do + before(:each) do + @job = described_class.new :handler => '--- !ruby/struct:GoingToRaiseArgError {}' + end + + it 'falls back reasonably' do + expect(YAML).to receive(:load_dj).and_raise(ArgumentError) + expect(@job.destroy_failed_jobs?).to be true + end + end + end + describe 'yaml serialization' do context 'when serializing jobs' do it 'raises error ArgumentError for new records' do @@ -500,6 +528,7 @@ Delayed::Worker.max_run_time = 1.second job = Delayed::Job.create :payload_object => LongRunningJob.new worker.run(job) + expect(job.error).to_not be_nil expect(job.reload.last_error).to match(/expired/) expect(job.reload.last_error).to match(/Delayed::Worker\.max_run_time is only 1 second/) expect(job.attempts).to eq(1) @@ -513,6 +542,7 @@ it 'marks the job as failed' do Delayed::Worker.destroy_failed_jobs = false job = described_class.create! :handler => '--- !ruby/object:JobThatDoesNotExist {}' + expect_any_instance_of(described_class).to receive(:destroy_failed_jobs?).and_return(false) worker.work_off job.reload expect(job).to be_failed @@ -535,6 +565,7 @@ Delayed::Worker.max_attempts = 1 worker.run(@job) @job.reload + expect(@job.error).to_not be_nil expect(@job.last_error).to match(/did not work/) expect(@job.attempts).to eq(1) expect(@job).to be_failed @@ -617,6 +648,10 @@ end context 'and we want to destroy jobs' do + after do + Delayed::Worker.destroy_failed_jobs = true + end + it_behaves_like 'any failure more than Worker.max_attempts times' it 'is destroyed if it failed more than Worker.max_attempts times' do @@ -624,6 +659,13 @@ Delayed::Worker.max_attempts.times { worker.reschedule(@job) } end + it 'is destroyed if the job has destroy failed jobs set' do + Delayed::Worker.destroy_failed_jobs = false + expect(@job).to receive(:destroy_failed_jobs?).and_return(true) + expect(@job).to receive(:destroy) + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + end + it 'is not destroyed if failed fewer than Worker.max_attempts times' do expect(@job).not_to receive(:destroy) (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } @@ -641,15 +683,35 @@ it_behaves_like 'any failure more than Worker.max_attempts times' - it 'is failed if it failed more than Worker.max_attempts times' do - expect(@job.reload).not_to be_failed - Delayed::Worker.max_attempts.times { worker.reschedule(@job) } - expect(@job.reload).to be_failed + context 'and destroy failed jobs is false' do + it 'is failed if it failed more than Worker.max_attempts times' do + expect(@job.reload).not_to be_failed + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + expect(@job.reload).to be_failed + end + + it 'is not failed if it failed fewer than Worker.max_attempts times' do + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } + expect(@job.reload).not_to be_failed + end end - it 'is not failed if it failed fewer than Worker.max_attempts times' do - (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } - expect(@job.reload).not_to be_failed + context 'and destroy failed jobs for job is false' do + before do + Delayed::Worker.destroy_failed_jobs = true + end + + it 'is failed if it failed more than Worker.max_attempts times' do + expect(@job).to receive(:destroy_failed_jobs?).and_return(false) + expect(@job.reload).not_to be_failed + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + expect(@job.reload).to be_failed + end + + it 'is not failed if it failed fewer than Worker.max_attempts times' do + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } + expect(@job.reload).not_to be_failed + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/command.rb new/lib/delayed/command.rb --- old/lib/delayed/command.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/command.rb 2015-09-24 23:40:11.000000000 +0200 @@ -6,15 +6,19 @@ end end require 'optparse' +require 'pathname' module Delayed class Command # rubocop:disable ClassLength attr_accessor :worker_count, :worker_pools + DIR_PWD = Pathname.new Dir.pwd + def initialize(args) # rubocop:disable MethodLength @options = { :quiet => true, - :pid_dir => "#{Rails.root}/tmp/pids" + :pid_dir => "#{root}/tmp/pids", + :log_dir => "#{root}/log" } @worker_count = 1 @@ -37,11 +41,14 @@ @options[:max_priority] = n end opt.on('-n', '--number_of_workers=workers', 'Number of unique workers to spawn') do |worker_count| - @worker_count = worker_count.to_i rescue 1 # rubocop:disable RescueModifier + @worker_count = worker_count.to_i rescue 1 end opt.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir| @options[:pid_dir] = dir end + opt.on('--log-dir=DIR', 'Specifies an alternate directory in which to store the delayed_job log.') do |dir| + @options[:log_dir] = dir + end opt.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n| @options[:identifier] = n end @@ -114,18 +121,19 @@ end def run(worker_name = nil, options = {}) - Dir.chdir(Rails.root) + Dir.chdir(root) Delayed::Worker.after_fork - Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 'delayed_job.log')) + Delayed::Worker.logger ||= Logger.new(File.join(@options[:log_dir], 'delayed_job.log')) worker = Delayed::Worker.new(options) worker.name_prefix = "#{worker_name} " worker.start rescue => e - Rails.logger.fatal e STDERR.puts e.message - exit 1 + STDERR.puts e.backtrace + ::Rails.logger.fatal(e) if rails_logger_defined? + exit_with_error_status end private @@ -142,5 +150,21 @@ worker_count = (worker_count || 1).to_i rescue 1 @worker_pools << [queues, worker_count] end + + def root + @root ||= rails_root_defined? ? ::Rails.root : DIR_PWD + end + + def rails_root_defined? + defined?(::Rails.root) + end + + def rails_logger_defined? + defined?(::Rails.logger) + end + + def exit_with_error_status + exit 1 + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/message_sending.rb new/lib/delayed/message_sending.rb --- old/lib/delayed/message_sending.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/message_sending.rb 2015-09-24 23:40:11.000000000 +0200 @@ -31,8 +31,10 @@ module ClassMethods def handle_asynchronously(method, opts = {}) - aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 # rubocop:disable PerlBackrefs - with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}" + aliased_method = method.to_s.sub(/([?!=])$/, '') + punctuation = $1 # rubocop:disable PerlBackrefs + with_method = "#{aliased_method}_with_delay#{punctuation}" + without_method = "#{aliased_method}_without_delay#{punctuation}" define_method(with_method) do |*args| curr_opts = opts.clone curr_opts.each_key do |key| diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/recipes.rb new/lib/delayed/recipes.rb --- old/lib/delayed/recipes.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/recipes.rb 2015-09-24 23:40:11.000000000 +0200 @@ -38,17 +38,17 @@ desc 'Stop the delayed_job process' task :stop, :roles => lambda { roles } do - run "cd #{current_path};#{rails_env} #{delayed_job_command} stop #{args}" + run "cd #{current_path} && #{rails_env} #{delayed_job_command} stop #{args}" end desc 'Start the delayed_job process' task :start, :roles => lambda { roles } do - run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}" + run "cd #{current_path} && #{rails_env} #{delayed_job_command} start #{args}" end desc 'Restart the delayed_job process' task :restart, :roles => lambda { roles } do - run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}" + run "cd #{current_path} && #{rails_env} #{delayed_job_command} restart #{args}" end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/syck_ext.rb new/lib/delayed/syck_ext.rb --- old/lib/delayed/syck_ext.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/syck_ext.rb 2015-09-24 23:40:11.000000000 +0200 @@ -29,7 +29,7 @@ # Constantize the object so that ActiveSupport can attempt # its auto loading magic. Will raise LoadError if not successful. name.constantize - "Struct::#{ name }" + "Struct::#{name}" end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/tasks.rb new/lib/delayed/tasks.rb --- old/lib/delayed/tasks.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/tasks.rb 2015-09-24 23:40:11.000000000 +0200 @@ -19,7 +19,7 @@ :min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY'], :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','), - :quiet => false + :quiet => ENV['QUIET'] } @worker_options[:sleep_delay] = ENV['SLEEP_DELAY'].to_i if ENV['SLEEP_DELAY'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/delayed/worker.rb new/lib/delayed/worker.rb --- old/lib/delayed/worker.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/lib/delayed/worker.rb 2015-09-24 23:40:11.000000000 +0200 @@ -39,6 +39,7 @@ self.delay_jobs = DEFAULT_DELAY_JOBS self.queues = DEFAULT_QUEUES self.read_ahead = DEFAULT_READ_AHEAD + @lifecycle = nil end reset @@ -97,13 +98,29 @@ end def self.lifecycle - @lifecycle ||= Delayed::Lifecycle.new + # In case a worker has not been set up, job enqueueing needs a lifecycle. + setup_lifecycle unless @lifecycle + + @lifecycle + end + + def self.setup_lifecycle + @lifecycle = Delayed::Lifecycle.new + plugins.each { |klass| klass.new } end def self.reload_app? defined?(ActionDispatch::Reloader) && Rails.application.config.cache_classes == false end + def self.delay_job?(job) + if delay_jobs.is_a?(Proc) + delay_jobs.arity == 1 ? delay_jobs.call(job) : delay_jobs.call + else + delay_jobs + end + end + def initialize(options = {}) @quiet = options.key?(:quiet) ? options[:quiet] : true @failed_reserve_count = 0 @@ -112,7 +129,9 @@ self.class.send("#{option}=", options[option]) if options.key?(option) end - plugins.each { |klass| klass.new } + # Reset lifecycle on the offhand chance that something lazily + # triggered its creation before all plugins had been registered. + self.class.setup_lifecycle end # Every worker has a unique name which by default is the pid of the process. There are some @@ -121,7 +140,7 @@ # it crashed before. def name return @name unless @name.nil? - "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}" # rubocop:disable RescueModifier + "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}" end # Sets the name of the worker. @@ -181,7 +200,8 @@ # Do num jobs and return stats on success/failure. # Exit early if interrupted. def work_off(num = 100) - success, failure = 0, 0 + success = 0 + failure = 0 num.times do case reserve_and_run_one_job @@ -190,7 +210,7 @@ when false failure += 1 else - break # leave if no work could be done + break # leave if no work could be done end break if stop? # leave if we're exiting end @@ -200,18 +220,18 @@ def run(job) job_say job, 'RUNNING' - runtime = Benchmark.realtime do + runtime = Benchmark.realtime do Timeout.timeout(max_run_time(job).to_i, WorkerTimeout) { job.invoke_job } job.destroy end job_say job, format('COMPLETED after %.4f', runtime) - return true # did work + return true # did work rescue DeserializationError => error - job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" + job.error = error failed(job) - rescue => error + rescue Exception => error # rubocop:disable RescueException self.class.lifecycle.run_callbacks(:error, self, job) { handle_failed_job(job, error) } - return false # work failed + return false # work failed end # Reschedule the job in the future (when a job fails). @@ -236,7 +256,7 @@ say "Error when running failure callback: #{error}", 'error' say error.backtrace.join("\n"), 'error' ensure - self.class.destroy_failed_jobs ? job.destroy : job.fail! + job.destroy_failed_jobs? ? job.destroy : job.fail! end end end @@ -268,7 +288,7 @@ protected def handle_failed_job(job, error) - job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" + job.error = error job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", 'error' reschedule(job) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2014-12-22 14:22:31.000000000 +0100 +++ new/metadata 2015-09-24 23:40:11.000000000 +0200 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: delayed_job version: !ruby/object:Gem::Version - version: 4.0.6 + version: 4.1.1 platform: ruby authors: - Brandon Keepers @@ -15,7 +15,7 @@ autorequire: bindir: bin cert_chain: [] -date: 2014-12-22 00:00:00.000000000 Z +date: 2015-09-24 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activesupport @@ -84,6 +84,7 @@ - spec/autoloaded/instance_clazz.rb - spec/autoloaded/instance_struct.rb - spec/autoloaded/struct.rb +- spec/daemons.rb - spec/delayed/backend/test.rb - spec/delayed/command_spec.rb - spec/delayed/serialization/test.rb @@ -117,7 +118,7 @@ version: '0' requirements: [] rubyforge_project: -rubygems_version: 2.4.4 +rubygems_version: 2.4.8 signing_key: specification_version: 4 summary: Database-backed asynchronous priority queue system -- Extracted from Shopify @@ -126,6 +127,7 @@ - spec/autoloaded/instance_clazz.rb - spec/autoloaded/instance_struct.rb - spec/autoloaded/struct.rb +- spec/daemons.rb - spec/delayed/backend/test.rb - spec/delayed/command_spec.rb - spec/delayed/serialization/test.rb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/autoloaded/instance_struct.rb new/spec/autoloaded/instance_struct.rb --- old/spec/autoloaded/instance_struct.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/autoloaded/instance_struct.rb 2015-09-24 23:40:11.000000000 +0200 @@ -1,5 +1,6 @@ module Autoloaded - class InstanceStruct < ::Struct.new(nil) + InstanceStruct = ::Struct.new(nil) + class InstanceStruct def perform end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/autoloaded/struct.rb new/spec/autoloaded/struct.rb --- old/spec/autoloaded/struct.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/autoloaded/struct.rb 2015-09-24 23:40:11.000000000 +0200 @@ -1,6 +1,7 @@ # Make sure this file does not get required manually module Autoloaded - class Struct < ::Struct.new(nil) + Struct = ::Struct.new(nil) + class Struct def perform end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/daemons.rb new/spec/daemons.rb --- old/spec/daemons.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/daemons.rb 2015-09-24 23:40:11.000000000 +0200 @@ -0,0 +1,2 @@ +# Fake "daemons" file on the spec load path to allow spec/delayed/command_spec.rb +# to test the Delayed::Command class without actually adding daemons as a dependency. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/delayed/command_spec.rb new/spec/delayed/command_spec.rb --- old/spec/delayed/command_spec.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/delayed/command_spec.rb 2015-09-24 23:40:11.000000000 +0200 @@ -2,6 +2,129 @@ require 'delayed/command' describe Delayed::Command do + let(:options) { [] } + let(:logger) { double('Logger') } + + subject { Delayed::Command.new options } + + before do + allow(Delayed::Worker).to receive(:after_fork) + allow(Dir).to receive(:chdir) + allow(Logger).to receive(:new).and_return(logger) + allow_any_instance_of(Delayed::Worker).to receive(:start) + allow(Delayed::Worker).to receive(:logger=) + allow(Delayed::Worker).to receive(:logger).and_return(nil, logger) + end + + shared_examples_for 'uses --log-dir option' do + context 'when --log-dir is specified' do + let(:options) { ['--log-dir=/custom/log/dir'] } + + it 'creates the delayed_job.log in the specified directory' do + expect(Logger).to receive(:new).with('/custom/log/dir/delayed_job.log') + subject.run + end + end + end + + describe 'run' do + it 'sets the Delayed::Worker logger' do + expect(Delayed::Worker).to receive(:logger=).with(logger) + subject.run + end + + context 'when Rails root is defined' do + let(:rails_root) { Pathname.new '/rails/root' } + let(:rails) { double('Rails', :root => rails_root) } + + before do + stub_const('Rails', rails) + end + + it 'runs the Delayed::Worker process in Rails.root' do + expect(Dir).to receive(:chdir).with(rails_root) + subject.run + end + + context 'when --log-dir is not specified' do + it 'creates the delayed_job.log in Rails.root/log' do + expect(Logger).to receive(:new).with('/rails/root/log/delayed_job.log') + subject.run + end + end + + include_examples 'uses --log-dir option' + end + + context 'when Rails root is not defined' do + let(:rails_without_root) { double('Rails') } + + before do + stub_const('Rails', rails_without_root) + end + + it 'runs the Delayed::Worker process in $PWD' do + expect(Dir).to receive(:chdir).with(Delayed::Command::DIR_PWD) + subject.run + end + + context 'when --log-dir is not specified' do + it 'creates the delayed_job.log in $PWD/log' do + expect(Logger).to receive(:new).with("#{Delayed::Command::DIR_PWD}/log/delayed_job.log") + subject.run + end + end + + include_examples 'uses --log-dir option' + end + + context 'when an error is raised' do + let(:test_error) { Class.new(StandardError) } + + before do + allow(Delayed::Worker).to receive(:new).and_raise(test_error.new('An error')) + allow(subject).to receive(:exit_with_error_status) + allow(STDERR).to receive(:puts) + end + + it 'prints the error message to STDERR' do + expect(STDERR).to receive(:puts).with('An error') + subject.run + end + + it 'exits with an error status' do + expect(subject).to receive(:exit_with_error_status) + subject.run + end + + context 'when Rails logger is not defined' do + let(:rails) { double('Rails') } + + before do + stub_const('Rails', rails) + end + + it 'does not attempt to use the Rails logger' do + subject.run + end + end + + context 'when Rails logger is defined' do + let(:rails_logger) { double('Rails logger') } + let(:rails) { double('Rails', :logger => rails_logger) } + + before do + stub_const('Rails', rails) + end + + it 'logs the error to the Rails logger' do + expect(rails_logger).to receive(:fatal).with(test_error) + subject.run + end + end + end + end + describe 'parsing --pool argument' do it 'should parse --pool correctly' do command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2']) @@ -40,13 +163,13 @@ expect(Dir).to receive(:mkdir).with('./tmp/pids').once [ - ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues => []}], - ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}], - ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}], - ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}], - ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}], - ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}], - ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}] + ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => []}], + ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}], + ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}], + ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}], + ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}], + ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => %w[mailers misc]}], + ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => %w[mailers misc]}] ].each do |args| expect(command).to receive(:run_process).with(*args).once end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/message_sending_spec.rb new/spec/message_sending_spec.rb --- old/spec/message_sending_spec.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/message_sending_spec.rb 2015-09-24 23:40:11.000000000 +0200 @@ -62,6 +62,7 @@ class FairyTail attr_accessor :happy_ending def self.princesses; end + def tell @happy_ending = true end @@ -118,5 +119,25 @@ end.not_to change(fairy_tail, :happy_ending) end.to change { Delayed::Job.count }.by(1) end + + it 'does delay when delay_jobs is a proc returning true' do + Delayed::Worker.delay_jobs = ->(_job) { true } + fairy_tail = FairyTail.new + expect do + expect do + fairy_tail.delay.tell + end.not_to change(fairy_tail, :happy_ending) + end.to change { Delayed::Job.count }.by(1) + end + + it 'does not delay the job when delay_jobs is a proc returning false' do + Delayed::Worker.delay_jobs = ->(_job) { false } + fairy_tail = FairyTail.new + expect do + expect do + fairy_tail.delay.tell + end.to change(fairy_tail, :happy_ending).from(nil).to(true) + end.not_to change { Delayed::Job.count } + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/performable_method_spec.rb new/spec/performable_method_spec.rb --- old/spec/performable_method_spec.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/performable_method_spec.rb 2015-09-24 23:40:11.000000000 +0200 @@ -68,7 +68,7 @@ story = Story.create expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) expect(story).to receive(:tell).and_raise(RuntimeError) - expect { story.delay.tell.invoke_job }.to raise_error + expect { story.delay.tell.invoke_job }.to raise_error(RuntimeError) end it 'delegates failure hook to object' do @@ -98,7 +98,7 @@ story = Story.create expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) expect(story).to receive(:tell).and_raise(RuntimeError) - expect { story.delay.tell }.to raise_error + expect { story.delay.tell }.to raise_error(RuntimeError) end it 'delegates failure hook to object' do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/sample_jobs.rb new/spec/sample_jobs.rb --- old/spec/sample_jobs.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/sample_jobs.rb 2015-09-24 23:40:11.000000000 +0200 @@ -1,4 +1,5 @@ -class NamedJob < Struct.new(:perform) +NamedJob = Struct.new(:perform) +class NamedJob def display_name 'named_job' end @@ -22,11 +23,12 @@ cattr_accessor :runs @runs = 0 def perform - raise 'did not work' + raise Exception, 'did not work' end end -class CustomRescheduleJob < Struct.new(:offset) +CustomRescheduleJob = Struct.new(:offset) +class CustomRescheduleJob cattr_accessor :runs @runs = 0 def perform diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/worker_spec.rb new/spec/worker_spec.rb --- old/spec/worker_spec.rb 2014-12-22 14:22:31.000000000 +0100 +++ new/spec/worker_spec.rb 2015-09-24 23:40:11.000000000 +0200 @@ -154,4 +154,22 @@ @worker.say(@text, Delayed::Worker.default_log_level) end end + + describe 'plugin registration' do + it 'does not double-register plugins on worker instantiation' do + performances = 0 + plugin = Class.new(Delayed::Plugin) do + callbacks do |lifecycle| + lifecycle.before(:enqueue) { performances += 1 } + end + end + Delayed::Worker.plugins << plugin + + Delayed::Worker.new + Delayed::Worker.new + Delayed::Worker.lifecycle.run_callbacks(:enqueue, nil) {} + + expect(performances).to eq(1) + end + end end
participants (1)
-
root@hilbert.suse.de