resque-multi-job-forks 0.5.0 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2bd1e8435173c27eefcab09cdc99cba876698404
4
- data.tar.gz: 96f8db0967c05a3104fc48e78fa49f5a0c5c4e79
2
+ SHA256:
3
+ metadata.gz: 142ac780146f82b8ab449daa58bfcb1319f113207045b9c94d6d81595e9dd399
4
+ data.tar.gz: fcaf1e33dc1aefae61e4a44f1cbab884635d266f9dbaed0fdcc8c9e458a0b720
5
5
  SHA512:
6
- metadata.gz: 91ffc7a44dcb0353754bcc96410ed3d4c4701d3176427dfa8b626d5b6a3af3eb1ce3a1d5e8a6f5d39809e661de98121ba39d6d08fd17b5d550adb0731312fb44
7
- data.tar.gz: 8bca3021d289308c02d199e30a7dea8551ca8103783effe134ab71dc641894ebd4ae727320a5551e3611357f786db2cb84a65872e74bf0130937d67735da4cae
6
+ metadata.gz: ef14ecd365691be385e6378b42beb60bd712687bcd1907e8899b5bb7e3b13b0c1c1a8821570631d7accb047cafeff444210fff7ea72802c036cc3b1d79c1976d
7
+ data.tar.gz: bf62253ac1e9cc96e6efc3c9d83da3918b479dc4ea904dfc3e0e47a860641cd507376a878c85c6982c9115942e80423a2351d25bebc10800ce904272d06f7520
@@ -10,6 +10,8 @@ module Resque
10
10
  attr_accessor :memory_threshold
11
11
  attr_reader :jobs_processed
12
12
 
13
+ WorkerTerminated = Class.new(StandardError)
14
+
13
15
  def self.multi_jobs_per_fork?
14
16
  ENV["DISABLE_MULTI_JOBS_PER_FORK"].nil?
15
17
  end
@@ -22,6 +24,20 @@ module Resque
22
24
  else
23
25
  if term_child
24
26
  unregister_signal_handlers
27
+ trap('TERM') do
28
+ trap('TERM') do
29
+ # Ignore subsequent term signals
30
+ end
31
+
32
+ if @performing_job
33
+ # If a job is in progress, stop it immediately.
34
+ raise TermException.new("SIGTERM")
35
+ else
36
+ # If we're not currently running a job, shut down cleanly.
37
+ # This allows us to push unworked jobs back on the queue.
38
+ shutdown
39
+ end
40
+ end
25
41
  trap('QUIT') { shutdown }
26
42
  end
27
43
  raise NotImplementedError, "Pretending to not have forked"
@@ -39,9 +55,20 @@ module Resque
39
55
 
40
56
  def perform_with_multi_job_forks(job = nil)
41
57
  @fork_per_job = true unless fork_hijacked? # reconnect and after_fork
58
+ if shutdown?
59
+ # We got a request to shut down _after_ grabbing a job but _before_ starting work
60
+ # on it. Immediately report the job as failed and return.
61
+ if job
62
+ report_failed_job(job, WorkerTerminated.new("shutdown before job start"))
63
+ end
64
+ return
65
+ end
66
+ @performing_job = true
42
67
  perform_without_multi_job_forks(job)
43
68
  hijack_fork unless fork_hijacked?
44
69
  @jobs_processed += 1
70
+ ensure
71
+ @performing_job = false
45
72
  end
46
73
  alias_method :perform_without_multi_job_forks, :perform
47
74
  alias_method :perform, :perform_with_multi_job_forks
@@ -54,14 +81,14 @@ module Resque
54
81
  alias_method :shutdown?, :shutdown_with_multi_job_forks?
55
82
 
56
83
  def shutdown_with_multi_job_forks
57
- shutdown_child if is_parent_process?
84
+ shutdown_child
58
85
  shutdown_without_multi_job_forks
59
86
  end
60
87
  alias_method :shutdown_without_multi_job_forks, :shutdown
