resque-multi-job-forks 0.4.4 → 0.4.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cae179fcc38decf3f26b41ecf89c37550845d13c
4
- data.tar.gz: 6e04ade5c457d3470d67a083f19ebed05eb664b8
2
+ SHA256:
3
+ metadata.gz: e14fad0b41eaab14a736ac76d9e234314c97279b190d90ab935a74717bc18921
4
+ data.tar.gz: fc1a9a67f6a6825180044316144b70923f771d454e5c6338c86ccd22aecb3fb8
5
5
  SHA512:
6
- metadata.gz: 5483cface37a516972d8303c5b9a99b3f1e0d1f6176bbedc7ac54e29e3949402400df4df31500583e27e69023e230d7c75fdcd3c43ef036bc7fe71a28d7d6e42
7
- data.tar.gz: c96715f6b5a10e31ac584bad2a4e86b127b86e8fa3788b35fac6d9dfe8f68b3e80671333185d5d59b86a9796e445b80ef6ee9a93c1de9474d42718da85fc8bd7
6
+ metadata.gz: 2da4375dc2b26da0c670e402954c0b5496a6ce46c3100eabe4f9991d50cd987b0c8d4e5745fdb72f1632f35934f112eb42df6a141c7d838904ed12419f0e8a7d
7
+ data.tar.gz: cc8c72fa05245eae3aa8a19811b54279ee00f6257fb8093942c9fd7c8d62336fb4ce1e7242de96e997772baff15486838cd3418ca0436d21ec38dd6f4cb4a3fe
@@ -15,7 +15,16 @@ module Resque
15
15
  end
16
16
 
17
17
  if multi_jobs_per_fork? && !method_defined?(:shutdown_without_multi_job_forks)
18
+ def work_with_multi_job_forks(*args)
19
+ pid # forces @pid to be set in the parent
20
+ work_without_multi_job_forks(*args)
21
+ release_and_exit! unless is_parent_process?
22
+ end
23
+ alias_method :work_without_multi_job_forks, :work
24
+ alias_method :work, :work_with_multi_job_forks
25
+
18
26
  def perform_with_multi_job_forks(job = nil)
27
+ trap('QUIT') { shutdown } unless fork_hijacked?
19
28
  perform_without_multi_job_forks(job)
20
29
  hijack_fork unless fork_hijacked?
21
30
  @jobs_processed += 1
@@ -31,15 +40,15 @@ module Resque
31
40
  alias_method :shutdown?, :shutdown_with_multi_job_forks?
32
41
 
33
42
  def shutdown_with_multi_job_forks
34
- shutdown_without_multi_job_forks
35
43
  shutdown_child if is_parent_process?
44
+ shutdown_without_multi_job_forks
36
45
  end
37
46
  alias_method :shutdown_without_multi_job_forks, :shutdown
38
47
  alias_method :shutdown, :shutdown_with_multi_job_forks
39
48
 
40
49
  def pause_processing_with_multi_job_forks
41
- pause_processing_without_multi_job_forks
42
50
  shutdown_child if is_parent_process?
51
+ pause_processing_without_multi_job_forks
43
52
  end
44
53
  alias_method :pause_processing_without_multi_job_forks, :pause_processing
45
54
  alias_method :pause_processing, :pause_processing_with_multi_job_forks
@@ -49,7 +58,7 @@ module Resque
49
58
  working_on_without_worker_registration(job)
50
59
  end
51
60
  alias_method :working_on_without_worker_registration, :working_on
52
- alias_method :working_on, :working_on_with_worker_registration
61
+ alias_method :working_on, :working_on_with_worker_registration
53
62
 
54
63
  # Reconnect only once
55
64
  def reconnect_with_multi_job_forks
@@ -62,18 +71,25 @@ module Resque
62
71
  alias_method :reconnect, :reconnect_with_multi_job_forks
63
72
  end
64
73
 
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
74
+ # Need to tell the child to shutdown since it might be looping performing
75
+ # multiple jobs per fork. The QUIT signal normally does a graceful shutdown,
76
+ # and is re-registered in children (term_child normally unregisters it).
67
77
  def shutdown_child
68
78
  begin
69
- Process.kill('TSTP', @child)
79
+ log! "multi_jobs_per_fork: Sending QUIT signal to #{@child}"
80
+ Process.kill('QUIT', @child)
70
81
  rescue Errno::ESRCH
71
82
  nil
72
83
  end
73
84
  end
74
85
 
75
86
  def is_parent_process?
76
- @child
87
+ @child || @pid == Process.pid
88
+ end
89
+
90
+ def release_and_exit!
91
+ release_fork if fork_hijacked?
92
+ run_at_exit_hooks ? exit : exit!(true)
77
93
  end
78
94
 
79
95
  def fork_hijacked?
@@ -87,7 +103,7 @@ module Resque
87
103
  @release_fork_limit = fork_job_limit
88
104
  @jobs_processed = 0
89
105
  @cant_fork = true
