resque-multi-job-forks 0.4.4 → 0.4.5

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: 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