sidekiq-clutch 2.0.2 → 2.1.0

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
2
  SHA256:
3
- metadata.gz: 796b85ff9627cc5bc227521ee81c318f38fc979a7e963df54c762ee174320564
4
- data.tar.gz: 9196201e96688775de3dca64d1322a8709b9641ed9fbbeb9ccdae187e4e7b3b9
3
+ metadata.gz: d471e4ebb14bcae3bcbb03c855ab949b1029b9a2bbb0210927bc6f38b7f8906d
4
+ data.tar.gz: 5059ca6f29a0a3de77bef0060112b7bd448a64db5c18672a859fa91b70f1b7d7
5
5
  SHA512:
6
- metadata.gz: 15b262b976dff8c5fd3034ec4368d8e7058bfb4879234e9ad881893fb00f08aa81c48c9c6b44044b84321606a7ccf6901165c395146fc07cddb0e0bdbece1edd
7
- data.tar.gz: de761b630c42ec9d78f82f9281f442c05fc88fb3161522f7190765603129852afbd1675fb2d93a1bd79ec8f46a979b062f29346e7e5406200ae962847babfa6f
6
+ metadata.gz: 9fec12c8483a7f59a466db621b77cf123bd4bb09671181b397926062b4f1c2a058be992581bb1a8b931394921da372ce737b3e365aff3dd420fe2562b818efc2
7
+ data.tar.gz: a9fc7e8afac8d30c45fa15766cc26eacb524b4f324ded0d6dfda9d314270dabed274831523ed728c8ec08900f9d3f6e540505ff05593daed26badac7cef08c89
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 2.1.0 - Sep 10, 2021
2
+
3
+ * PERF: Don't pass step data in success callback to every batch
4
+
5
+ NOTE: This changes the structure of arguments passed to Batch callbacks, though we
6
+ also handle the old way of doing it (v2.0.2 and before) so it shouldn't affect
7
+ any in-progress batches you may have running.
8
+
1
9
  # 2.0.2 - Sep 24, 2020
2
10
 
3
11
  * PERF: Delete keys based on known values, instead of a glob
@@ -3,9 +3,6 @@ module Sidekiq
3
3
  class JobWrapper
4
4
  include Sidekiq::Worker
5
5
 
6
- # 22 days - how long a Sidekiq job can live with exponential backoff
7
- RESULT_KEY_EXPIRATION_DURATION = 22 * 24 * 60 * 60
8
-
9
6
  def perform(bid, job_class, args, last_result_key, current_result_key)
10
7
  job = Object.const_get(job_class).new
11
8
  assign_previous_results(job, last_result_key)
@@ -14,7 +11,7 @@ module Sidekiq
14
11
  Sidekiq.redis do |redis|
15
12
  redis.multi do |multi|
16
13
  multi.rpush(current_result_key, result.to_json)
17
- multi.expire(current_result_key, RESULT_KEY_EXPIRATION_DURATION)
14
+ multi.expire(current_result_key, TEMPORARY_KEY_EXPIRATION_DURATION)
18
15
  end
19
16
  end
20
17
  end
@@ -4,7 +4,6 @@ module Sidekiq
4
4
  def initialize(service)
5
5
  @service = service
6
6
  @jobs = []
7
- @result_key_prefix = SecureRandom.uuid
8
7
  @result_key_index = 0
9
8
  end
10
9
 
@@ -25,19 +24,18 @@ module Sidekiq
25
24
  @jobs << new_parallel_step unless continue_existing_parallel_step?
26
25
  @jobs.last['parallel'] << [klass.name, params]
27
26
  else
28
- @jobs << { 'series' => [klass.name, params], 'result_key' => next_result_key }
27
+ @jobs << { 'series' => [klass.name, params], 'result_key_index' => next_result_key_index }
29
28
  end
30
29
  end
31
30
 
32
- def next_result_key
31
+ def next_result_key_index
33
32
  @result_key_index += 1
34
- "#{@result_key_prefix}-#{@result_key_index}"
35
33
  end
36
34
 
37
35
  private
38
36
 
39
37
  def new_parallel_step
40
- { 'parallel' => [], 'result_key' => next_result_key, 'parallel_key' => @service.parallel_key }
38
+ { 'parallel' => [], 'result_key_index' => next_result_key_index, 'parallel_key' => @service.parallel_key }
41
39
  end
42
40
 
43
41
  def continue_existing_parallel_step?
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  class Clutch
3
- VERSION = "2.0.2"
3
+ VERSION = '2.1.0'.freeze
4
4
  end
5
5
  end
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  Sidekiq.redis do |redis|
16
16
  redis.multi do |multi|
17
17
  multi.rpush(current_result_key, result.to_json)
18
- multi.expire(current_result_key, RESULT_KEY_EXPIRATION_DURATION)
18
+ multi.expire(current_result_key, TEMPORARY_KEY_EXPIRATION_DURATION)
19
19
  end
20
20
  end
21
21
  end
@@ -6,6 +6,9 @@ require 'sidekiq/clutch/job_wrapper'
6
6
 
7
7
  module Sidekiq
8
8
  class Clutch
9
+ # 22 days - how long a Sidekiq job can live with exponential backoff
10
+ TEMPORARY_KEY_EXPIRATION_DURATION = 22 * 24 * 60 * 60
11
+
9
12
  def initialize(batch = nil)
10
13
  @batch = batch || Sidekiq::Batch.new
11
14
  end
@@ -51,9 +54,10 @@ module Sidekiq
51
54
  def setup_batch
52
55
  jobs_queue = jobs.raw.dup
53
56
  step = jobs_queue.shift
57
+ set_jobs_data_in_redis(jobs_queue)
54
58
  return if step.nil?
55
59
  batch.callback_queue = queue if queue
56
- batch.on(:success, Sidekiq::Clutch, 'jobs' => jobs_queue.dup, 'result_key' => step['result_key'])
60
+ batch.on(:success, Sidekiq::Clutch, 'key_base' => key_base, 'result_key_index' => result_key_index(step))
57
61
  on_failure_name = on_failure&.name
58
62
  batch.on(:complete, Sidekiq::Clutch, 'on_failure' => on_failure_name) if on_failure_name
59
63
  batch.jobs do
@@ -68,14 +72,23 @@ module Sidekiq
68
72
  end
69
73
 
70
74
  def on_success(status, options)
71
- if options['jobs'].empty?
72
- clean_up_result_keys(options['result_key'].sub(/-\d+$/, ''))
75
+ return on_success_legacy(status, options) if options['jobs']
76
+
77
+ raise 'invariant: key_base is missing!' unless options['key_base']
78
+ raise 'invariant: result_key_index is missing!' unless options['result_key_index']
79
+
80
+ # NOTE: This is a brand new instance of Sidekiq::Clutch that Sidekiq instantiates,
81
+ # so we need to set @key_base again.
82
+ @key_base = options['key_base']
83
+ remaining_jobs = JSON.parse(Sidekiq.redis { |r| r.get(jobs_key) })
84
+ if remaining_jobs.empty?
85
+ clean_up_temporary_keys
73
86
  return
74
87
  end
75
88
  parent_batch = Sidekiq::Batch.new(status.parent_bid)
76
89
  service = self.class.new(parent_batch)
77
- service.jobs.raw = options['jobs']
78
- service.current_result_key = options['result_key']
90
+ service.jobs.raw = remaining_jobs
91
+ service.current_result_key = "#{key_base}-#{options['result_key_index']}"
79
92
  service.engage
80
93
  end
81
94
 
@@ -87,19 +100,61 @@ module Sidekiq
87
100
 
88
101
  private
89
102
 
103
+ # accept old style of passing job data, will be removed in 3.0
104
+ def on_success_legacy(status, options)
105
+ @key_base = options['result_key'].sub(/-\d+$/, '')
106
+ if options['jobs'].empty?
107
+ clean_up_result_keys
108
+ return
109
+ end
110
+ parent_batch = Sidekiq::Batch.new(status.parent_bid)
111
+ service = self.class.new(parent_batch)
112
+ service.jobs.raw = options['jobs']
113
+ service.current_result_key = options['result_key']
114
+ service.engage
115
+ end
116
+
117
+ def key_base
118
+ @key_base ||= SecureRandom.uuid
119
+ end
120
+
121
+ def jobs_key
122
+ "#{key_base}-jobs"
123
+ end
124
+
125
+ def set_jobs_data_in_redis(data)
126
+ Sidekiq.redis do |redis|
127
+ redis.multi do |multi|
128
+ multi.set(jobs_key, data.to_json)
129
+ multi.expire(jobs_key, TEMPORARY_KEY_EXPIRATION_DURATION)
130
+ end
131
+ end
132
+ end
133
+
90
134
  def series_step(step)
91
135
  (klass, params) = step['series']
92
- enqueue_job(klass, params, step['result_key'])
136
+ enqueue_job(klass, params, result_key_index(step))
93
137
  end
94
138
 
95
139
  def parallel_step(step)
96
140
  step['parallel'].each do |(klass, params)|
97
- enqueue_job(klass, params, step['result_key'])
141
+ enqueue_job(klass, params, result_key_index(step))
142
+ end
143
+ end
144
+
145
+ def result_key_index(step)
146
+ if step['result_key_index']
147
+ step['result_key_index']
148
+ elsif step['result_key'] # legacy style, will be removed in 3.0
149
+ step['result_key'].split('-').last.to_i
150
+ else
151
+ raise "invariant: expected result_key_index passed in step; got: #{step.inspect}"
98
152
  end
99
153
  end
100
154
 
101
- def enqueue_job(klass, params, result_key)
155
+ def enqueue_job(klass, params, result_key_index)
102
156
  job_options = Object.const_get(klass).sidekiq_options
157
+ result_key = "#{key_base}-#{result_key_index}"
103
158
  options = {
104
159
  'class' => JobWrapper,
105
160
  'queue' => queue || job_options['queue'],
@@ -111,8 +166,9 @@ module Sidekiq
111
166
  Sidekiq::Client.push(options)
112
167
  end
113
168
 
114
- def clean_up_result_keys(key_base)
169
+ def clean_up_temporary_keys
115
170
  Sidekiq.redis do |redis|
171
+ redis.del(jobs_key)
116
172
  result_key_index = 1
117
173
  loop do
118
174
  result = redis.del("#{key_base}-#{result_key_index}")
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_dependency 'sidekiq', '>= 5.0.0'
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 2.1.4'
24
+ spec.add_development_dependency 'bundler', '~> 2.2.4'
25
25
  spec.add_development_dependency 'rake', '~> 13.0.1'
26
26
  spec.add_development_dependency 'rspec', '~> 3.9.0'
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-clutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Morgan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-24 00:00:00.000000000 Z
11
+ date: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.1.4
33
+ version: 2.2.4
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.1.4
40
+ version: 2.2.4
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
110
  requirements: []
111
- rubygems_version: 3.0.3
111
+ rubygems_version: 3.1.6
112
112
  signing_key:
113
113
  specification_version: 4
114
114
  summary: An ergonomic wrapper API for Sidekiq Batches