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.
data/README.md CHANGED
@@ -1,14 +1,436 @@
1
- # tuber
1
+ # Tuber
2
2
 
3
- Ruby client for [Tuber](https://github.com/tuberq/tuber), a fast job queue server written in Rust. Tuber is wire-compatible with [beanstalkd](https://github.com/beanstalkd/beanstalkd) and extends it with unique jobs, concurrency control, job group pipelines, batch operations, weighted queues, and offloaded job bodies.
3
+ The Ruby client for [Tuber](https://github.com/tuberq/tuber), the simple, fast job queue server. One binary, zero dependencies with unique/idempotent jobs, concurrency keys, job group pipelines, weighted tubes and batch operations built in.
4
4
 
5
- **This gem currently reserves the name while the client is finalized.** In the meantime, use [russet](https://github.com/dkam/russet) a beaneater fork with full support for Tuber's protocol extensions to talk to Tuber or beanstalkd from Ruby.
5
+ Tuber (the server and this gem) is wire-compatible with [beanstalkd](https://github.com/beanstalkd/beanstalkd): point this client at a stock beanstalkd and everything except the Tuber-only extensions works unchanged. The gem is a fork of [beaneater](https://github.com/beanstalkd/beaneater), so it is also a drop-in beaneater replacement — see [Migrating from beaneater](#migrating-from-beaneater).
6
6
 
7
- ## Links
7
+ ## Quick Start
8
8
 
9
- - Tuber server: https://github.com/tuberq/tuber
10
- - Ruby client (current): https://github.com/dkam/russet
9
+ ```ruby
10
+ @tuber = Tuber.new('localhost:11300')
11
11
 
12
- ## License
12
+ @tube = @tuber.tubes["my-tube"]
13
+ @tube.put '{"key": "foo"}', pri: 5
14
+ @tube.put '{"key": "bar"}', delay: 3
13
15
 
14
- MIT
16
+ while @tube.peek(:ready)
17
+ job = @tube.reserve
18
+ puts job.body
19
+ job.delete
20
+ end
21
+
22
+ @tuber.close
23
+ ```
24
+
25
+ ## Installation
26
+
27
+ Install the [tuber server](https://github.com/tuberq/tuber) (a single binary; `brew install tuberq/tuber/tuber`, Docker image `ghcr.io/tuberq/tuber`, or a [release binary](https://github.com/tuberq/tuber/releases)) — or use an existing beanstalkd — then add the gem:
28
+
29
+ ```ruby
30
+ # Gemfile
31
+ gem 'tuber'
32
+ ```
33
+
34
+ ## Migrating from beaneater
35
+
36
+ Tuber forked from beaneater 1.1.4. The rename is the only breaking change — every
37
+ method, option and return value is unchanged. Two things to update:
38
+
39
+ ```ruby
40
+ require 'beaneater' # before
41
+ require 'tuber' # after
42
+
43
+ @beanstalk = Beaneater.new('localhost:11300') # before
44
+ @beanstalk = Tuber.new('localhost:11300') # after
45
+ ```
46
+
47
+ That includes the nested constants (`Beaneater::Job` → `Tuber::Job`), the error
48
+ classes (`Beaneater::NotConnected` → `Tuber::NotConnected`) and the configuration
49
+ block (`Beaneater.configure` → `Tuber.configure`). In most codebases a
50
+ case-sensitive `Beaneater` → `Tuber` and `beaneater` → `tuber` replacement is the
51
+ whole migration.
52
+
53
+ The `BEANSTALKD_URL` environment variable is still honoured (it's a beanstalkd
54
+ convention, not a beaneater one), though `TUBER_URL` now takes precedence.
55
+ Likewise `config.beanstalkd_url` remains as an alias for `config.tuber_url`.
56
+
57
+ ## Usage
58
+
59
+ ### Configuration
60
+
61
+ To setup advanced options for tuber, you can pass configuration options using:
62
+
63
+ ```ruby
64
+ Tuber.configure do |config|
65
+ # config.default_put_delay = 0
66
+ # config.default_put_pri = 65536
67
+ # config.default_put_ttr = 120
68
+ # config.job_parser = lambda { |body| body }
69
+ # config.job_serializer = lambda { |body| body }
70
+ # config.tuber_url = 'localhost:11300'
71
+ # config.connect_timeout = nil
72
+ # config.resolv_timeout = nil
73
+ # config.read_timeout = nil
74
+ # config.write_timeout = nil
75
+ # config.connect_retries = 0
76
+ # config.connect_retry_interval = 1
77
+ end
78
+ ```
79
+
80
+ The above options are all defaults, so only include a configuration block if you need to make changes.
81
+
82
+ `connect_timeout` and `resolv_timeout` are passed through to `TCPSocket.new` on Ruby 3.0 and newer (ignored on Ruby < 3.0 for compatibility). `read_timeout` and `write_timeout` apply socket read and write timeouts via `setsockopt`.
83
+
84
+ ### Connection
85
+
86
+ ```ruby
87
+ @tuber = Tuber.new('10.0.1.5:11300')
88
+
89
+ # Or use ENV['TUBER_URL'] (or ENV['BEANSTALKD_URL'])
90
+ @tuber = Tuber.new
91
+
92
+ @tuber.close
93
+ ```
94
+
95
+ #### Surviving a server restart
96
+
97
+ An established connection heals itself: when a command notices the socket has
98
+ died, the connection reconnects, replays its tube state, and re-sends the
99
+ command (except for the non-idempotent verbs — `put`, `delete`, `release`,
100
+ `bury`, `touch`, `kick` — where a blind re-send could duplicate a job or act on
101
+ a reservation that died with the old socket, so the original error is raised
102
+ for the caller to decide).
103
+
104
+ That healing gets three connect attempts a second apart. A server restart
105
+ typically takes longer, so give the client a budget that outlasts it:
106
+
107
+ ```ruby
108
+ Tuber.configure do |config|
109
+ config.connect_retries = 15 # extra attempts, 0 = a single attempt
110
+ config.connect_retry_interval = 1 # seconds between them
111
+ end
112
+ ```
113
+
114
+ `connect_retries` covers both a cold start (`Tuber.new` while the server is
115
+ down) and reconnects on a client you already hold. It defaults to `0` — a
116
+ single attempt — which is the historical behaviour.
117
+
118
+ When a command does surface `Tuber::NotConnected`, heal the client you have
119
+ rather than building a new one:
120
+
121
+ ```ruby
122
+ begin
123
+ job = @tuber.tubes.reserve
124
+ rescue Tuber::NotConnected
125
+ @tuber.reconnect! # watched tubes, their weights, the used tube and the
126
+ retry # reserve mode all come back with it
127
+ end
128
+ ```
129
+
130
+ A fresh `Tuber.new` starts with no tube state, so a worker that reconnects that
131
+ way has to re-watch and re-weight every tube by hand — and will silently fall
132
+ back to FIFO if it forgets the reserve mode. `reconnect!` replays all of it.
133
+ It takes the same budget as the config, overridable per call:
134
+ `@tuber.reconnect!(tries: 30, retry_interval: 2)`.
135
+
136
+ ### Tubes
137
+
138
+ Tubes are named work queues. Jobs are `put` into the used tube and `reserve`d from watched tubes. Each tube has a _ready_, _delayed_, and _buried_ queue.
139
+
140
+ ```ruby
141
+ @tube = @tuber.tubes.find("some-tube")
142
+
143
+ # Watch tubes for reserving jobs
144
+ @tuber.tubes.watch!('some-tube') # watch only these tubes
145
+ @tuber.tubes.watch('another-tube') # append to watch list
146
+ @tuber.tubes.ignore('some-tube') # stop watching
147
+
148
+ # List tubes
149
+ @tuber.tubes.all # => [<Tube name='foo'>, <Tube name='bar'>]
150
+ @tuber.tubes.used # => <Tube name='bar'>
151
+ @tuber.tubes.watched # => [<Tube name='foo'>]
152
+
153
+ # Manage tubes
154
+ @tube.pause(3) # pause for 3 seconds
155
+ @tube.clear # delete all jobs
156
+ @tube.flush # delete all jobs, returns count
157
+ @tube.flush_buried # delete only buried jobs (Tuber only), returns count
158
+ ```
159
+
160
+ Each client manages two separate concerns: **use**/**using** controls where `put` places jobs, and **watch**/**watching** controls where `reserve` takes jobs from. These are fully orthogonal.
161
+
162
+ ### Jobs
163
+
164
+ A job has a body (string) and metadata. The typical lifecycle:
165
+
166
+ ```
167
+ put reserve delete
168
+ -----> [READY] ---------> [RESERVED] --------> *poof*
169
+ ```
170
+
171
+ Jobs are in one of three states:
172
+
173
+ | State | Description |
174
+ | ------- | ----------- |
175
+ | ready | Waiting to be reserved and processed. |
176
+ | delayed | Waiting to become ready after a delay. |
177
+ | buried | Held aside after failure, waiting to be kicked. |
178
+
179
+ #### Inserting jobs
180
+
181
+ ```ruby
182
+ @tube.put "job-data-here"
183
+ @tube.put({foo: 'bar'}.to_json)
184
+ @tube.put "job-data-here", pri: 1000, delay: 50, ttr: 200
185
+ ```
186
+
187
+ - **pri** — integer < 2^32, lower values run first (default: 65536)
188
+ - **delay** — seconds to wait before the job becomes ready (default: 0)
189
+ - **ttr** — time to run, seconds a worker has to finish the job (default: 120)
190
+
191
+ #### Reserving and processing jobs
192
+
193
+ ```ruby
194
+ job = @tuber.tubes.reserve # blocks until a job is available
195
+ job = @tuber.tubes.reserve(5) # wait up to 5 seconds
196
+
197
+ puts job.body
198
+ puts job.tube
199
+ puts job.stats.state # => 'reserved'
200
+
201
+ job.touch # extend ttr
202
+ job.delete # success
203
+ job.release delay: 5 # retry later
204
+ job.bury # set aside for inspection
205
+ ```
206
+
207
+ #### Peeking and kicking
208
+
209
+ ```ruby
210
+ @tuber.jobs.find(123) # peek at a specific job
211
+ @tube.peek(:ready) # peek at next ready job
212
+ @tube.peek(:buried)
213
+ @tube.peek(:delayed)
214
+
215
+ @tuber.tubes['some-tube'].kick(3) # kick 3 buried jobs back to ready
216
+ ```
217
+
218
+ #### Automatic processing
219
+
220
+ Register handlers for tubes and let `process!` loop over incoming jobs:
221
+
222
+ ```ruby
223
+ @tuber.jobs.register('some-tube', retry_on: [SomeError]) do |job|
224
+ do_something(job)
225
+ end
226
+
227
+ @tuber.jobs.register('other-tube') do |job|
228
+ do_something_else(job)
229
+ end
230
+
231
+ @tuber.jobs.process!
232
+ ```
233
+
234
+ The loop reserves a job, calls the matching handler, then: deletes on success, releases on `retry_on` errors, and buries on other exceptions. Raise `AbortProcessingError` to stop the loop.
235
+
236
+ ### Job Dependencies (Tuber only)
237
+
238
+ When using [Tuber](https://github.com/tuberq/tuber), you can group related jobs and chain dependent work using `group:` and `after:` options on `put`. After-jobs are held until every job in the group they depend on has been deleted:
239
+
240
+ ```ruby
241
+ # Fan-out: enqueue grouped work
242
+ @tube.put "import-row-1", group: "import"
243
+ @tube.put "import-row-2", group: "import"
244
+
245
+ # Fan-in: this job waits until all "import" jobs are deleted
246
+ @tube.put "send-summary", after: "import"
247
+ ```
248
+
249
+ Chain stages together by combining `after:` and `group:` on the same job to build a simple DAG pipeline:
250
+
251
+ ```ruby
252
+ @tube.put "row-1", group: "extract"
253
+ @tube.put "row-2", group: "extract"
254
+ @tube.put "transform", after: "extract", group: "transform"
255
+ @tube.put "load", after: "transform"
256
+ ```
257
+
258
+ Here `transform` waits for the extract group to finish, then becomes part of the `transform` group. `load` waits for `transform` to complete.
259
+
260
+ Buried jobs block group completion — kick them to let the group finish. Group names are global and can span multiple tubes.
261
+
262
+ ### Unique Jobs / Idempotency (Tuber only)
263
+
264
+ Prevent duplicate jobs with the `idempotency:` option. If a job with the same key already exists in the tube, the original job is returned instead of creating a duplicate:
265
+
266
+ ```ruby
267
+ @tube.put "send-report", idempotency: "daily-report"
268
+ # => <Tuber::Job id=1 body="send-report">
269
+
270
+ @tube.put "send-report", idempotency: "daily-report"
271
+ # => <Tuber::Job id=1 body="send-report"> (same job, no duplicate created)
272
+ ```
273
+
274
+ The key is scoped to the tube and cleared when the job is deleted, so the same key can be reused afterwards.
275
+
276
+ Add a cooldown TTL to keep deduplicating for N seconds after deletion — useful for preventing rapid resubmission:
277
+
278
+ ```ruby
279
+ @tube.put "send-report", idempotency: "daily-report", idempotency_ttl: 300
280
+ ```
281
+
282
+ ### Concurrency Keys (Tuber only)
283
+
284
+ Limit parallel processing of related jobs. When a job with a concurrency key is reserved, other ready jobs sharing the same key are hidden from `reserve` until the reservation ends:
285
+
286
+ ```ruby
287
+ # Only one job per user can be processed at a time
288
+ @tube.put "process-user-42", concurrency: "user-42"
289
+ @tube.put "process-user-42-again", concurrency: "user-42"
290
+ ```
291
+
292
+ The second job won't be reserved until the first is deleted, released, or buried. Set a higher limit to allow N concurrent reservations:
293
+
294
+ ```ruby
295
+ # Allow up to 3 concurrent API jobs
296
+ @tube.put "api-call-1", concurrency: "api", concurrency_limit: 3
297
+ @tube.put "api-call-2", concurrency: "api", concurrency_limit: 3
298
+ ```
299
+
300
+ ### Weighted Tubes (Tuber only)
301
+
302
+ By default, `reserve` picks the highest-priority job across all watched tubes. Switch to weighted mode to select tubes randomly in proportion to their weight:
303
+
304
+ ```ruby
305
+ @tuber.tubes.watch('email')
306
+ @tuber.tubes.watch('notifications', weight: 2)
307
+ @tuber.tubes.watch('batch-jobs', weight: 6)
308
+
309
+ @tuber.tubes.reserve_mode(:weighted)
310
+
311
+ job = @tuber.tubes.reserve # batch-jobs selected 6x as often as email
312
+ ```
313
+
314
+ Tubes default to weight 1. Switch back with `reserve_mode(:fifo)`.
315
+
316
+ Weights and reserve mode are per-connection server state. The client remembers
317
+ both and replays them if the connection has to be re-established, so a worker
318
+ that heals across a server blip keeps reserving by weight.
319
+
320
+ ### Batch Reserve (Tuber only)
321
+
322
+ Reserve multiple jobs atomically in a single call:
323
+
324
+ ```ruby
325
+ jobs = @tuber.tubes.reserve_batch(10) # up to 10 jobs
326
+
327
+ jobs.each do |job|
328
+ process(job)
329
+ job.delete
330
+ end
331
+ ```
332
+
333
+ By default `reserve_batch` is non-blocking — it returns whatever is ready
334
+ immediately, possibly an empty array. Pass a timeout (in seconds) to long-poll
335
+ instead: the call blocks until the first job arrives, then drains everything
336
+ ready up to `count`, or returns an empty array when the timeout elapses. This
337
+ avoids hot-looping a worker on empty polls.
338
+
339
+ ```ruby
340
+ jobs = @tuber.tubes.reserve_batch(10, 30) # block up to 30s for the first job
341
+ ```
342
+
343
+ While blocked, a positive-timeout batch reserve may raise
344
+ `Tuber::DeadlineSoonError` if one of the connection's already-reserved jobs
345
+ is about to hit its TTR — service that job, then reserve again.
346
+
347
+ ### Batch Touch (Tuber 0.12.0+)
348
+
349
+ A batch reserve starts the TTR clock on every job at the same instant, but a
350
+ worker processes them serially — so the tail of a large batch can expire and
351
+ return to the queue while the worker is still busy. `touch_all` extends the TTR
352
+ of every job the connection currently holds in a single command:
353
+
354
+ ```ruby
355
+ jobs = @tuber.tubes.reserve_batch(10)
356
+
357
+ jobs.each do |job|
358
+ process(job)
359
+ job.delete
360
+ @tuber.jobs.touch_all # heartbeat whatever is still held
361
+ end
362
+ ```
363
+
364
+ No ids are sent: the server tracks the reserved set per connection, so jobs
365
+ already deleted, released, buried or lost to a TTR timeout are simply absent.
366
+ Each job keeps its own TTR — deadlines are extended individually, not levelled
367
+ onto a common value.
368
+
369
+ The return value is how many jobs the connection *actually* still holds. A count
370
+ lower than expected means jobs hit their TTR and went back to the queue while the
371
+ worker was busy — otherwise invisible, since nothing notifies a worker that it
372
+ lost a job.
373
+
374
+ ### Stats
375
+
376
+ ```ruby
377
+ @tuber.stats # server-wide stats
378
+ @tuber.tubes['some-tube'].stats # tube stats
379
+ @tuber.jobs[some_job_id].stats # job stats
380
+ ```
381
+
382
+ ## Configuration
383
+
384
+ ```ruby
385
+ Tuber.configure do |config|
386
+ config.default_put_delay = 0
387
+ config.default_put_pri = 65536
388
+ config.default_put_ttr = 120
389
+ config.job_parser = lambda { |body| body }
390
+ config.job_serializer = lambda { |body| body }
391
+ config.tuber_url = 'localhost:11300'
392
+
393
+ # Connect attempts; see "Surviving a server restart" above.
394
+ config.connect_retries = 0
395
+ config.connect_retry_interval = 1
396
+ end
397
+ ```
398
+
399
+ The `job_serializer` is applied to every `put` body — useful for automatic JSON encoding:
400
+
401
+ ```ruby
402
+ Tuber.configure do |config|
403
+ config.job_serializer = lambda { |body| JSON.dump(body) }
404
+ end
405
+ ```
406
+
407
+ ## Error Handling
408
+
409
+ | Error | Description |
410
+ | ----------------------------- | ----------- |
411
+ | Tuber::NotConnected | Cannot connect to the server. |
412
+ | Tuber::InvalidTubeName | Tube name is not valid. |
413
+ | Tuber::NotFoundError | Job or tube not found. |
414
+ | Tuber::TimedOutError | Reserve timed out. |
415
+ | Tuber::JobNotReserved | Action requires a reserved job. |
416
+
417
+ See the [Tuber protocol](https://github.com/tuberq/tuber/blob/main/docs/protocol.md) (a superset of the [beanstalk protocol](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt)) for additional error types.
418
+
419
+ ## Resources
420
+
421
+ * [Tuber](https://github.com/tuberq/tuber)
422
+ * [Tuber protocol](https://github.com/tuberq/tuber/blob/main/docs/protocol.md)
423
+ * [Tuber on RubyGems](https://rubygems.org/gems/tuber)
424
+ * [Beanstalkd](https://github.com/beanstalkd/beanstalkd) and the [beanstalk protocol](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt)
425
+ * [Backburner](https://github.com/nesquena/backburner) — Ruby job queue for Rails/Sinatra
426
+
427
+ ## Contributors
428
+
429
+ Tuber is maintained by [Dan Milne](https://github.com/dkam), and builds on the work of
430
+ everyone who wrote beaneater, from which it is forked:
431
+
432
+ - [Nico Taing](https://github.com/Nico-Taing) - Creator and co-maintainer of beaneater
433
+ - [Nathan Esquenazi](https://github.com/nesquena) - Contributor and co-maintainer
434
+ - [Keith Rarick](https://github.com/kr) - Much code inspired and adapted from beanstalk-client
435
+ - [Vidar Hokstad](https://github.com/vidarh) - Replaced telnet with correct TCP socket handling
436
+ - [Andreas Loupasakis](https://github.com/alup) - Improve test coverage, improve job configuration
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require 'rake/testtask'
5
+ require 'yard'
6
+ require 'redcarpet'
7
+
8
+ # rake test
9
+ Rake::TestTask.new do |t|
10
+ t.libs.push "lib"
11
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)] -
12
+ FileList[File.expand_path('../test/**/tuber_test.rb', __FILE__)]
13
+ t.verbose = true
14
+ end
15
+
16
+ # rake test:integration
17
+ Rake::TestTask.new("test:integration") do |t|
18
+ t.libs.push "lib"
19
+ t.test_files = FileList[File.expand_path('../test/**/tuber_test.rb', __FILE__)]
20
+ t.verbose = true
21
+ end
22
+
23
+ # rake test:full
24
+ Rake::TestTask.new("test:full") do |t|
25
+ t.libs.push "lib"
26
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
27
+ t.verbose = true
28
+ end
29
+
30
+ YARD::Rake::YardocTask.new do |t|
31
+ t.files = ['lib/tuber/**/*.rb']
32
+ t.options = []
33
+ end
34
+
35
+ task :default => 'test:full'
data/examples/demo.rb ADDED
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'term/ansicolor'
4
+ class String; include Term::ANSIColor; end
5
+ def step(msg); "\n[STEP] #{msg}...".yellow; end
6
+ $:.unshift("../lib")
7
+ require 'tuber'
8
+
9
+ # Establish a pool of beanstalks
10
+ puts step("Connecting to Beanstalk")
11
+ bc = Tuber.new('localhost')
12
+ puts bc
13
+
14
+ # Print out key stats
15
+ puts step("Print Stats")
16
+ p bc.stats.keys
17
+ p [bc.stats.total_connections, bc.stats[:total_connections], bc.stats['total_connections']]
18
+
19
+ # find tube
20
+ puts step("Find tube")
21
+ tube = bc.tubes.find('tube2')
22
+ puts tube
23
+
24
+ # Put job onto tube
25
+ puts step("Put job")
26
+ response = tube.put "foo bar", :pri => 1000, :ttr => 10, :delay => 0
27
+ puts response
28
+
29
+ # peek tube
30
+ puts step("Peek tube")
31
+ p tube.peek :ready
32
+
33
+ # watch tube
34
+ bc.tubes.watch!('tube2')
35
+
36
+ # Check tube stats
37
+ puts step("Get tube stats")
38
+ p tube.stats.keys
39
+ p tube.stats.name
40
+ p tube.stats.current_jobs_ready
41
+
42
+ # Reserve job from tube
43
+ puts step("Reserve job")
44
+ p job = bc.tubes.reserve
45
+ jid = job.id
46
+
47
+ # pause tube
48
+ puts step("Pause tube")
49
+ p tube.pause(1)
50
+
51
+ # Register jobs
52
+ puts step("Register jobs for tubes")
53
+ bc.jobs.register('tube_test', :retry_on => [Timeout::Error]) do |job|
54
+ p 'tube_test'
55
+ p job
56
+ raise Tuber::AbortProcessingError
57
+ end
58
+
59
+ bc.jobs.register('tube_test2', :retry_on => [Timeout::Error]) do |job|
60
+ p 'tube_test2'
61
+ p job
62
+ raise Tuber::AbortProcessingError
63
+ end
64
+
65
+ p bc.jobs.processors
66
+
67
+ response = bc.tubes.find('tube_test').put "foo register", :pri => 1000, :ttr => 10, :delay => 0
68
+ response = bc.tubes.find('tube_test2').put "foo baz", :pri => 1000, :ttr => 10, :delay => 0
69
+
70
+ # Process jobs
71
+ puts step("Process jobs")
72
+ 2.times { bc.jobs.process! }
73
+
74
+ # Get job from id (peek job)
75
+ puts step("Get job from id")
76
+ p bc.jobs.find(jid)
77
+ p bc.jobs.peek(jid)
78
+
79
+ # Check job stats
80
+ puts step("Get job stats")
81
+ p job.stats.keys
82
+ p job.stats.tube
83
+ p job.stats.state
84
+
85
+ # bury job
86
+ puts step("Bury job")
87
+ p job.bury
88
+
89
+ # delete job
90
+ puts step("Delete job")
91
+ p job.delete
92
+
93
+ # list tubes
94
+ puts step("List tubes")
95
+ p bc.tubes.watched
96
+ p bc.tubes.used
97
+ p bc.tubes.all
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Tuber
4
+ class Configuration
5
+ attr_accessor :default_put_delay # default delay value to put a job
6
+ attr_accessor :default_put_pri # default priority value to put a job
7
+ attr_accessor :default_put_ttr # default ttr value to put a job
8
+ attr_accessor :job_parser # default job_parser to parse job body
9
+ attr_accessor :job_serializer # default serializer for job body
10
+ attr_accessor :tuber_url # default server url
11
+ alias_method :beanstalkd_url, :tuber_url # compatibility with beaneater configs
12
+ alias_method :beanstalkd_url=, :tuber_url=
13
+ attr_accessor :connect_timeout # TCP connect timeout in seconds
14
+ attr_accessor :resolv_timeout # DNS resolve timeout in seconds
15
+ attr_accessor :read_timeout # socket read timeout in seconds
16
+ attr_accessor :write_timeout # socket write timeout in seconds
17
+
18
+ # How hard a client tries to connect — "how long should this ride out a
19
+ # server restart", in one knob.
20
+ #
21
+ # An established connection has always retried when it notices a dropped
22
+ # socket (Tuber::Connection::MAX_RETRIES attempts), but the *initial*
23
+ # connect got exactly one: a cold start during an outage — a web boot, a
24
+ # fresh Tuber.new, a daemon rebuilding its client after an error — failed
25
+ # where a held connection would have healed.
26
+ #
27
+ # Defaults to that single attempt, so nothing changes until you raise it.
28
+ # Raising it covers the initial connect and lifts the reconnect budget of
29
+ # connections already established along with it.
30
+ attr_accessor :connect_retries # extra connect attempts, 0 = one shot
31
+ attr_accessor :connect_retry_interval # seconds between connect attempts
32
+
33
+ def initialize
34
+ @default_put_delay = 0
35
+ @default_put_pri = 65536
36
+ @default_put_ttr = 120
37
+ @job_parser = lambda { |body| body }
38
+ @job_serializer = lambda { |body| body }
39
+ @tuber_url = nil
40
+ @connect_timeout = nil
41
+ @resolv_timeout = nil
42
+ @read_timeout = nil
43
+ @write_timeout = nil
44
+ @connect_retries = 0
45
+ # Matches Tuber::Connection::DEFAULT_RETRY_INTERVAL, which is not loaded yet.
46
+ @connect_retry_interval = 1
47
+ end
48
+ end # Configuration
49
+ end # Tuber