90
- trap('TSTP') { shutdown }
106
+ trap('QUIT') { shutdown }
91
107
  end
92
108
 
93
109
  def release_fork
@@ -100,7 +116,7 @@ module Resque
100
116
  end
101
117
 
102
118
  def fork_job_limit
103
- jobs_per_fork.nil? ? Time.now.to_i + seconds_per_fork : jobs_per_fork
119
+ jobs_per_fork.nil? ? Time.now.to_f + seconds_per_fork : jobs_per_fork
104
120
  end
105
121
 
106
122
  def fork_job_limit_reached?
@@ -108,7 +124,7 @@ module Resque
108
124
  end
109
125
 
110
126
  def fork_job_limit_remaining
111
- jobs_per_fork.nil? ? @release_fork_limit - Time.now.to_i : jobs_per_fork - @jobs_processed
127
+ jobs_per_fork.nil? ? @release_fork_limit - Time.now.to_f : jobs_per_fork - @jobs_processed
112
128
  end
113
129
 
114
130
  def seconds_per_fork
@@ -12,72 +12,48 @@ 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
+
22
23
  def log(msg)
23
24
  puts "*** #{msg}" unless ENV['VERBOSE'].nil?
24
25
  end
25
26
  alias_method :log!, :log
26
27
 
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?
46
- end
47
- end
48
28
  end
49
29
  end
50
30
 
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
31
  # test job, tracks sequence.
56
32
  class SequenceJob
57
33
  @queue = :jobs
58
34
  def self.perform(i)
59
- $SEQUENCE << "work_#{i}".to_sym
60
35
  sleep(2)
36
+ $SEQ_WRITER.print "work_#{i}\n"
61
37
  end
62
38
  end
63
39
 
64
40
  class QuickSequenceJob
65
41
  @queue = :jobs
66
42
  def self.perform(i)
67
- $SEQUENCE << "work_#{i}".to_sym
43
+ $SEQ_WRITER.print "work_#{i}\n"
68
44
  end
69
45
  end
70
46
 
71
47
 
72
48
  # test hooks, tracks sequence.
73
49
  Resque.after_fork do
74
- $SEQUENCE << :after_fork
50
+ $SEQ_WRITER.print "after_fork\n"
75
51
  end
76
52
 
77
53
  Resque.before_fork do
78
- $SEQUENCE << :before_fork
54
+ $SEQ_WRITER.print "before_fork\n"
79
55
  end
80
56
 
81
57
  Resque.before_child_exit do |worker|
82
- $SEQUENCE << "before_child_exit_#{worker.jobs_processed}".to_sym
58
+ $SEQ_WRITER.print "before_child_exit_#{worker.jobs_processed}\n"
83
59
  end
@@ -2,8 +2,8 @@ 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
 
@@ -16,39 +16,103 @@ class TestResqueMultiJobForks < Test::Unit::TestCase
16
16
  Resque.enqueue(SequenceJob, 3)
17
17
  Resque.enqueue(SequenceJob, 4)
18
18
 
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
19
+ # INTERVAL=0 will exit when no more jobs left
20
+ @worker.work(0)
21
+ $SEQ_WRITER.close
22
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
24
23
 
25
24
  # test the sequence is correct.
26
25
  assert_equal([:before_fork, :after_fork, :work_1, :work_2, :work_3,
27
26
  :before_child_exit_3, :before_fork, :after_fork, :work_4,
28
- :before_child_exit_1], $SEQUENCE, 'correct sequence')
27
+ :before_child_exit_1], sequence, 'correct sequence')
28
+ end
29
+
30
+ def test_graceful_shutdown_during_first_job
31
+ # enough time for all jobs to process.
32
+ @worker.seconds_per_fork = 60
33
+
34
+ Resque.enqueue(SequenceJob, 1)
35
+ Resque.enqueue(SequenceJob, 2)
36
+ t = Thread.new do
37
+ sleep 1 # before first job can complete
38
+ @worker.shutdown
39
+ end
40
+ # INTERVAL=0 will exit when no more jobs left
41
+ @worker.work(0)
42
+ $SEQ_WRITER.close
43
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
44
+
45
+ # test the sequence is correct.
46
+ assert_equal([:before_fork, :after_fork, :work_1,
47
+ :before_child_exit_1], sequence, 'correct sequence')
48
+ t.join
49
+ end
50
+
51
+ def test_immediate_shutdown_during_first_job
52
+ # enough time for all jobs to process.
53
+ @worker.seconds_per_fork = 60
54
+ @worker.term_child = false
55
+
56
+ Resque.enqueue(SequenceJob, 1)
57
+ Resque.enqueue(SequenceJob, 2)
58
+ t = Thread.new do
59
+ sleep 0.5 # before first job can complete
60
+ Process.kill("INT", @worker.pid) # triggers shutdown! in main thread
61
+ end
62
+ # INTERVAL=0 will exit when no more jobs left
63
+ @worker.work(0)
64
+ $SEQ_WRITER.close
65
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
66
+
67
+ # test the sequence is correct.
68
+ assert_equal([:before_fork, :after_fork], sequence, 'correct sequence')
69
+ t.join
70
+ end
71
+
72
+ def test_sigterm_shutdown_during_first_job
73
+ # enough time for all jobs to process.
74
+ @worker.seconds_per_fork = 60
75
+ @worker.term_child = true
76
+ @worker.term_timeout = 0.5
77
+
78
+ Resque.enqueue(SequenceJob, 1)
79
+ Resque.enqueue(SequenceJob, 2)
80
+ t = Thread.new do
81
+ sleep 1.0 # before first job can complete
82
+ Process.kill("INT", @worker.pid) # triggers shutdown! in main thread
83
+ end
84
+ # INTERVAL=0 will exit when no more jobs left
85
+ @worker.work(0)
86
+ $SEQ_WRITER.close
87
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
88
+
89
+ # test the sequence is correct.
90
+ assert_equal([:before_fork, :after_fork,
91
+ :before_child_exit_1], sequence, 'correct sequence')
92
+ t.join
29
93
  end