61
88
  alias_method :shutdown, :shutdown_with_multi_job_forks
62
89
 
63
90
  def pause_processing_with_multi_job_forks
64
- shutdown_child if is_parent_process?
91
+ shutdown_child
65
92
  pause_processing_without_multi_job_forks
66
93
  end
67
94
  alias_method :pause_processing_without_multi_job_forks, :pause_processing
@@ -89,8 +116,9 @@ module Resque
89
116
  # multiple jobs per fork. The QUIT signal normally does a graceful shutdown,
90
117
  # and is re-registered in children (term_child normally unregisters it).
91
118
  def shutdown_child
119
+ return unless @child
92
120
  begin
93
- log! "multi_jobs_per_fork: Sending QUIT signal to #{@child}"
121
+ log_with_severity :debug, "multi_jobs_per_fork: Sending QUIT signal to #{@child}"
94
122
  Process.kill('QUIT', @child)
95
123
  rescue Errno::ESRCH
96
124
  nil
@@ -111,7 +139,7 @@ module Resque
111
139
  end
112
140
 
113
141
  def hijack_fork
114
- log 'hijack fork.'
142
+ log_with_severity :debug, 'hijack fork.'
115
143
  @suppressed_fork_hooks = [Resque.after_fork, Resque.before_fork]
116
144
  Resque.after_fork = Resque.before_fork = nil
117
145
  @release_fork_limit = fork_job_limit
@@ -120,11 +148,11 @@ module Resque
120
148
  end
121
149
 
122
150
  def release_fork
123
- log "jobs processed by child: #{jobs_processed}; rss: #{rss}"
151
+ log_with_severity :info, "jobs processed by child: #{jobs_processed}; rss: #{rss}"
124
152
  run_hook :before_child_exit, self
125
153
  Resque.after_fork, Resque.before_fork = *@suppressed_fork_hooks
126
154
  @release_fork_limit = @jobs_processed = nil
127
- log 'hijack over, counter terrorists win.'
155
+ log_with_severity :debug, 'hijack over, counter terrorists win.'
128
156
  @shutdown = true
129
157
  end
130
158
 
@@ -169,16 +197,11 @@ module Resque
169
197
  # Call with a block to set the hook.
170
198
  # Call with no arguments to return the hook.
171
199
  def self.before_child_exit(&block)
172
- if block
173
- @before_child_exit ||= []
174
- @before_child_exit << block
175
- end
176
- @before_child_exit
200
+ block ? register_hook(:before_child_exit, block) : hooks(:before_child_exit)
177
201
  end
178
202
 
179
203
  # Set the before_child_exit proc.
180
204
  def self.before_child_exit=(before_child_exit)
181
- @before_child_exit = before_child_exit.respond_to?(:each) ? before_child_exit : [before_child_exit].compact
205
+ register_hook(:before_child_exit, block)
182
206
  end
183
-
184
207
  end
data/test/helper.rb CHANGED
@@ -19,6 +19,7 @@ Resque.redis = $redis
19
19
  # set `VERBOSE=true` when running the tests to view resques log output.
20
20
  module Resque
21
21
  class Worker
22
+ attr_accessor :start_lag
22
23
 
23
24
  def log_with_severity(severity, msg)
24
25
  if ENV['VERBOSE']
@@ -27,14 +28,18 @@ module Resque
27
28
  end
28
29
  end
29
30
 
30
- def log(message)
31
- log_with_severity :info, message
31
+ def report_failed_job(job, exception)
32
+ $SEQ_WRITER.print "failed_job_#{exception.class.name.downcase.gsub('::', '_')}\n"
32
33
  end
33
34
 
34
- def log!(message)
35
- log_with_severity :debug, message
35
+ def fork_hijacked?
36
+ if @release_fork_limit
37
+ if start_lag
38
+ sleep start_lag
39
+ end
40
+ end
41
+ @release_fork_limit
36
42
  end
37
-
38
43
  end
39
44
  end
40
45
 
@@ -8,7 +8,7 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
8
8
  end
