resque-multi-job-forks 0.4.3 → 0.5.2

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: a6c7a43817f8c3f8d4b3ed8a0c7efc997c079de4
4
- data.tar.gz: 374c601a0654d238db83989970a3317d2399f65b
2
+ SHA256:
3
+ metadata.gz: 2b710d8dc861eb050596364959ae65da3f099c29492370d3a817239428cf5a38
4
+ data.tar.gz: 45b5e45ddf6aac1a092b5d9047483ad4b9d4064920d5f875f8251686310e2d46
5
5
  SHA512:
6
- metadata.gz: bc729ac05237225b44fd4a2745e3a97aca598cb51561624c34d29183a09b321789e8e095cf656b9234a4875c38b03aeda74c5c3bc862973f310fa8055d31c80d
7
- data.tar.gz: eb6a4d42cad8379ee8c18f9ece2231ee16298ba359a6f036e5b5d426904dc5d1b922c0bb5037abd4e7d661d48477527d53eab22e39bb5b7712f47935a4eaafc0
6
+ metadata.gz: 2d98283d9a0b31141e943d452cf786781c5b6e3e42dbacc2a7175a41e8f4380bc85914570215111c1a088ed16e6f9d08855cba454ca943e75eac09694f0d8da2
7
+ data.tar.gz: 6199e71957234fe7c374778ddb28e56207e9c438750eb2051852f0bd150cb89304c587351e007d76eb680ed0a09c42fcb6e52ef0f7f3c99eaae84e9e2a7d06a4
@@ -15,7 +15,30 @@ module Resque
15
15
  end
16
16
 
17
17
  if multi_jobs_per_fork? && !method_defined?(:shutdown_without_multi_job_forks)
18
+
19
+ def fork(&block)
20
+ if child = Kernel.fork
21
+ return child
22
+ else
23
+ if term_child
24
+ unregister_signal_handlers
25
+ trap('QUIT') { shutdown }
26
+ end
27
+ raise NotImplementedError, "Pretending to not have forked"
28
+ # perform_with_fork will run the job and continue working...
29
+ end
30
+ end
31
+
32
+ def work_with_multi_job_forks(*args)
33
+ pid # forces @pid to be set in the parent
34
+ work_without_multi_job_forks(*args)
35
+ release_and_exit! unless is_parent_process?
36
+ end
37
+ alias_method :work_without_multi_job_forks, :work
38
+ alias_method :work, :work_with_multi_job_forks
39
+
18
40
  def perform_with_multi_job_forks(job = nil)
41
+ @fork_per_job = true unless fork_hijacked? # reconnect and after_fork
19
42
  perform_without_multi_job_forks(job)
20
43
  hijack_fork unless fork_hijacked?
21
44
  @jobs_processed += 1
@@ -31,15 +54,15 @@ module Resque
31
54
  alias_method :shutdown?, :shutdown_with_multi_job_forks?
32
55
 
33
56
  def shutdown_with_multi_job_forks
57
+ shutdown_child
34
58
  shutdown_without_multi_job_forks
35
- shutdown_child if is_parent_process?
36
59
  end
37
60
  alias_method :shutdown_without_multi_job_forks, :shutdown
38
61
  alias_method :shutdown, :shutdown_with_multi_job_forks
39
62
 
40
63
  def pause_processing_with_multi_job_forks
64
+ shutdown_child
41
65
  pause_processing_without_multi_job_forks
42
- shutdown_child if is_parent_process?
43
66
  end
44
67
  alias_method :pause_processing_without_multi_job_forks, :pause_processing
45
68
  alias_method :pause_processing, :pause_processing_with_multi_job_forks
@@ -49,7 +72,7 @@ module Resque
49
72
  working_on_without_worker_registration(job)
50
73
  end
51
74
  alias_method :working_on_without_worker_registration, :working_on
52
- alias_method :working_on, :working_on_with_worker_registration
75
+ alias_method :working_on, :working_on_with_worker_registration
53
76
 
54
77
  # Reconnect only once
55
78
  def reconnect_with_multi_job_forks
@@ -62,18 +85,26 @@ module Resque
62
85
  alias_method :reconnect, :reconnect_with_multi_job_forks
63
86
  end
64
87
 
65
- # Need to tell the child to shutdown since it might be looping performing multiple jobs per fork
66
- # The TSTP signal is registered only in forked processes and calls this function
88
+ # Need to tell the child to shutdown since it might be looping performing
89
+ # multiple jobs per fork. The QUIT signal normally does a graceful shutdown,
90
+ # and is re-registered in children (term_child normally unregisters it).
67
91
  def shutdown_child