30
94
 
31
95
  # test we can also limit fork job process by a job limit.
32
96
  def test_job_limit_sequence_of_events
33
- # only allow enough time for 3 jobs to process.
97
+ # only allow 20 jobs per fork
34
98
  ENV['JOBS_PER_FORK'] = '20'
35
99
 
36
100
  # queue 40 jobs.
37
101
  (1..40).each { |i| Resque.enqueue(QuickSequenceJob, i) }
38
102
 
39
- begin
40
- Timeout::timeout(3) { @worker.work(1) }
41
- rescue Timeout::Error
42
- end
103
+ # INTERVAL=0 will exit when no more jobs left
104
+ @worker.work(0)
105
+ $SEQ_WRITER.close
106
+ sequence = $SEQ_READER.each_line.map {|l| l.strip.to_sym }
43
107
 
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.'
108
+ assert_equal :before_fork, sequence[0], 'first before_fork call.'
109
+ assert_equal :after_fork, sequence[1], 'first after_fork call.'
110
+ assert_equal :work_20, sequence[21], '20th chunk of work.'
111
+ assert_equal :before_child_exit_20, sequence[22], 'first before_child_exit call.'
112
+ assert_equal :before_fork, sequence[23], 'final before_fork call.'
113
+ assert_equal :after_fork, sequence[24], 'final after_fork call.'
114
+ assert_equal :work_40, sequence[44], '40th chunk of work.'
115
+ assert_equal :before_child_exit_20, sequence[45], 'final before_child_exit call.'
52
116
  end
53
117
 
54
118
  def teardown
@@ -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.4
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -10,28 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-02-27 00:00:00.000000000 Z
13
+ date: 2019-02-10 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
- - - ">="
20
- - !ruby/object:Gem::Version
21
- version: '1.24'
22
- - - "<"
19
+ - - "~>"
23
20
  - !ruby/object:Gem::Version
24
- version: '1.27'
21
+ version: 1.26.0
25
22
  type: :runtime
26
23
  prerelease: false
27
24
  version_requirements: !ruby/object:Gem::Requirement
28
25
  requirements:
29
- - - ">="
26
+ - - "~>"
30
27
  - !ruby/object:Gem::Version
31
- version: '1.24'
32
- - - "<"
33
- - !ruby/object:Gem::Version
34
- version: '1.27'
28
+ version: 1.26.0
35
29
  - !ruby/object:Gem::Dependency
36
30
  name: json
37
31
  requirement: !ruby/object:Gem::Requirement
@@ -74,6 +68,20 @@ dependencies:
74
68
  - - ">="
75
69
  - !ruby/object:Gem::Version
76
70
  version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rake
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
77
85
  description: When your resque jobs are frequent and fast, the overhead of forking
78
86
  and running your after_fork might get too big.
79
87
  email:
@@ -89,7 +97,7 @@ files:
89
97
  - test/helper.rb
90
98
  - test/test_resque-multi-job-forks.rb
91
99
  - test/test_rss_reader.rb
92
- homepage: http://github.com/staugaard/resque-multi-job-forks
100
+ homepage: https://github.com/stulentsev/resque-multi-job-forks
93
101
  licenses: []
94
102
  metadata: {}
95
103
  post_install_message:
@@ -107,12 +115,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
115
  - !ruby/object:Gem::Version
108
116
  version: '0'
109
117
  requirements: []
110
- rubyforge_project:
111
- rubygems_version: 2.6.13
118
+ rubygems_version: 3.0.1
112
119
  signing_key:
113
120
  specification_version: 4
114
121
  summary: Have your resque workers process more that one job
115
122
  test_files:
116
- - test/test_resque-multi-job-forks.rb
117
123
  - test/helper.rb
124
+ - test/test_resque-multi-job-forks.rb
118
125
  - test/test_rss_reader.rb