9
9
 
10
10
  def test_timeout_limit_sequence_of_events
11
- @worker.log! "in test_timeout_limit_sequence_of_events"
11
+ @worker.log_with_severity :debug, "in test_timeout_limit_sequence_of_events"
12
12
  # only allow enough time for 3 jobs to process.
13
13
  @worker.seconds_per_fork = 3
14
14
 
@@ -29,7 +29,7 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
29
29
  end
30
30
 
31
31
  def test_graceful_shutdown_during_first_job
32
- @worker.log! "in test_graceful_shutdown_during_first_job"
32
+ @worker.log_with_severity :debug, "in test_graceful_shutdown_during_first_job"
33
33
  # enough time for all jobs to process.
34
34
  @worker.seconds_per_fork = 60
35
35
 
@@ -51,7 +51,7 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
51
51
  end
52
52
 
53
53
  def test_immediate_shutdown_during_first_job
54
- @worker.log! "in test_immediate_shutdown_during_first_job"
54
+ @worker.log_with_severity :debug, "in test_immediate_shutdown_during_first_job"
55
55
  # enough time for all jobs to process.
56
56
  @worker.seconds_per_fork = 60
57
57
  @worker.term_child = false
@@ -73,7 +73,7 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
73
73
  end
74
74
 
75
75
  def test_sigterm_shutdown_during_first_job
76
- @worker.log! "in test_sigterm_shutdown_during_first_job"
76
+ @worker.log_with_severity :debug, "in test_sigterm_shutdown_during_first_job"
77
77
  # enough time for all jobs to process.
78
78
  @worker.seconds_per_fork = 60
79
79
  @worker.term_child = true
@@ -91,14 +91,38 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
91
91
  sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
92
92
 
93
93
  # test the sequence is correct.
94
- assert_equal([:before_fork, :after_fork,
94
+ assert_equal([:before_fork, :after_fork, :failed_job_resque_termexception,
95
95
  :before_child_exit_1], sequence, 'correct sequence')
96
96
  t.join
97
97
  end
98
98
 
99
+ def test_shutdown_between_jobs
100
+ @worker.log_with_severity :debug, "in test_sigterm_shutdown_during_first_job"
101
+ # enough time for all jobs to process.
102
+ @worker.seconds_per_fork = 60
103
+ @worker.term_child = true
104
+ @worker.graceful_term = true
105
+ @worker.term_timeout = 0.5
106
+ @worker.start_lag = 1
107
+
108
+ Resque.enqueue(QuickSequenceJob, 1)
109
+ Resque.enqueue(SequenceJob, 2)
110
+ t = Thread.new do
111
+ sleep 2
112
+ Process.kill("TERM", @worker.pid)
113
+ end
114
+ @worker.work(0)
115
+ $SEQ_WRITER.close
116
+
117
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
118
+ assert_equal([:before_fork, :after_fork, :work_1, :before_child_exit_1, :failed_job_resque_worker_workerterminated], sequence, 'correct sequence')
119
+
120
+ t.join
121
+ end
122
+
99
123
  # test we can also limit fork job process by a job limit.
100
124
  def test_job_limit_sequence_of_events
101
- @worker.log! "in test_job_limit_sequence_of_events"
125
+ @worker.log_with_severity :debug, "in test_job_limit_sequence_of_events"
102
126
  # only allow 20 jobs per fork
103
127
  ENV['JOBS_PER_FORK'] = '20'
104
128
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-multi-job-forks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-02-10 00:00:00.000000000 Z
13
+ date: 2021-11-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: resque
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 1.27.0
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
- version: '2.1'
24
+ version: '2.2'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  version: 1.27.0
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
- version: '2.1'
34
+ version: '2.2'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: json
37
37
  requirement: !ruby/object:Gem::Requirement
@@ -121,8 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.6.14.3
124
+ rubygems_version: 3.1.6
126
125
  signing_key:
127
126
  specification_version: 4
128
127
  summary: Have your resque workers process more that one job