92
+ return unless @child
68
93
  begin
69
- Process.kill('TSTP', @child)
94
+ log_with_severity :debug, "multi_jobs_per_fork: Sending QUIT signal to #{@child}"
95
+ Process.kill('QUIT', @child)
70
96
  rescue Errno::ESRCH
71
97
  nil
72
98
  end
73
99
  end
74
100
 
75
101
  def is_parent_process?
76
- @child
102
+ @child || @pid == Process.pid
103
+ end
104
+
105
+ def release_and_exit!
106
+ release_fork if fork_hijacked?
107
+ run_at_exit_hooks ? exit : exit!(true)
77
108
  end
78
109
 
79
110
  def fork_hijacked?
@@ -81,26 +112,25 @@ module Resque
81
112
  end
82
113
 
83
114
  def hijack_fork
84
- log 'hijack fork.'
115
+ log_with_severity :debug, 'hijack fork.'
85
116
  @suppressed_fork_hooks = [Resque.after_fork, Resque.before_fork]
86
117
  Resque.after_fork = Resque.before_fork = nil
87
118
  @release_fork_limit = fork_job_limit
88
119
  @jobs_processed = 0
89
- @cant_fork = true
90
- trap('TSTP') { shutdown }
120
+ @fork_per_job = false
91
121
  end
92
122
 
93
123
  def release_fork
94
- log "jobs processed by child: #{jobs_processed}; rss: #{rss}"
124
+ log_with_severity :info, "jobs processed by child: #{jobs_processed}; rss: #{rss}"
95
125
  run_hook :before_child_exit, self
96
126
  Resque.after_fork, Resque.before_fork = *@suppressed_fork_hooks
97
- @release_fork_limit = @jobs_processed = @cant_fork = nil
98
- log 'hijack over, counter terrorists win.'
99
- @shutdown = true unless $TESTING
127
+ @release_fork_limit = @jobs_processed = nil
128
+ log_with_severity :debug, 'hijack over, counter terrorists win.'
129
+ @shutdown = true
100
130
  end
101
131
 
102
132
  def fork_job_limit
103
- jobs_per_fork.nil? ? Time.now.to_i + seconds_per_fork : jobs_per_fork
133
+ jobs_per_fork.nil? ? Time.now.to_f + seconds_per_fork : jobs_per_fork
104
134
  end
105
135
 
106
136
  def fork_job_limit_reached?
@@ -108,7 +138,7 @@ module Resque
108
138
  end
109
139
 
110
140
  def fork_job_limit_remaining
111
- jobs_per_fork.nil? ? @release_fork_limit - Time.now.to_i : jobs_per_fork - @jobs_processed
141
+ jobs_per_fork.nil? ? @release_fork_limit - Time.now.to_f : jobs_per_fork - @jobs_processed
112
142
  end
113
143
 
114
144
  def seconds_per_fork
@@ -140,16 +170,11 @@ module Resque
140
170
  # Call with a block to set the hook.
141
171
  # Call with no arguments to return the hook.
142
172
  def self.before_child_exit(&block)
143
- if block
144
- @before_child_exit ||= []
145
- @before_child_exit << block
146
- end
147
- @before_child_exit
173
+ block ? register_hook(:before_child_exit, block) : hooks(:before_child_exit)
148
174
  end
149
175
 
150
176
  # Set the before_child_exit proc.
151
177
  def self.before_child_exit=(before_child_exit)
152
- @before_child_exit = before_child_exit.respond_to?(:each) ? before_child_exit : [before_child_exit].compact
178
+ register_hook(:before_child_exit, block)
153
179
  end
154
-
155
180
  end
data/test/helper.rb CHANGED
@@ -12,72 +12,50 @@ require 'resque-multi-job-forks'
12
12
  require 'timeout'
13
13
 
14
14
  # setup redis & resque.
15
- redis = Redis.new(:db => 1)
16
- Resque.redis = redis
15
+ $redis = Redis.new(:db => 1)
16
+ Resque.redis = $redis
17
17
 
18
18
  # adds simple STDOUT logging to test workers.
19
19
  # set `VERBOSE=true` when running the tests to view resques log output.
20
20
  module Resque
21
21
  class Worker
