job_dispatch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +20 -0
  6. data/Guardfile +13 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +85 -0
  9. data/Rakefile +10 -0
  10. data/bin/job-dispatcher +34 -0
  11. data/bin/job-status +69 -0
  12. data/bin/job-worker +40 -0
  13. data/examples/mongoid-job.rb +43 -0
  14. data/job_dispatch.gemspec +33 -0
  15. data/lib/job_dispatch/broker/command.rb +45 -0
  16. data/lib/job_dispatch/broker/internal_job.rb +32 -0
  17. data/lib/job_dispatch/broker/socket.rb +85 -0
  18. data/lib/job_dispatch/broker.rb +523 -0
  19. data/lib/job_dispatch/client/proxy.rb +34 -0
  20. data/lib/job_dispatch/client/proxy_error.rb +18 -0
  21. data/lib/job_dispatch/client/synchronous_proxy.rb +29 -0
  22. data/lib/job_dispatch/client.rb +49 -0
  23. data/lib/job_dispatch/configuration.rb +7 -0
  24. data/lib/job_dispatch/identity.rb +54 -0
  25. data/lib/job_dispatch/job.rb +44 -0
  26. data/lib/job_dispatch/signaller.rb +30 -0
  27. data/lib/job_dispatch/sockets/enqueue.rb +18 -0
  28. data/lib/job_dispatch/status.rb +79 -0
  29. data/lib/job_dispatch/version.rb +3 -0
  30. data/lib/job_dispatch/worker/item.rb +43 -0
  31. data/lib/job_dispatch/worker/socket.rb +96 -0
  32. data/lib/job_dispatch/worker.rb +120 -0
  33. data/lib/job_dispatch.rb +97 -0
  34. data/spec/factories/jobs.rb +19 -0
  35. data/spec/job_dispatch/broker/socket_spec.rb +53 -0
  36. data/spec/job_dispatch/broker_spec.rb +737 -0
  37. data/spec/job_dispatch/identity_spec.rb +88 -0
  38. data/spec/job_dispatch/job_spec.rb +77 -0
  39. data/spec/job_dispatch/worker/socket_spec.rb +32 -0
  40. data/spec/job_dispatch/worker_spec.rb +24 -0
  41. data/spec/job_dispatch_spec.rb +0 -0
  42. data/spec/spec_helper.rb +23 -0
  43. data/spec/support/test_job.rb +30 -0
  44. metadata +255 -0
@@ -0,0 +1,88 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe JobDispatch::Identity do
5
+
6
+ context "with a string" do
7
+
8
+ subject { JobDispatch::Identity.new('hello')}
9
+
10
+ it "returns a string" do
11
+ expect(subject.to_s).to eq('hello')
12
+ end
13
+ it "returns a symbol" do
14
+ expect(subject.to_sym).to eq(:hello)
15
+ end
16
+ it "returns hex string" do
17
+ expect(subject.to_hex).to eq('68656c6c6f')
18
+ end
19
+ end
20
+
21
+ context "with a symbol" do
22
+
23
+ subject { JobDispatch::Identity.new(:hello)}
24
+
25
+ it "returns a string" do
26
+ expect(subject.to_s).to eq('hello')
27
+ end
28
+ it "returns a symbol" do
29
+ expect(subject.to_sym).to eq(:hello)
30
+ end
31
+ it "returns hex string" do
32
+ expect(subject.to_hex).to eq('68656c6c6f')
33
+ end
34
+ end
35
+
36
+
37
+ context "with no-printable characters" do
38
+ let(:worker_id) { [0, 0x80, 0, 0x41, 0xB9].pack('c*') }
39
+ subject { JobDispatch::Identity.new(worker_id)}
40
+
41
+ it "returns a string" do
42
+ expect(subject.to_s).to eq(worker_id)
43
+ end
44
+ it "returns a symbol" do
45
+ expect(subject.to_sym).to eq(worker_id.to_sym)
46
+ end
47
+ it "returns hex string" do
48
+ expect(subject.to_hex).to eq('00800041b9')
49
+ end
50
+ end
51
+
52
+ context "testing equality" do
53
+
54
+ context "with same identity" do
55
+ before :each do
56
+ @a = JobDispatch::Identity.new('a')
57
+ @b = JobDispatch::Identity.new('a')
58
+ end
59
+
60
+ it "is equal to another with the same identity" do
61
+ expect(@a == @b).to be_true
62
+ end
63
+
64
+ it "can look up an item from a hash" do
65
+ hash = {}
66
+ hash[@a] = 'hello'
67
+ expect(hash[@b]).to eq('hello')
68
+ end
69
+ end
70
+
71
+ context "with different identity" do
72
+ before :each do
73
+ @a = JobDispatch::Identity.new('a')
74
+ @b = JobDispatch::Identity.new('b')
75
+ end
76
+
77
+ it "is not equal to another with the same identity" do
78
+ expect(@a == @b).to be_false
79
+ end
80
+
81
+ it "can look up an item from a hash" do
82
+ hash = {}
83
+ hash[@a] = 'hello'
84
+ expect(hash[@b]).to be_nil
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,77 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe JobDispatch::Job do
5
+
6
+ context "succeeded" do
7
+ subject { TestJob.new(1, 'default', JobDispatch::Job::PENDING) }
8
+ before(:each) { subject.succeeded!('result') }
9
+ it "sets the status to COMPLETED" do
10
+ expect(subject.status).to eq(JobDispatch::Job::COMPLETED)
11
+ end
12
+
13
+ it "sets the result" do
14
+ expect(subject.result).to eq('result')
15
+ end
16
+ it "sets completed at" do
17
+ expect(subject.completed_at).to be_within(1.second).of(Time.now)
18
+ end
19
+ end
20
+
21
+ context "failed" do
22
+ subject { TestJob.new(1, 'default', JobDispatch::Job::PENDING) }
23
+ before(:each) { subject.failed!('error message') }
24
+ it "sets the status to FAILED" do
25
+ expect(subject.status).to eq(JobDispatch::Job::FAILED)
26
+ end
27
+
28
+ it "sets the result" do
29
+ expect(subject.result).to eq('error message')
30
+ end
31
+
32
+ it "sets completed at" do
33
+ expect(subject.completed_at).to be_within(1.second).of(Time.now)
34
+ end
35
+
36
+ context "and retryable" do
37
+ before(:each) do
38
+ subject.retry_count = 1
39
+ subject.retry_delay = 10
40
+ subject.failed!('error message')
41
+ end
42
+ it "sets the status to PENDING" do
43
+ expect(subject.status).to eq(JobDispatch::Job::PENDING)
44
+ end
45
+ it "sets completed at" do
46
+ expect(subject.completed_at).to be_within(1.second).of(Time.now)
47
+ end
48
+ it "sets the result" do
49
+ expect(subject.result).to eq('error message')
50
+ end
51
+ it "decrements retry_count" do
52
+ expect(subject.retry_count).to eq(0)
53
+ end
54
+ it "doubles the retry_delay" do
55
+ expect(subject.retry_delay).to eq(20)
56
+ end
57
+ end
58
+
59
+ context "and out of retries" do
60
+ before(:each) do
61
+ subject.retry_count = 0
62
+ subject.retry_delay = 20
63
+ subject.failed!('error message')
64
+ end
65
+ it "sets the status to PENDING" do
66
+ expect(subject.status).to eq(JobDispatch::Job::FAILED)
67
+ end
68
+ it "sets completed at" do
69
+ expect(subject.completed_at).to be_within(1.second).of(Time.now)
70
+ end
71
+ it "sets the result" do
72
+ expect(subject.result).to eq('error message')
73
+ end
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,32 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe JobDispatch::Worker::Socket do
5
+
6
+ let(:endpoint) { 'ipc://test' }
7
+ let(:item_class) do
8
+ double('ItemClass')
9
+ end
10
+ subject { JobDispatch::Worker::Socket.new(endpoint, item_class) }
11
+
12
+ context "receiving a message" do
13
+ before :each do
14
+ @item = double('Item')
15
+ @item.stub(:job_id=)
16
+ item_class.stub(:new).and_return(@item)
17
+ end
18
+
19
+ it "creates an item of the right class" do
20
+ item_class.should_receive(:new)
21
+ subject.socket.stub(:recv).and_return(JSON.dump({command:'idle'}))
22
+ expect(subject.read_item).to eq(@item)
23
+ end
24
+
25
+ it "quits when a quit message is received" do
26
+ Process.stub(:exit).and_return(nil)
27
+ Process.should_receive(:exit).with(0)
28
+ subject.socket.stub(:recv).and_return(JSON.dump({command:'quit'}))
29
+ subject.read_item
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe JobDispatch::Worker do
5
+
6
+ Item ||= JobDispatch::Worker::Item
7
+ Socket ||= JobDispatch::Worker::Socket
8
+
9
+ context "with custom item class" do
10
+ before :each do
11
+ @klass = double('ItemClass')
12
+ @worker = JobDispatch::Worker.new('ipc://test', item_class: @klass)
13
+ end
14
+ it "initialises the custom item class" do
15
+ expect(@worker.item_class).to eq(@klass)
16
+ end
17
+
18
+ it "sets the custom class on the socket" do
19
+ @worker.connect
20
+ expect(@worker.socket.item_class).to eq(@klass)
21
+ end
22
+ end
23
+
24
+ end
File without changes
@@ -0,0 +1,23 @@
1
+ require 'simplecov'
2
+ SimpleCov.start 'rails'
3
+
4
+ require 'job_dispatch'
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc,
7
+ # in spec/support/ and its subdirectories.
8
+ Dir["spec/support/**/*.rb"].each { |f| load f }
9
+
10
+ require 'factory_girl'
11
+ Dir["spec/factories/**/*.rb"].each { |f| load f }
12
+
13
+ require 'timecop'
14
+
15
+
16
+ RSpec.configure do |config|
17
+
18
+ # Run specs in random order to surface order dependencies. If you find an
19
+ # order dependency and want to debug it, you can fix the order by providing
20
+ # the seed, which is printed after each run.
21
+ # --seed 1234
22
+ #config.order = "random"
23
+ end
@@ -0,0 +1,30 @@
1
+ # This class is a stand in for an ActiveRecord or Mongoid::Document style class
2
+ TestJob = Struct.new :id,
3
+ :queue,
4
+ :status,
5
+ :enqueued_at,
6
+ :expire_execution_at,
7
+ :scheduled_at,
8
+ :completed_at,
9
+ :result,
10
+ :target,
11
+ :method,
12
+ :parameters,
13
+ :retry_count,
14
+ :retry_delay,
15
+ :timeout do
16
+ include JobDispatch::Job
17
+
18
+ def save!
19
+ true
20
+ end
21
+
22
+ def save
23
+ true
24
+ end
25
+
26
+ def reload
27
+ self
28
+ end
29
+
30
+ end
metadata ADDED
@@ -0,0 +1,255 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: job_dispatch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Connolly
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rbczmq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nullobject
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'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: text-table
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.14'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.14'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.8'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: timecop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.7'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.7'
139
+ - !ruby/object:Gem::Dependency
140
+ name: factory_girl
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 4.3.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 4.3.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Job Dispatch to workers via ZeroMQ
168
+ email:
169
+ - matt@mobiledataanywhere.com
170
+ executables:
171
+ - job-dispatcher
172
+ - job-status
173
+ - job-worker
174
+ extensions: []
175
+ extra_rdoc_files: []
176
+ files:
177
+ - ".gitignore"
178
+ - ".rspec"
179
+ - ".travis.yml"
180
+ - Gemfile
181
+ - Guardfile
182
+ - LICENSE.txt
183
+ - README.md
184
+ - Rakefile
185
+ - bin/job-dispatcher
186
+ - bin/job-status
187
+ - bin/job-worker
188
+ - examples/mongoid-job.rb
189
+ - job_dispatch.gemspec
190
+ - lib/job_dispatch.rb
191
+ - lib/job_dispatch/broker.rb
192
+ - lib/job_dispatch/broker/command.rb
193
+ - lib/job_dispatch/broker/internal_job.rb
194
+ - lib/job_dispatch/broker/socket.rb
195
+ - lib/job_dispatch/client.rb
196
+ - lib/job_dispatch/client/proxy.rb
197
+ - lib/job_dispatch/client/proxy_error.rb
198
+ - lib/job_dispatch/client/synchronous_proxy.rb
199
+ - lib/job_dispatch/configuration.rb
200
+ - lib/job_dispatch/identity.rb
201
+ - lib/job_dispatch/job.rb
202
+ - lib/job_dispatch/signaller.rb
203
+ - lib/job_dispatch/sockets/enqueue.rb
204
+ - lib/job_dispatch/status.rb
205
+ - lib/job_dispatch/version.rb
206
+ - lib/job_dispatch/worker.rb
207
+ - lib/job_dispatch/worker/item.rb
208
+ - lib/job_dispatch/worker/socket.rb
209
+ - spec/factories/jobs.rb
210
+ - spec/job_dispatch/broker/socket_spec.rb
211
+ - spec/job_dispatch/broker_spec.rb
212
+ - spec/job_dispatch/identity_spec.rb
213
+ - spec/job_dispatch/job_spec.rb
214
+ - spec/job_dispatch/worker/socket_spec.rb
215
+ - spec/job_dispatch/worker_spec.rb
216
+ - spec/job_dispatch_spec.rb
217
+ - spec/spec_helper.rb
218
+ - spec/support/test_job.rb
219
+ homepage: ''
220
+ licenses:
221
+ - MIT
222
+ metadata: {}
223
+ post_install_message:
224
+ rdoc_options: []
225
+ require_paths:
226
+ - lib
227
+ required_ruby_version: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - ">="
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ required_rubygems_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ requirements: []
238
+ rubyforge_project:
239
+ rubygems_version: 2.2.2
240
+ signing_key:
241
+ specification_version: 4
242
+ summary: Job Dispatch is a gem for dispatching jobs to workers in an asynchronous
243
+ manner. Job Dispatch does not require any specific database. Jobs are dispatched
244
+ using ZeroMQ messages and workers can be implemented in any language.
245
+ test_files:
246
+ - spec/factories/jobs.rb
247
+ - spec/job_dispatch/broker/socket_spec.rb
248
+ - spec/job_dispatch/broker_spec.rb
249
+ - spec/job_dispatch/identity_spec.rb
250
+ - spec/job_dispatch/job_spec.rb
251
+ - spec/job_dispatch/worker/socket_spec.rb
252
+ - spec/job_dispatch/worker_spec.rb
253
+ - spec/job_dispatch_spec.rb
254
+ - spec/spec_helper.rb
255
+ - spec/support/test_job.rb