sidekiq-bulk 0.0.2 → 0.1

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