22
- def log(msg)
23
- puts "*** #{msg}" unless ENV['VERBOSE'].nil?
24
- end
25
- alias_method :log!, :log
26
22
 
27
- # Processes a given job in the child.
28
- def perform_without_multi_job_forks(job)
29
- begin
30
- # 'will_fork?' returns false in test mode, but since we need to test
31
- # if the :after_fork hook runs, we ignore 'will_fork?' here.
32
- run_hook :after_fork, job # if will_fork?
33
- job.perform
34
- rescue Object => e
35
- log "#{job.inspect} failed: #{e.inspect}"
36
- begin
37
- job.fail(e)
38
- rescue Object => e
39
- log "Received exception when reporting failure: #{e.inspect}"
40
- end
41
- failed!
42
- else
43
- log "done: #{job.inspect}"
44
- ensure
45
- yield job if block_given?
23
+ def log_with_severity(severity, msg)
24
+ if ENV['VERBOSE']
25
+ s = severity.to_s[0].upcase
26
+ $stderr.print "*** [#{Time.now}] [#{Process.pid}] #{self} #{s}: #{msg}\n"
46
27
  end
47
28
  end
29
+
48
30
  end
49
31
  end
50
32
 
51
- # stores a record of the job processing sequence.
52
- # you may wish to reset this in the test `setup` method.
53
- $SEQUENCE = []
54
-
55
33
  # test job, tracks sequence.
56
34
  class SequenceJob
57
35
  @queue = :jobs
58
36
  def self.perform(i)
59
- $SEQUENCE << "work_#{i}".to_sym
60
37
  sleep(2)
38
+ $SEQ_WRITER.print "work_#{i}\n"
61
39
  end
62
40
  end
63
41
 
64
42
  class QuickSequenceJob
65
43
  @queue = :jobs
66
44
  def self.perform(i)
67
- $SEQUENCE << "work_#{i}".to_sym
45
+ $SEQ_WRITER.print "work_#{i}\n"
68
46
  end
69
47
  end
70
48
 
71
49
 
72
50
  # test hooks, tracks sequence.
73
51
  Resque.after_fork do
74
- $SEQUENCE << :after_fork
52
+ $SEQ_WRITER.print "after_fork\n"
75
53
  end
76
54
 
77
55
  Resque.before_fork do
78
- $SEQUENCE << :before_fork
56
+ $SEQ_WRITER.print "before_fork\n"
79
57
  end
80
58
 
81
59
  Resque.before_child_exit do |worker|
82
- $SEQUENCE << "before_child_exit_#{worker.jobs_processed}".to_sym
60
+ $SEQ_WRITER.print "before_child_exit_#{worker.jobs_processed}\n"
83
61
  end
@@ -2,12 +2,13 @@ require File.join(File.expand_path(File.dirname(__FILE__)), '/helper')
2
2
 
3
3
  class TestResqueMultiJobForks < Test::Unit::TestCase
4
4
  def setup
5
- $SEQUENCE = []
6
- Resque.redis.flushdb
5
+ $SEQ_READER, $SEQ_WRITER = IO.pipe
6
+ $redis.flushdb
7
7
  @worker = Resque::Worker.new(:jobs)
8
8
  end
9
9
 
10
10
  def test_timeout_limit_sequence_of_events
11
+ @worker.log_with_severity :debug, "in test_timeout_limit_sequence_of_events"
11
12
  # only allow enough time for 3 jobs to process.
12
13
  @worker.seconds_per_fork = 3
13
14
 
@@ -16,43 +17,119 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
16
17
  Resque.enqueue(SequenceJob, 3)
17
18
  Resque.enqueue(SequenceJob, 4)
18
19
 
19
- # make sure we don't take longer then 15 seconds.
20
- begin
21
- Timeout::timeout(15) { @worker.work(1) }
22
- rescue Timeout::Error
23
- end
20
+ # INTERVAL=0 will exit when no more jobs left
21
+ @worker.work(0)
22
+ $SEQ_WRITER.close
23
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
24
24
 
25
25
  # test the sequence is correct.
