sidekiq-bulk 0.0.2 → 0.1

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
  SHA1:
3
- metadata.gz: 1ab5533e949a3c0fd54cc150325b0b6fa0c58e18
4
- data.tar.gz: 3734f891f92c8e9668779c8e8ecdb29f118e8456
3
+ metadata.gz: 6e466d4ca76140df65784cbe9c529e50c506e806
4
+ data.tar.gz: b56858162f9045a52b429ab18a42a052be4f5267
5
5
  SHA512:
6
- metadata.gz: 0a84969c10529edd072482cac04682223b0738f3f81a757acf7efa4f42da9b70e814b865de30db8516836891031b739cbb4c3b65abac0c8b1ddb18b2009fe016
7
- data.tar.gz: 048773246bf5ef94b82cbdac5e6e69df7596acf3dc00d219407cc199f1a166b527ea0a80aad0ecf9523bb10028aae2f229b825cdcc2aec3afd312cc555e2bcad
6
+ metadata.gz: 9789acdf3dd12f7113bd2edeeafe57d63d9f2920b6a5072728d0e8bad8c6439adf2cacba9fe1457acab521ac66cd88b165169f858989d8e0d44fc580ea13c4ae
7
+ data.tar.gz: 30fd6e17e8893ab0fcf2ec6367114ad8a18ab00db4643fa117c8a3b3480be7a6a7d12f729da245a5dea67fe285162952218765d7a8babd7f84db8828ab6ea320
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # sidekiq-bulk
2
2
 
3
- [![Build Status](https://travis-ci.org/aprescott/sidekiq-bulk.svg?branch=master)](https://travis-ci.org/aprescott/sidekiq-bulk)
3
+ [![Build Status](https://travis-ci.org/aprescott/sidekiq-bulk.svg?branch=master)](https://travis-ci.org/aprescott/sidekiq-bulk) [![Code Climate](https://codeclimate.com/github/aprescott/sidekiq-bulk/badges/gpa.svg)](https://codeclimate.com/github/aprescott/sidekiq-bulk)
4
4
 
5
5
  Give your workers more to do!
6
6
 
@@ -11,7 +11,7 @@ Sidekiq comes with `Sidekiq::Client.push_bulk` which can be faster than `perform
11
11
  This gem provides a wrapper around `Sidekiq::Client.push_bulk` so that instead of
12
12
 
13
13
  ```ruby
14
- Sidekiq::Client.push_bulk("class" => FooJob, "args" => [[1], [2], [3])
14
+ Sidekiq::Client.push_bulk("class" => FooJob, "args" => [[1], [2], [3]])
15
15
  ```
16
16
 
17
17
  You can write
@@ -67,6 +67,39 @@ all_users.each do |user|
67
67
  end
68
68
  ```
69
69
 
70
+ ## Job count splitting
71
+
72
+ `push_bulk` will only enqueue at most 10,000 jobs at a time. That is, if `items` has 20,000 elements, `push_bulk(items)` will push the first 10,000, then the second 20,000. You can control the threshold with `limit:`.
73
+
74
+ ```ruby
75
+ # push in groups of 50,000 jobs at a time
76
+ FooJob.push_bulk(items, limit: 50_000)
77
+
78
+ # equivalent to FooJob.push_bulk(items, limit: 10_000)
79
+ FooJob.push_bulk(items)
80
+ ```
81
+
82
+ This also works with a block.
83
+
84
+ ```ruby
85
+ # this results in 5 pushes
86
+
87
+ users.length # => 100_000
88
+ FooJob.push_bulk(users, limit: 20_000) do |user|
89
+ [user.id, "some-value"]
90
+ end
91
+ ```
92
+
93
+ And to disable push splitting, use `push_bulk!`.
94
+
95
+ ```ruby
96
+ # one single push of 500,000 jobs, no splitting
97
+
98
+ FooJob.push_bulk!(users) do |user|
99
+ [user.id, "some-value"]
100
+ end
101
+ ```
102
+
70
103
  ### License
71
104
 
72
105
  Copyright (c) 2015 Adam Prescott, licensed under the MIT license. See LICENSE.
data/lib/sidekiq/bulk.rb CHANGED
@@ -1,5 +1,13 @@
1
+ require "active_support/core_ext/array/grouping"
2
+
1
3
  module SidekiqBulk
2
- def push_bulk(items, &block)
4
+ def push_bulk(items, limit: 10_000, &block)
5
+ items.in_groups_of(limit, false).each do |group|
6
+ push_bulk!(group, &block)
7
+ end
8
+ end
9
+
10
+ def push_bulk!(items, &block)
3
11
  if block
4
12
  args = items.map(&block)
5
13
  else
data/sidekiq-bulk.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "sidekiq-bulk"
3
- s.version = "0.0.2"
3
+ s.version = "0.1"
4
4
  s.authors = ["Adam Prescott"]
5
5
  s.email = ["adam@aprescott.com"]
6
6
  s.homepage = "https://github.com/aprescott/sidekiq-bulk"
@@ -13,6 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.licenses = ["MIT"]
14
14
 
15
15
  s.add_dependency("sidekiq")
16
+ s.add_dependency("activesupport")
16
17
  s.add_development_dependency("rspec", ">= 3.3")
17
18
  s.add_development_dependency("rspec-sidekiq")
18
19
  s.add_development_dependency("pry-byebug")
data/spec/examples.txt CHANGED
@@ -1,8 +1,21 @@
1
- example_id | status | run_time |
2
- ---------------------------------- | ------ | --------------- |
3
- ./spec/sidekiq_bulk_spec.rb[1:1] | passed | 0.00181 seconds |
4
- ./spec/sidekiq_bulk_spec.rb[1:2] | passed | 0.00131 seconds |
5
- ./spec/sidekiq_bulk_spec.rb[1:3] | passed | 0.00118 seconds |
6
- ./spec/sidekiq_bulk_spec.rb[1:4] | passed | 0.00685 seconds |
7
- ./spec/sidekiq_bulk_spec.rb[1:5] | passed | 0.00366 seconds |
8
- ./spec/sidekiq_bulk_spec.rb[1:6:1] | passed | 0.00135 seconds |
1
+ example_id | status | run_time |
2
+ -------------------------------------- | ------ | --------------- |
3
+ ./spec/sidekiq_bulk_spec.rb[1:1:1] | passed | 0.00012 seconds |
4
+ ./spec/sidekiq_bulk_spec.rb[1:1:2] | passed | 0.00047 seconds |
5
+ ./spec/sidekiq_bulk_spec.rb[1:1:3] | passed | 0.00036 seconds |
6
+ ./spec/sidekiq_bulk_spec.rb[1:1:4] | passed | 0.00037 seconds |
7
+ ./spec/sidekiq_bulk_spec.rb[1:1:5] | passed | 0.00035 seconds |
8
+ ./spec/sidekiq_bulk_spec.rb[1:1:6] | passed | 0.00074 seconds |
9
+ ./spec/sidekiq_bulk_spec.rb[1:1:7] | passed | 0.00044 seconds |
10
+ ./spec/sidekiq_bulk_spec.rb[1:1:8] | passed | 0.00139 seconds |
11
+ ./spec/sidekiq_bulk_spec.rb[1:1:9:1:1] | passed | 0.00193 seconds |
12
+ ./spec/sidekiq_bulk_spec.rb[1:1:9:2:1] | passed | 0.00221 seconds |
13
+ ./spec/sidekiq_bulk_spec.rb[1:1:9:3:1] | passed | 0.01202 seconds |
14
+ ./spec/sidekiq_bulk_spec.rb[1:2:1] | passed | 0.00074 seconds |
15
+ ./spec/sidekiq_bulk_spec.rb[1:2:2] | passed | 0.00123 seconds |
16
+ ./spec/sidekiq_bulk_spec.rb[1:2:3] | passed | 0.00047 seconds |
17
+ ./spec/sidekiq_bulk_spec.rb[1:2:4] | passed | 0.00692 seconds |
18
+ ./spec/sidekiq_bulk_spec.rb[1:2:5] | passed | 0.00237 seconds |
19
+ ./spec/sidekiq_bulk_spec.rb[1:2:6] | passed | 0.23908 seconds |
20
+ ./spec/sidekiq_bulk_spec.rb[1:3:1] | passed | 0.00145 seconds |
21
+ ./spec/sidekiq_bulk_spec.rb[1:3:2] | passed | 0.00037 seconds |
@@ -10,41 +10,122 @@ RSpec.describe SidekiqBulk do
10
10
  class BarJob < FooJob
11
11
  end
12
12
 
13
- it "provides a push_bulk method on job classes" do
14
- expect(FooJob).to respond_to(:push_bulk)
15
- end
13
+ shared_examples "a bulk push method" do |method_name|
14
+ it "provides a push_bulk method on job classes" do
15
+ expect(FooJob).to respond_to(method_name)
16
+ end
16
17
 
17
- it "enqueues the job" do
18
- FooJob.push_bulk([1, 2, 3]) { |el| [el, "some-value"] }
18
+ it "enqueues the job" do
19
+ FooJob.public_send(method_name, [1, 2, 3]) { |el| [2*el, "some-value"] }
19
20
 
20
- expect(FooJob.jobs.length).to eq(3)
21
- expect(FooJob).to have_enqueued_job(1, "some-value")
22
- expect(FooJob).to have_enqueued_job(2, "some-value")
23
- expect(FooJob).to have_enqueued_job(3, "some-value")
24
- end
21
+ expect(FooJob.jobs.length).to eq(3)
22
+ expect(FooJob).to have_enqueued_job(2, "some-value")
23
+ expect(FooJob).to have_enqueued_job(4, "some-value")
24
+ expect(FooJob).to have_enqueued_job(6, "some-value")
25
+ end
26
+
27
+ it "goes through the Sidekiq::Client interface" do
28
+ expect(Sidekiq::Client).to receive(:push_bulk).once.with("class" => FooJob, "args" => [[1], [2], [3]])
25
29
 
26
- it "goes through the Sidekiq::Client interface" do
27
- expect(Sidekiq::Client).to receive(:push_bulk).with("class" => FooJob, "args" => [[1], [2], [3]])
30
+ FooJob.public_send(method_name, [1, 2, 3])
31
+ end
32
+
33
+ it "uses the correct class name for subclasses" do
34
+ expect(Sidekiq::Client).to receive(:push_bulk).once.with("class" => BarJob, "args" => [[1], [2], [3]])
35
+
36
+ BarJob.push_bulk([1, 2, 3])
37
+ end
28
38
 
29
- FooJob.push_bulk([1, 2, 3])
39
+ it "defaults to the identity function with no block given" do
40
+ FooJob.public_send(method_name, [10, -6.1, "a thing"])
41
+
42
+ expect(FooJob.jobs.length).to eq(3)
43
+ expect(FooJob).to have_enqueued_job(10)
44
+ expect(FooJob).to have_enqueued_job(-6.1)
45
+ expect(FooJob).to have_enqueued_job("a thing")
46
+ end
30
47
  end
31
48
 
32
- it "uses the correct class name for subclasses" do
33
- expect(Sidekiq::Client).to receive(:push_bulk).with("class" => BarJob, "args" => [[1], [2], [3]])
49
+ describe "#push_bulk" do
50
+ include_examples "a bulk push method", :push_bulk
51
+
52
+ it "limits the size of groups" do
53
+ FooJob.push_bulk([1, 2, 3, 4, 5, 6, 7], limit: 3)
54
+
55
+ expect(FooJob.jobs.length).to eq(7)
56
+ expect(FooJob).to have_enqueued_job(1)
57
+ expect(FooJob).to have_enqueued_job(2)
58
+ expect(FooJob).to have_enqueued_job(3)
59
+ expect(FooJob).to have_enqueued_job(4)
60
+ expect(FooJob).to have_enqueued_job(5)
61
+ expect(FooJob).to have_enqueued_job(6)
62
+ expect(FooJob).to have_enqueued_job(7)
63
+ end
64
+
65
+ it "limits with the item transformation" do
66
+ allow(Sidekiq::Client).to receive(:push_bulk)
34
67
 
35
- BarJob.push_bulk([1, 2, 3])
68
+ FooJob.push_bulk([1, 2, 3, 4, 5, 6, 7], limit: 4) do |item|
69
+ [2*item, "some-value"]
70
+ end
71
+
72
+ expect(Sidekiq::Client).to have_received(:push_bulk).exactly(2).times
73
+ expect(Sidekiq::Client).to have_received(:push_bulk).with("class" => FooJob, "args" => [[2, "some-value"], [4, "some-value"], [6, "some-value"], [8, "some-value"]])
74
+ expect(Sidekiq::Client).to have_received(:push_bulk).with("class" => FooJob, "args" => [[10, "some-value"], [12, "some-value"], [14, "some-value"]])
75
+ end
76
+
77
+ it "goes through the Sidekiq::Client interface" do
78
+ allow(Sidekiq::Client).to receive(:push_bulk)
79
+
80
+ FooJob.push_bulk([1, 2, 3, 4, 5, 6, 7], limit: 3)
81
+
82
+ expect(Sidekiq::Client).to have_received(:push_bulk).exactly(3).times
83
+ expect(Sidekiq::Client).to have_received(:push_bulk).with("class" => FooJob, "args" => [[1], [2], [3]])
84
+ expect(Sidekiq::Client).to have_received(:push_bulk).with("class" => FooJob, "args" => [[4], [5], [6]])
85
+ expect(Sidekiq::Client).to have_received(:push_bulk).with("class" => FooJob, "args" => [[7]])
86
+ end
87
+
88
+ context "when no limit is specified" do
89
+ let(:item_count) { 9_999 }
90
+
91
+ before do
92
+ allow(Sidekiq::Client).to receive(:push_bulk)
93
+
94
+ FooJob.push_bulk((1..item_count).to_a)
95
+ end
96
+
97
+ context "when the item count is 10,000" do
98
+ let(:item_count) { 10_000 }
99
+
100
+ specify { expect(Sidekiq::Client).to have_received(:push_bulk).exactly(1).times }
101
+ end
102
+
103
+ context "when the item count is 10,001" do
104
+ let(:item_count) { 10_001 }
105
+
106
+ specify { expect(Sidekiq::Client).to have_received(:push_bulk).exactly(2).times }
107
+ end
108
+
109
+ context "when the item count is 40,000" do
110
+ let(:item_count) { 40_000 }
111
+
112
+ specify { expect(Sidekiq::Client).to have_received(:push_bulk).exactly(4).times }
113
+ end
114
+ end
36
115
  end
37
116
 
38
- it "defaults to the identity function with no block given" do
39
- FooJob.push_bulk([1, 2, 3])
117
+ describe "#push_bulk!" do
118
+ include_examples "a bulk push method", :push_bulk!
40
119
 
41
- expect(FooJob.jobs.length).to eq(3)
42
- expect(FooJob).to have_enqueued_job(1)
43
- expect(FooJob).to have_enqueued_job(2)
44
- expect(FooJob).to have_enqueued_job(3)
120
+ it "does not limit the number of jobs in one push" do
121
+ expect(Sidekiq::Client).to receive(:push_bulk).once.with("class" => FooJob, "args" => (1..100_000).map { |e| [e] })
122
+
123
+ FooJob.push_bulk!((1..100_000).to_a)
124
+ end
45
125
  end
46
126
 
47
127
  describe "inline test", sidekiq: :inline do
48
128
  specify { expect { FooJob.push_bulk([1, 2, 3]) }.to raise_error(RuntimeError, "1") }
129
+ specify { expect { FooJob.push_bulk!([1, 2, 3]) }.to raise_error(RuntimeError, "1") }
49
130
  end
50
131
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-bulk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: '0.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Prescott
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-20 00:00:00.000000000 Z
11
+ date: 2015-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement