tuber 0.0.1 → 0.6.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 +4 -4
- data/.github/workflows/ruby.yml +68 -0
- data/.gitignore +17 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +134 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +27 -0
- data/README.md +430 -8
- data/Rakefile +35 -0
- data/examples/demo.rb +97 -0
- data/lib/tuber/configuration.rb +49 -0
- data/lib/tuber/connection.rb +500 -0
- data/lib/tuber/errors.rb +78 -0
- data/lib/tuber/job/collection.rb +160 -0
- data/lib/tuber/job/record.rb +226 -0
- data/lib/tuber/job.rb +4 -0
- data/lib/tuber/stats/fast_struct.rb +98 -0
- data/lib/tuber/stats/stat_struct.rb +52 -0
- data/lib/tuber/stats.rb +81 -0
- data/lib/tuber/tube/collection.rb +257 -0
- data/lib/tuber/tube/record.rb +231 -0
- data/lib/tuber/tube.rb +4 -0
- data/lib/tuber/version.rb +6 -0
- data/lib/tuber.rb +125 -2
- data/test/connection_test.rb +627 -0
- data/test/errors_test.rb +35 -0
- data/test/job_test.rb +250 -0
- data/test/jobs_test.rb +152 -0
- data/test/prompt_regexp_test.rb +59 -0
- data/test/stat_struct_test.rb +58 -0
- data/test/stats_test.rb +45 -0
- data/test/test_helper.rb +74 -0
- data/test/tube_test.rb +299 -0
- data/test/tuber_test.rb +112 -0
- data/test/tubes_test.rb +342 -0
- data/tuber.gemspec +33 -0
- metadata +111 -6
data/test/tube_test.rb
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# test/tube_test.rb
|
|
4
|
+
|
|
5
|
+
require File.expand_path('../test_helper', __FILE__)
|
|
6
|
+
|
|
7
|
+
describe Tuber::Tube do
|
|
8
|
+
before do
|
|
9
|
+
@beanstalk = Tuber.new(tuber_address)
|
|
10
|
+
@tube = Tuber::Tube.new(@beanstalk, 'baz')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "for #put" do
|
|
14
|
+
before do
|
|
15
|
+
@time = Time.now.to_i
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should insert a job" do
|
|
19
|
+
@tube.put "bar put #{@time}"
|
|
20
|
+
assert_equal "bar put #{@time}", @tube.peek(:ready).body
|
|
21
|
+
assert_equal 120, @tube.peek(:ready).ttr
|
|
22
|
+
assert_equal 65536, @tube.peek(:ready).pri
|
|
23
|
+
assert_equal 0, @tube.peek(:ready).delay
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should insert a delayed job" do
|
|
27
|
+
@tube.put "delayed put #{@time}", :delay => 1
|
|
28
|
+
assert_equal "delayed put #{@time}", @tube.peek(:delayed).body
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should support custom serializer" do
|
|
32
|
+
Tuber.configure.job_serializer = lambda { |b| JSON.dump(b) }
|
|
33
|
+
@tube.put({ foo: "bar"})
|
|
34
|
+
assert_equal '{"foo":"bar"}', @tube.peek(:ready).body
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
after do
|
|
38
|
+
Tuber::Connection.any_instance.unstub(:transmit)
|
|
39
|
+
Tuber.configure.job_serializer = lambda { |b| b }
|
|
40
|
+
end
|
|
41
|
+
end # put
|
|
42
|
+
|
|
43
|
+
describe "for #peek" do
|
|
44
|
+
before do
|
|
45
|
+
@time = Time.now.to_i
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should peek delayed" do
|
|
49
|
+
@tube.put "foo delay #{@time}", :delay => 1
|
|
50
|
+
assert_equal "foo delay #{@time}", @tube.peek(:delayed).body
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should peek ready" do
|
|
54
|
+
@tube.put "foo ready #{@time}", :delay => 0
|
|
55
|
+
assert_equal "foo ready #{@time}", @tube.peek(:ready).body
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should peek buried" do
|
|
59
|
+
@tube.put "foo buried #{@time}"
|
|
60
|
+
@tube.reserve.bury
|
|
61
|
+
|
|
62
|
+
assert_equal "foo buried #{@time}", @tube.peek(:buried).body
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should return nil for empty peek" do
|
|
66
|
+
assert_nil @tube.peek(:buried)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "supports valid JSON" do
|
|
70
|
+
json = '{ "foo" : "bar" }'
|
|
71
|
+
@tube.put(json)
|
|
72
|
+
assert_equal 'bar', JSON.parse(@tube.peek(:ready).body)['foo']
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "supports non valid JSON" do
|
|
76
|
+
json = '{"message":"hi"}'
|
|
77
|
+
@tube.put(json)
|
|
78
|
+
assert_equal 'hi', JSON.parse(@tube.peek(:ready).body)['message']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "supports passing crlf through" do
|
|
82
|
+
@tube.put("\r\n")
|
|
83
|
+
assert_equal "\r\n", @tube.peek(:ready).body
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "supports passing any byte value through" do
|
|
87
|
+
bytes = (0..255).to_a.pack("c*")
|
|
88
|
+
@tube.put(bytes)
|
|
89
|
+
assert_equal bytes, @tube.peek(:ready).body
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should support custom parser" do
|
|
93
|
+
Tuber.configure.job_parser = lambda { |b| JSON.parse(b) }
|
|
94
|
+
json = '{"message": "hi: there! this: is a long message"}'
|
|
95
|
+
@tube.put(json)
|
|
96
|
+
assert_equal 'hi: there! this: is a long message', @tube.peek(:ready).body['message']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
after do
|
|
100
|
+
Tuber.configure.job_parser = lambda { |b| b }
|
|
101
|
+
end
|
|
102
|
+
end # peek
|
|
103
|
+
|
|
104
|
+
describe "for #reserve" do
|
|
105
|
+
before do
|
|
106
|
+
@time = Time.now.to_i
|
|
107
|
+
@json = %Q&{ "foo" : "#{@time} bar" }&
|
|
108
|
+
@tube.put @json
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should reserve job" do
|
|
112
|
+
@job = @tube.reserve
|
|
113
|
+
assert_equal "#{@time} bar", JSON.parse(@job.body)['foo']
|
|
114
|
+
@job.delete
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should reserve job with block" do
|
|
118
|
+
job = nil
|
|
119
|
+
@tube.reserve { |j| job = j; job.delete }
|
|
120
|
+
assert_equal "#{@time} bar", JSON.parse(job.body)['foo']
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should support custom parser" do
|
|
124
|
+
Tuber.configure.job_parser = lambda { |b| JSON.parse(b) }
|
|
125
|
+
@job = @tube.reserve
|
|
126
|
+
assert_equal "#{@time} bar", @job.body['foo']
|
|
127
|
+
@job.delete
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
after do
|
|
131
|
+
Tuber.configure.job_parser = lambda { |b| b }
|
|
132
|
+
end
|
|
133
|
+
end # reserve
|
|
134
|
+
|
|
135
|
+
describe "for #put with tuber tags" do
|
|
136
|
+
before do
|
|
137
|
+
@beanstalk = stub
|
|
138
|
+
@connection = stub(tube_used: 'baz')
|
|
139
|
+
@beanstalk.stubs(:connection).returns(@connection)
|
|
140
|
+
@tubes = stub
|
|
141
|
+
@tubes.stubs(:use).with('baz').returns('baz')
|
|
142
|
+
@beanstalk.stubs(:tubes).returns(@tubes)
|
|
143
|
+
@tube = Tuber::Tube.new(@beanstalk, 'baz')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should append idp tag as string" do
|
|
147
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:report\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
148
|
+
@tube.put "data", idp: "report"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should append idp tag with ttl as array" do
|
|
152
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:report:300\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
153
|
+
@tube.put "data", idp: ["report", 300]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should append con tag as string" do
|
|
157
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 con:db\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
158
|
+
@tube.put "data", con: "db"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should append con tag with limit as array" do
|
|
162
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 con:db:3\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
163
|
+
@tube.put "data", con: ["db", 3]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should append grp tag" do
|
|
167
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 grp:batch-1\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
168
|
+
@tube.put "data", grp: "batch-1"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should append aft tag" do
|
|
172
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 aft:batch-0\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
173
|
+
@tube.put "data", aft: "batch-0"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should append all four tags" do
|
|
177
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:key:60 con:db:2 grp:g1 aft:g0\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
178
|
+
@tube.put "data", idp: ["key", 60], con: ["db", 2], grp: "g1", aft: "g0"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should send plain put without tags" do
|
|
182
|
+
@connection.expects(:transmit).with("put 65536 0 120 4\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
183
|
+
@tube.put "data"
|
|
184
|
+
end
|
|
185
|
+
end # put with tuber tags
|
|
186
|
+
|
|
187
|
+
describe "for #pause" do
|
|
188
|
+
before do
|
|
189
|
+
@time = Time.now.to_i
|
|
190
|
+
@tube = Tuber::Tube.new(@beanstalk, 'bam')
|
|
191
|
+
@tube.put "foo pause #{@time}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should allow tube pause" do
|
|
195
|
+
assert_equal 0, @tube.stats.pause
|
|
196
|
+
@tube.pause(1)
|
|
197
|
+
assert_equal 1, @tube.stats.pause
|
|
198
|
+
end
|
|
199
|
+
end # pause
|
|
200
|
+
|
|
201
|
+
describe "for #stats" do
|
|
202
|
+
before do
|
|
203
|
+
@time = Time.now.to_i
|
|
204
|
+
@tube.put "foo stats #{@time}"
|
|
205
|
+
@stats = @tube.stats
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "should return total number of jobs in tube" do
|
|
209
|
+
assert_equal 1, @stats['current_jobs_ready']
|
|
210
|
+
assert_equal 0, @stats.current_jobs_delayed
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "should raise error for empty tube" do
|
|
214
|
+
assert_raises(Tuber::NotFoundError) { @beanstalk.tubes.find('fake_tube').stats }
|
|
215
|
+
end
|
|
216
|
+
end # stats
|
|
217
|
+
|
|
218
|
+
describe "for #kick" do
|
|
219
|
+
before do
|
|
220
|
+
@time, @time2 = 2.times.map { Time.now.to_i }
|
|
221
|
+
@tube.put "kick #{@time}"
|
|
222
|
+
@tube.put "kick #{@time2}"
|
|
223
|
+
|
|
224
|
+
2.times.map { @tube.reserve.bury }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "should kick 2 buried jobs" do
|
|
228
|
+
assert_equal 2, @tube.stats.current_jobs_buried
|
|
229
|
+
@tube.kick(2)
|
|
230
|
+
assert_equal 0, @tube.stats.current_jobs_buried
|
|
231
|
+
assert_equal 2, @tube.stats.current_jobs_ready
|
|
232
|
+
end
|
|
233
|
+
end # kick
|
|
234
|
+
|
|
235
|
+
describe "for #clear" do
|
|
236
|
+
@time = Time.now.to_i
|
|
237
|
+
before do
|
|
238
|
+
2.times { |i| @tube.put "to clear success #{i} #{@time}" }
|
|
239
|
+
2.times { |i| @tube.put "to clear delayed #{i} #{@time}", :delay => 5 }
|
|
240
|
+
2.times { |i| @tube.put "to clear bury #{i} #{@time}", :pri => 1 }
|
|
241
|
+
@tube.reserve.bury while @tube.peek(:ready).stats['pri'] == 1
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "should clear all jobs in tube" do
|
|
245
|
+
tube_counts = lambda { %w(ready buried delayed).map { |s| @tube.stats["current_jobs_#{s}"] } }
|
|
246
|
+
assert_equal [2, 2, 2], tube_counts.call
|
|
247
|
+
@tube.clear
|
|
248
|
+
@tube.stats
|
|
249
|
+
assert_equal [0, 0, 0], tube_counts.call
|
|
250
|
+
end
|
|
251
|
+
end # clear
|
|
252
|
+
describe "for #flush" do
|
|
253
|
+
before do
|
|
254
|
+
require_tuber!
|
|
255
|
+
@tube = Tuber::Tube.new(@beanstalk, 'flush_test')
|
|
256
|
+
3.times { |i| @tube.put "flush job #{i}" }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should flush all jobs and return count" do
|
|
260
|
+
count = @tube.flush
|
|
261
|
+
assert_equal 3, count
|
|
262
|
+
assert_nil @tube.peek(:ready)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should return 0 for empty tube" do
|
|
266
|
+
@tube.flush
|
|
267
|
+
count = @tube.flush
|
|
268
|
+
assert_equal 0, count
|
|
269
|
+
end
|
|
270
|
+
end # flush
|
|
271
|
+
|
|
272
|
+
describe "for #flush_buried" do
|
|
273
|
+
before do
|
|
274
|
+
require_tuber!
|
|
275
|
+
@tube = Tuber::Tube.new(@beanstalk, 'flush_buried_test')
|
|
276
|
+
@beanstalk.tubes.watch! 'flush_buried_test'
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
after do
|
|
280
|
+
@tube.clear
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "should flush only buried jobs and return count" do
|
|
284
|
+
2.times { |i| @tube.put "buried job #{i}" }
|
|
285
|
+
2.times { @beanstalk.tubes.reserve.bury }
|
|
286
|
+
@tube.put "ready job"
|
|
287
|
+
|
|
288
|
+
count = @tube.flush_buried
|
|
289
|
+
assert_equal 2, count
|
|
290
|
+
assert_nil @tube.peek(:buried)
|
|
291
|
+
refute_nil @tube.peek(:ready)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it "should return 0 when there are no buried jobs" do
|
|
295
|
+
@tube.put "ready only"
|
|
296
|
+
assert_equal 0, @tube.flush_buried
|
|
297
|
+
end
|
|
298
|
+
end # flush_buried
|
|
299
|
+
end # Tuber::Tube
|
data/test/tuber_test.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path('../test_helper', __FILE__)
|
|
4
|
+
|
|
5
|
+
describe "beanstalk-client" do
|
|
6
|
+
before do
|
|
7
|
+
@beanstalk = Tuber.new(tuber_address)
|
|
8
|
+
@tubes = ['one', 'two', 'three']
|
|
9
|
+
|
|
10
|
+
# Put something on each tube so they exist
|
|
11
|
+
tube_one = @beanstalk.tubes.find('one')
|
|
12
|
+
tube_one.put('one')
|
|
13
|
+
|
|
14
|
+
tube_two = @beanstalk.tubes.find('two')
|
|
15
|
+
tube_two.put('two')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "test thread safe one" do
|
|
19
|
+
before do
|
|
20
|
+
# Create threads that will execute
|
|
21
|
+
# A: use one
|
|
22
|
+
# B: use one
|
|
23
|
+
# B: put two
|
|
24
|
+
# A: put one
|
|
25
|
+
a = Thread.new do
|
|
26
|
+
tube_one = @beanstalk.tubes.find('one')
|
|
27
|
+
sleep 0.5
|
|
28
|
+
tube_one.put('one')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
b = Thread.new do
|
|
32
|
+
sleep 0.125
|
|
33
|
+
tube_two = @beanstalk.tubes.find('two')
|
|
34
|
+
tube_two.put('two')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
a.join
|
|
38
|
+
b.join
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should return correct current-jobs-ready for tube one" do
|
|
42
|
+
one = @beanstalk.tubes.find('one').stats
|
|
43
|
+
assert_equal 2, one.current_jobs_ready
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should return correct current-jobs-ready for tube two" do
|
|
47
|
+
two = @beanstalk.tubes.find('two').stats
|
|
48
|
+
assert_equal 2, two.current_jobs_ready
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "test thread safe two" do
|
|
53
|
+
before do
|
|
54
|
+
a = Thread.new do
|
|
55
|
+
tube_one = @beanstalk.tubes.find('one')
|
|
56
|
+
sleep 0.5
|
|
57
|
+
tube_one.put('one')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
b = Thread.new do
|
|
61
|
+
tube_two = @beanstalk.tubes.find('two')
|
|
62
|
+
sleep 0.125
|
|
63
|
+
tube_two.put('two')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
a.join
|
|
67
|
+
b.join
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should return correct current-jobs-ready for tube one" do
|
|
71
|
+
one = @beanstalk.tubes.find('one').stats
|
|
72
|
+
assert_equal 2, one.current_jobs_ready
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should return correct current-jobs-ready for tube two" do
|
|
76
|
+
two = @beanstalk.tubes.find('two').stats
|
|
77
|
+
assert_equal 2, two.current_jobs_ready
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "test delete job in reserved state" do
|
|
82
|
+
before do
|
|
83
|
+
@tube_three = @beanstalk.tubes.find('three')
|
|
84
|
+
@tube_three.put('one')
|
|
85
|
+
@job = @tube_three.reserve
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "should be deleted properly" do
|
|
89
|
+
assert_equal 'one', @job.body
|
|
90
|
+
assert_equal 'one', @beanstalk.jobs.find(@job.id).body
|
|
91
|
+
@job.delete
|
|
92
|
+
assert_nil @beanstalk.jobs.find(@job.id)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe "test delete job in buried state" do
|
|
97
|
+
before do
|
|
98
|
+
@tube_three = @beanstalk.tubes.find('three')
|
|
99
|
+
@tube_three.put('two')
|
|
100
|
+
@job = @tube_three.reserve
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should delete job as expected in buried state" do
|
|
104
|
+
assert_equal 'two', @job.body
|
|
105
|
+
@job.bury
|
|
106
|
+
assert_equal 'two', @tube_three.peek(:buried).body
|
|
107
|
+
|
|
108
|
+
@job.delete
|
|
109
|
+
assert_nil @beanstalk.jobs.find(@job.id)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|