26
26
  assert_equal([:before_fork, :after_fork, :work_1, :work_2, :work_3,
27
27
  :before_child_exit_3, :before_fork, :after_fork, :work_4,
28
- :before_child_exit_1], $SEQUENCE, 'correct sequence')
28
+ :before_child_exit_1], sequence, 'correct sequence')
29
+ end
30
+
31
+ def test_graceful_shutdown_during_first_job
32
+ @worker.log_with_severity :debug, "in test_graceful_shutdown_during_first_job"
33
+ # enough time for all jobs to process.
34
+ @worker.seconds_per_fork = 60
35
+
36
+ Resque.enqueue(SequenceJob, 1)
37
+ Resque.enqueue(SequenceJob, 2)
38
+ t = Thread.new do
39
+ sleep 1 # before first job can complete
40
+ @worker.shutdown
41
+ end
42
+ # INTERVAL=0 will exit when no more jobs left
43
+ @worker.work(0)
44
+ $SEQ_WRITER.close
45
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
46
+
47
+ # test the sequence is correct.
48
+ assert_equal([:before_fork, :after_fork, :work_1,
49
+ :before_child_exit_1], sequence, 'correct sequence')
50
+ t.join
51
+ end
52
+
53
+ def test_immediate_shutdown_during_first_job
54
+ @worker.log_with_severity :debug, "in test_immediate_shutdown_during_first_job"
55
+ # enough time for all jobs to process.
56
+ @worker.seconds_per_fork = 60
57
+ @worker.term_child = false
58
+
59
+ Resque.enqueue(SequenceJob, 1)
60
+ Resque.enqueue(SequenceJob, 2)
61
+ t = Thread.new do
62
+ sleep 0.5 # before first job can complete
63
+ Process.kill("INT", @worker.pid) # triggers shutdown! in main thread
64
+ end
65
+ # INTERVAL=0 will exit when no more jobs left
66
+ @worker.work(0)
67
+ $SEQ_WRITER.close
68
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
69
+
70
+ # test the sequence is correct.
71
+ assert_equal([:before_fork, :after_fork], sequence, 'correct sequence')
72
+ t.join
73
+ end
74
+
75
+ def test_sigterm_shutdown_during_first_job
76
+ @worker.log_with_severity :debug, "in test_sigterm_shutdown_during_first_job"
77
+ # enough time for all jobs to process.
78
+ @worker.seconds_per_fork = 60
79
+ @worker.term_child = true
80
+ @worker.term_timeout = 0.5
81
+
82
+ Resque.enqueue(SequenceJob, 1)
83
+ Resque.enqueue(SequenceJob, 2)
84
+ t = Thread.new do
85
+ sleep 1.0 # before first job can complete
86
+ Process.kill("INT", @worker.pid) # triggers shutdown! in main thread
87
+ end
88
+ # INTERVAL=0 will exit when no more jobs left
89
+ @worker.work(0)
90
+ $SEQ_WRITER.close
91
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
92
+
93
+ # test the sequence is correct.
94
+ assert_equal([:before_fork, :after_fork,
95
+ :before_child_exit_1], sequence, 'correct sequence')
96
+ t.join
29
97
  end
30
98
 
31
99
  # test we can also limit fork job process by a job limit.
32
100
  def test_job_limit_sequence_of_events
33
- # only allow enough time for 3 jobs to process.
101
+ @worker.log_with_severity :debug, "in test_job_limit_sequence_of_events"
102
+ # only allow 20 jobs per fork
34
103
  ENV['JOBS_PER_FORK'] = '20'
35
104
 
36
105
  # queue 40 jobs.
37
106
  (1..40).each { |i| Resque.enqueue(QuickSequenceJob, i) }
38
107
 
39
- begin
40
- Timeout::timeout(3) { @worker.work(1) }
41
- rescue Timeout::Error
42
- end
108
+ # INTERVAL=0 will exit when no more jobs left
109
+ @worker.work(0)
110
+ $SEQ_WRITER.close
111
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
43
112
 
44
- assert_equal :before_fork, $SEQUENCE[0], 'first before_fork call.'
45
- assert_equal :after_fork, $SEQUENCE[1], 'first after_fork call.'
46
- assert_equal :work_20, $SEQUENCE[21], '20th chunk of work.'
47
- assert_equal :before_child_exit_20, $SEQUENCE[22], 'first before_child_exit call.'
48
- assert_equal :before_fork, $SEQUENCE[23], 'final before_fork call.'
49
- assert_equal :after_fork, $SEQUENCE[24], 'final after_fork call.'
50
- assert_equal :work_40, $SEQUENCE[44], '40th chunk of work.'
51
- assert_equal :before_child_exit_20, $SEQUENCE[45], 'final before_child_exit call.'
113
+ assert_equal(%i[
114
+ before_fork after_fork
115
+ work_1 work_2 work_3 work_4 work_5
116
+ work_6 work_7 work_8 work_9 work_10
117
+ work_11 work_12 work_13 work_14 work_15
118
+ work_16 work_17 work_18 work_19 work_20
119
+ before_child_exit_20
120
+ before_fork after_fork
121
+ work_21 work_22 work_23 work_24 work_25
122
+ work_26 work_27 work_28 work_29 work_30
123
+ work_31 work_32 work_33 work_34 work_35
124
+ work_36 work_37 work_38 work_39 work_40
125
+ before_child_exit_20
126
+ ], sequence, 'correct sequence')
52
127
  end
