sidekiq-batch 0.1.8 → 0.2.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: 56a9cb0fe2d48eba0959e20ad98e52e62e326bb71168f13f4c48e0ad33079447
4
- data.tar.gz: ae145f7d0381f9a83ec2f0ba93be5a290589b1cd77e7544fc20b4f15fc823d0e
3
+ metadata.gz: e0e119457317bdc0bbe60785e41d4fff908f0d818ec760802b96fd9905ad14fc
4
+ data.tar.gz: 46cd957ceb643e946a6bf5ebb49ae93e14e3514384753fa7d910da0476e720b3
5
5
  SHA512:
6
- metadata.gz: 45aee8ac26397847e5738ebf21b34a7505cd367d64596b9e72ff1c51993c622e53850999cb9bcae4fa2152c7e0471c65d3c824af3af06cdefecee199b4f7c8a5
7
- data.tar.gz: 294c3bb0b9f908ee2aabf6dd62adcb296ff6409a148b3db77a456f50c728f369583bf15af4d2aa034947fb5175741985549c70d5328acb8ff471332689ad27c7
6
+ metadata.gz: c86dcadbf158fb9a533163ad042e77d18192241ab7985c4204b6526f78d3cb307a08a48fc173dcef7a7697dfb7316a847fe664ec098178b9e244d7d279978fbb
7
+ data.tar.gz: a070c7c91c502c06adc83269c3f28b97789250a80b8518ce80ba48511ddacb80537e7ffe9075c03f45062384a53b50fd03af46cb9f3b8dba39e3f03f0498e8a8
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+ github: breamware
3
+ open_collective: sidekiq-batch
@@ -1,16 +1,30 @@
1
1
  name: CI
2
2
 
3
- on: [push, pull_request]
3
+ on: [pull_request, workflow_dispatch]
4
4
 
5
5
  jobs:
6
6
  test:
7
+ env:
8
+ REDIS_HOST: 'redis'
7
9
 
8
10
  runs-on: ubuntu-latest
11
+ services:
12
+ redis:
13
+ image: redis
14
+ # Set health checks to wait until redis has started
15
+ options: >-
16
+ --health-cmd "redis-cli ping"
17
+ --health-interval 10s
18
+ --health-timeout 5s
19
+ --health-retries 5
20
+ ports:
21
+ # Maps port 6379 on service container to the host
22
+ - 6379:6379
9
23
 
10
24
  strategy:
11
25
  fail-fast: false
12
26
  matrix:
13
- ruby: ["2.5", "2.6", "2.7", "3.0", "3.1", ruby-head]
27
+ ruby: ["3.0", "3.1", "3.2", ruby-head]
14
28
 
15
29
  steps:
16
30
  - uses: actions/checkout@v2
@@ -0,0 +1,70 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ master ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ master ]
20
+ schedule:
21
+ - cron: '24 3 * * 2'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'ruby' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v3
42
+
43
+ # Initializes the CodeQL tools for scanning.
44
+ - name: Initialize CodeQL
45
+ uses: github/codeql-action/init@v2
46
+ with:
47
+ languages: ${{ matrix.language }}
48
+ # If you wish to specify custom queries, you can do so here or in a config file.
49
+ # By default, queries listed here will override any specified in a config file.
50
+ # Prefix the list here with "+" to use these queries and those in the config file.
51
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
+
53
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
+ # If this step fails, then you should remove it and run the build manually (see below)
55
+ - name: Autobuild
56
+ uses: github/codeql-action/autobuild@v2
57
+
58
+ # ℹ️ Command-line programs to run using the OS shell.
59
+ # 📚 https://git.io/JvXDl
60
+
61
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
+ # and modify them (or add more) to build your code if your project
63
+ # uses a compiled language
64
+
65
+ #- run: |
66
+ # make bootstrap
67
+ # make release
68
+
69
+ - name: Perform CodeQL Analysis
70
+ uses: github/codeql-action/analyze@v2
data/README.md CHANGED
@@ -32,7 +32,41 @@ Or install it yourself as:
32
32
 
33
33
  ## Usage
34
34
 
35
- Sidekiq Batch is drop-in replacement for the API from Sidekiq PRO. See https://github.com/mperham/sidekiq/wiki/Batches for usage.
35
+ Sidekiq Batch is MOSTLY a drop-in replacement for the API from Sidekiq PRO. See https://github.com/mperham/sidekiq/wiki/Batches for usage.
36
+
37
+ ## Caveats/Gotchas
38
+
39
+ Consider the following workflow:
40
+
41
+ * Batch Z created
42
+ * Worker A queued in batch Z
43
+ * Worker A starts Worker B in batch Z
44
+ * Worker B completes *before* worker A does
45
+ * Worker A completes
46
+
47
+ In the standard configuration, the `on(:success)` and `on(:complete)` callbacks will be triggered when Worker B completes.
48
+ This configuration is the default, simply for legacy reasons. This gem adds the following option to the sidekiq.yml options:
49
+
50
+ ```yaml
51
+ :batch_push_interval: 0
52
+ ```
53
+
54
+ When this value is *absent* (aka legacy), Worker A will only push the increment of batch jobs (aka Worker B) *when it completes*
55
+
56
+ When this value is set to `0`, Worker A will increment the count as soon as `WorkerB.perform_async` is called
57
+
58
+ When this value is a positive number, Worker A will wait a maximum of value-seconds before pushing the increment to redis, or until it's done, whichever comes first.
59
+
60
+ This comes into play if Worker A is queueing thousands of WorkerB jobs, or has some other reason for WorkerB to complete beforehand.
61
+
62
+ If you are queueing many WorkerB jobs, it is recommended to set this value to something like `3` to avoid thousands of calls to redis, and call WorkerB like so:
63
+ ```ruby
64
+ WorkerB.perform_in(4.seconds, some, args)
65
+ ```
66
+ this will ensure that the batch callback does not get triggered until WorkerA *and* the last WorkerB job complete.
67
+
68
+ If WorkerA is just slow for whatever reason, setting to `0` will update the batch status immediately so that the callbacks don't fire.
69
+
36
70
 
37
71
  ## Contributing
38
72
 
@@ -40,10 +40,10 @@ module Sidekiq
40
40
 
41
41
  _, _, success, _, complete, pending, children, failure = Sidekiq.redis do |r|
42
42
  r.multi do |pipeline|
43
- pipeline.sadd("BID-#{parent_bid}-success", bid)
43
+ pipeline.sadd("BID-#{parent_bid}-success", [bid])
44
44
  pipeline.expire("BID-#{parent_bid}-success", Sidekiq::Batch::BID_EXPIRE_TTL)
45
45
  pipeline.scard("BID-#{parent_bid}-success")
46
- pipeline.sadd("BID-#{parent_bid}-complete", bid)
46
+ pipeline.sadd("BID-#{parent_bid}-complete", [bid])
47
47
  pipeline.scard("BID-#{parent_bid}-complete")
48
48
  pipeline.hincrby("BID-#{parent_bid}", "pending", 0)
49
49
  pipeline.hincrby("BID-#{parent_bid}", "children", 0)
@@ -81,7 +81,7 @@ module Sidekiq
81
81
  Sidekiq.logger.debug {"Finalize parent complete bid: #{parent_bid}"}
82
82
  _, complete, pending, children, failure = Sidekiq.redis do |r|
83
83
  r.multi do |pipeline|
84
- pipeline.sadd("BID-#{parent_bid}-complete", bid)
84
+ pipeline.sadd("BID-#{parent_bid}-complete", [bid])
85
85
  pipeline.scard("BID-#{parent_bid}-complete")
86
86
  pipeline.hincrby("BID-#{parent_bid}", "pending", 0)
87
87
  pipeline.hincrby("BID-#{parent_bid}", "children", 0)
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  class Batch
3
- VERSION = '0.1.8'.freeze
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
data/lib/sidekiq/batch.rb CHANGED
@@ -20,7 +20,11 @@ module Sidekiq
20
20
  @initialized = false
21
21
  @created_at = Time.now.utc.to_f
22
22
  @bidkey = "BID-" + @bid.to_s
23
- @ready_to_queue = []
23
+ @queued_jids = []
24
+ @pending_jids = []
25
+
26
+ @incremental_push = !Sidekiq.default_configuration[:batch_push_interval].nil?
27
+ @batch_push_interval = Sidekiq.default_configuration[:batch_push_interval]
24
28
  end
25
29
 
26
30
  def description=(description)
@@ -43,10 +47,10 @@ module Sidekiq
43
47
  callback_key = "#{@bidkey}-callbacks-#{event}"
44
48
  Sidekiq.redis do |r|
45
49
  r.multi do |pipeline|
46
- pipeline.sadd(callback_key, JSON.unparse({
50
+ pipeline.sadd(callback_key, [JSON.unparse({
47
51
  callback: callback,
48
52
  opts: options
49
- }))
53
+ })])
50
54
  pipeline.expire(callback_key, BID_EXPIRE_TTL)
51
55
  end
52
56
  end
@@ -64,51 +68,86 @@ module Sidekiq
64
68
  Sidekiq.redis do |r|
65
69
  r.multi do |pipeline|
66
70
  pipeline.hset(@bidkey, "created_at", @created_at)
67
- pipeline.hset(@bidkey, "parent_bid", parent_bid.to_s) if parent_bid
68
71
  pipeline.expire(@bidkey, BID_EXPIRE_TTL)
72
+ if parent_bid
73
+ pipeline.hset(@bidkey, "parent_bid", parent_bid.to_s)
74
+ pipeline.hincrby("BID-#{parent_bid}", "children", 1)
75
+ end
69
76
  end
70
77
  end
71
78
 
72
79
  @initialized = true
73
80
  end
74
81
 
75
- @ready_to_queue = []
82
+ @queued_jids = []
83
+ @pending_jids = []
76
84
 
77
85
  begin
78
86
  parent = Thread.current[:batch]
79
87
  Thread.current[:batch] = self
88
+ Thread.current[:parent_bid] = parent_bid
80
89
  yield
81
90
  ensure
82
91
  Thread.current[:batch] = parent
92
+ Thread.current[:parent_bid] = nil
83
93
  end
84
94
 
85
- return [] if @ready_to_queue.size == 0
95
+ return [] if @queued_jids.size == 0
96
+ conditional_redis_increment!(true)
86
97
 
87
98
  Sidekiq.redis do |r|
88
99
  r.multi do |pipeline|
89
100
  if parent_bid
90
- pipeline.hincrby("BID-#{parent_bid}", "children", 1)
91
- pipeline.hincrby("BID-#{parent_bid}", "total", @ready_to_queue.size)
92
101
  pipeline.expire("BID-#{parent_bid}", BID_EXPIRE_TTL)
93
102
  end
94
103
 
95
- pipeline.hincrby(@bidkey, "pending", @ready_to_queue.size)
96
- pipeline.hincrby(@bidkey, "total", @ready_to_queue.size)
97
104
  pipeline.expire(@bidkey, BID_EXPIRE_TTL)
98
105
 
99
- pipeline.sadd(@bidkey + "-jids", @ready_to_queue)
106
+ pipeline.sadd(@bidkey + "-jids", @queued_jids)
100
107
  pipeline.expire(@bidkey + "-jids", BID_EXPIRE_TTL)
101
108
  end
102
109
  end
103
110
 
104
- @ready_to_queue
111
+ @queued_jids
105
112
  ensure
106
113
  Thread.current[:bid_data] = bid_data
107
114
  end
108
115
  end
109
116
 
110
117
  def increment_job_queue(jid)
111
- @ready_to_queue << jid
118
+ @queued_jids << jid
119
+ @pending_jids << jid
120
+ conditional_redis_increment!
121
+ end
122
+
123
+ def conditional_redis_increment!(force=false)
124
+ if should_increment? || force
125
+ parent_bid = Thread.current[:parent_bid]
126
+ Sidekiq.redis do |r|
127
+ r.multi do |pipeline|
128
+ if parent_bid
129
+ pipeline.hincrby("BID-#{parent_bid}", "total", @pending_jids.length)
130
+ pipeline.expire("BID-#{parent_bid}", BID_EXPIRE_TTL)
131
+ end
132
+
133
+ pipeline.hincrby(@bidkey, "pending", @pending_jids.length)
134
+ pipeline.hincrby(@bidkey, "total", @pending_jids.length)
135
+ pipeline.expire(@bidkey, BID_EXPIRE_TTL)
136
+ end
137
+ end
138
+ @pending_jids = []
139
+ end
140
+ end
141
+
142
+ def should_increment?
143
+ return false unless @incremental_push
144
+ return true if @batch_push_interval == 0 || @queued_jids.length == 1
145
+ now = Time.now.to_f
146
+ @last_increment ||= now
147
+ if @last_increment + @batch_push_interval > now
148
+ @last_increment = now
149
+ return true
150
+ end
112
151
  end
113
152
 
114
153
  def invalidate_all
@@ -130,7 +169,7 @@ module Sidekiq
130
169
  end
131
170
 
132
171
  def valid?(batch = self)
133
- valid = !Sidekiq.redis { |r| r.exists("invalidated-bid-#{batch.bid}") }
172
+ valid = Sidekiq.redis { |r| r.exists("invalidated-bid-#{batch.bid}") }.zero?
134
173
  batch.parent ? valid && valid?(batch.parent) : valid
135
174
  end
136
175
 
@@ -149,7 +188,7 @@ module Sidekiq
149
188
  def process_failed_job(bid, jid)
150
189
  _, pending, failed, children, complete, parent_bid = Sidekiq.redis do |r|
151
190
  r.multi do |pipeline|
152
- pipeline.sadd("BID-#{bid}-failed", jid)
191
+ pipeline.sadd("BID-#{bid}-failed", [jid])
153
192
 
154
193
  pipeline.hincrby("BID-#{bid}", "pending", 0)
155
194
  pipeline.scard("BID-#{bid}-failed")
@@ -166,7 +205,7 @@ module Sidekiq
166
205
  Sidekiq.redis do |r|
167
206
  r.multi do |pipeline|
168
207
  pipeline.hincrby("BID-#{parent_bid}", "pending", 1)
169
- pipeline.sadd("BID-#{parent_bid}-failed", jid)
208
+ pipeline.sadd("BID-#{parent_bid}-failed", [jid])
170
209
  pipeline.expire("BID-#{parent_bid}-failed", BID_EXPIRE_TTL)
171
210
  end
172
211
  end
@@ -188,8 +227,8 @@ module Sidekiq
188
227
  pipeline.hget("BID-#{bid}", "total")
189
228
  pipeline.hget("BID-#{bid}", "parent_bid")
190
229
 
191
- pipeline.srem("BID-#{bid}-failed", jid)
192
- pipeline.srem("BID-#{bid}-jids", jid)
230
+ pipeline.srem("BID-#{bid}-failed", [jid])
231
+ pipeline.srem("BID-#{bid}-jids", [jid])
193
232
  pipeline.expire("BID-#{bid}", BID_EXPIRE_TTL)
194
233
  end
195
234
  end
@@ -209,7 +248,7 @@ module Sidekiq
209
248
  already_processed, _, callbacks, queue, parent_bid, callback_batch = Sidekiq.redis do |r|
210
249
  r.multi do |pipeline|
211
250
  pipeline.hget(batch_key, event_name)
212
- pipeline.hset(batch_key, event_name, true)
251
+ pipeline.hset(batch_key, event_name, 'true')
213
252
  pipeline.smembers(callback_key)
214
253
  pipeline.hget(batch_key, "callback_queue")
215
254
  pipeline.hget(batch_key, "parent_bid")
@@ -253,7 +292,7 @@ module Sidekiq
253
292
  else
254
293
  # Otherwise finalize in sub batch complete callback
255
294
  cb_batch = self.new
256
- cb_batch.callback_batch = true
295
+ cb_batch.callback_batch = 'true'
257
296
  Sidekiq.logger.debug {"Adding callback batch: #{cb_batch.bid} for batch: #{bid}"}
258
297
  cb_batch.on(:complete, "Sidekiq::Batch::Callback::Finalize#dispatch", opts)
259
298
  cb_batch.jobs do
@@ -19,10 +19,9 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "sidekiq", ">= 3"
22
+ spec.add_dependency "sidekiq", ">= 7", "<8"
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 2.1"
25
25
  spec.add_development_dependency "rake", "~> 13.0"
26
26
  spec.add_development_dependency "rspec", "~> 3.0"
27
- spec.add_development_dependency "fakeredis", "~> 0.8.0"
28
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-batch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Naglik
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-24 00:00:00.000000000 Z
11
+ date: 2024-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3'
19
+ version: '7'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '3'
29
+ version: '7'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: bundler
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +72,6 @@ dependencies:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
74
  version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: fakeredis
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 0.8.0
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 0.8.0
83
75
  description: Sidekiq Batch Jobs Implementation
84
76
  email:
85
77
  - marcin.naglik@gmail.com
@@ -87,8 +79,10 @@ executables: []
87
79
  extensions: []
88
80
  extra_rdoc_files: []
89
81
  files:
82
+ - ".github/FUNDING.yml"
90
83
  - ".github/dependabot.yml"
91
84
  - ".github/workflows/ci.yml"
85
+ - ".github/workflows/codeql-analysis.yml"
92
86
  - ".github/workflows/stale.yml"
93
87
  - ".gitignore"
94
88
  - ".rspec"
@@ -124,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
118
  - !ruby/object:Gem::Version
125
119
  version: '0'
126
120
  requirements: []
127
- rubygems_version: 3.3.7
121
+ rubygems_version: 3.5.11
128
122
  signing_key:
129
123
  specification_version: 4
130
124
  summary: Sidekiq Batch Jobs