sidekiq-clutch 2.0.2 → 2.1.0

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