53
128
 
54
129
  def teardown
55
130
  # make sure we don't clobber any other tests.
56
131
  ENV['JOBS_PER_FORK'] = nil
132
+ Resque::Worker.kill_all_heartbeat_threads
57
133
  end
134
+
58
135
  end
@@ -7,17 +7,20 @@ class TestRssReader < Test::Unit::TestCase
7
7
  @object.extend Resque::Plugins::MultiJobForks::RssReader
8
8
  end
9
9
 
10
+ # support before/after ruby 2.4 without deprecation notice
11
+ IntegerClass = 1.class
12
+
10
13
  def test_current_process_rss
11
14
  rss = @object.rss
12
15
  # not a very strict test, but have you ever seen a ruby process < 1Mb?
13
16
  # the "real" test is manual verification via top/ps/htop
14
- assert_equal Fixnum, rss.class
17
+ assert_equal IntegerClass, rss.class
15
18
  assert @object.rss > 1000
16
19
  end
17
20
 
18
21
  def test_rss_other_processes
19
22
  rss = @object.rss(1) # init is guaranteed to exist
20
- assert_equal Fixnum, rss.class
23
+ assert_equal IntegerClass, rss.class
21
24
  assert @object.rss > 1000
22
25
  end
23
26
 
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.4.3
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -10,22 +10,28 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-10-26 00:00:00.000000000 Z
13
+ date: 2021-03-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: resque
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 1.27.0
22
+ - - "<"
20
23
  - !ruby/object:Gem::Version
21
- version: '1.22'
24
+ version: '2.1'
22
25
  type: :runtime
23
26
  prerelease: false
24
27
  version_requirements: !ruby/object:Gem::Requirement
25
28
  requirements:
26
- - - "~>"
29
+ - - ">="
27
30
  - !ruby/object:Gem::Version
28
- version: '1.22'
31
+ version: 1.27.0
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.1'
29
35
  - !ruby/object:Gem::Dependency
30
36
  name: json
31
37
  requirement: !ruby/object:Gem::Requirement
@@ -41,7 +47,7 @@ dependencies:
41
47
  - !ruby/object:Gem::Version
42
48
  version: '0'
43
49
  - !ruby/object:Gem::Dependency
44
- name: rake
50
+ name: test-unit
45
51
  requirement: !ruby/object:Gem::Requirement
46
52
  requirements:
47
53
  - - ">="
@@ -68,6 +74,20 @@ dependencies:
68
74
  - - ">="
69
75
  - !ruby/object:Gem::Version
70
76
  version: '0'
77
+ - !ruby/object:Gem::Dependency
78
+ name: rake
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
71
91
  description: When your resque jobs are frequent and fast, the overhead of forking
72
92
  and running your after_fork might get too big.
73
93
  email:
@@ -83,7 +103,7 @@ files:
83
103
  - test/helper.rb
84
104
  - test/test_resque-multi-job-forks.rb
85
105
  - test/test_rss_reader.rb
86
- homepage: http://github.com/staugaard/resque-multi-job-forks
106
+ homepage: https://github.com/stulentsev/resque-multi-job-forks
87
107
  licenses: []
88
108
  metadata: {}
89
109
  post_install_message:
@@ -101,8 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
121
  - !ruby/object:Gem::Version
102
122
  version: '0'
103
123
  requirements: []
104
- rubyforge_project:
105
- rubygems_version: 2.4.5.1
124
+ rubygems_version: 3.1.4
106
125
  signing_key:
107
126
  specification_version: 4
108
127
  summary: Have your resque workers process more that one job
@@ -110,4 +129,3 @@ test_files:
110
129
  - test/helper.rb
111
130
  - test/test_resque-multi-job-forks.rb
112
131
  - test/test_rss_reader.rb
113
- has_rdoc: