beaneater 0.3.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 83a6a5bc5d6a0e34828c5b011ca65b3bb99a296ab0d038b348279fd7b5bbf842
4
+ data.tar.gz: 30e307f3cb14d2d078cfc2eb23219add703535b2a2c9e8212b60dd54eb808bd9
5
+ SHA512:
6
+ metadata.gz: 820d14df0a37f2d3afd63db394a8d971a0e98436331a67b989e4c58335f1d3803f79b233b261c096069c511cba5ff77d7bf0eb887f14a9f61b39251d9ac8ea12
7
+ data.tar.gz: ed53e2479c36ce9e2af27f3e63a00c3540e935fcc822414649ae2135daa93d59cc14154f71c70e6d02a9f53a42d11b360fe01808350915a85b91abd8ffc95056
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5
4
+ - 2.6
5
+ - 2.7
6
+ before_install:
7
+ - curl -L https://github.com/kr/beanstalkd/archive/v1.9.tar.gz | tar xz -C /tmp
8
+ - cd /tmp/beanstalkd-1.9/
9
+ - make
10
+ - ./beanstalkd &
11
+ - cd $TRAVIS_BUILD_DIR
12
+ script:
13
+ - bundle install
14
+ - bundle exec rake test:full
data/CHANGELOG.md CHANGED
@@ -1,6 +1,36 @@
1
1
  # CHANGELOG for Beaneater
2
2
 
3
- ## 0.3.1 (Unreleased)
3
+ ## 1.1.0 (April 25th 2021)
4
+
5
+ * 'clear' behavior was failing unexpectedly, swallow issues during delete as well (@bfolkens)
6
+ * Fix assigned but unused variables (@utilum)
7
+ * Fix last_used tube not stored (@albb0920)
8
+ * Fix deprecation warning in ruby 2.7 (@albb0920)
9
+ * Fix watched tubes not restored after reconnect (@albb0920)
10
+ * Fix keyword arguemnt warning (@albb0920)
11
+
12
+ ## 1.0.0 (April 26th 2015)
13
+
14
+ * Beginning from version 1.0.0 the support for `Beaneater::Pool` has been dropped (@alup)
15
+ * `Jobs#find_all` method has been removed, since it is no longer necessary after removing pool (@alup)
16
+ * `Tubes` is now an enumerable allowing `tubes` to be handled as a collection (@Aethelflaed)
17
+
18
+ ## 0.3.3 (August 16th 2014)
19
+
20
+ * Fix failure when job is not defined and fix exception handling for jobs (@nicholasorenrawlings)
21
+ * Add reserve_timeout option to job processing (@nicholasorenrawlings)
22
+ * Add travis-ci badge (@tdg5)
23
+ * Fix tests to run more reliably (@tdg5)
24
+
25
+ ## 0.3.2 (Sept 15 2013)
26
+
27
+ * Fix #29 ExpectedCrlfError name and invocation (@tdg5)
28
+
29
+ ## 0.3.1 (Jun 28 2013)
30
+
31
+ * Fixes issue with "chomp" nil exception when losing connection (Thanks @simao)
32
+ * Better handling of unknown or invalid commands during transmit
33
+ * Raise proper CRLF exception (Thanks @carlosmoutinho)
4
34
 
5
35
  ## 0.3.0 (Jan 23 2013)
6
36
 
data/Gemfile CHANGED
@@ -7,4 +7,8 @@ group :development do
7
7
  gem 'redcarpet', '~> 1'
8
8
  gem 'github-markup'
9
9
  gem 'yard'
10
- end
10
+ end
11
+
12
+ group :development, :test do
13
+ gem 'coveralls', :require => false
14
+ end
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # Beaneater
2
+ [![Build Status](https://secure.travis-ci.org/beanstalkd/beaneater.png)](http://travis-ci.org/beanstalkd/beaneater)
3
+ [![Coverage Status](https://coveralls.io/repos/beanstalkd/beaneater/badge.png?branch=master)](https://coveralls.io/r/beanstalkd/beaneater)
2
4
 
3
5
  Beaneater is the best way to interact with beanstalkd from within Ruby.
4
6
  [Beanstalkd](http://kr.github.com/beanstalkd/) is a simple, fast work queue. Its interface is generic, but was
5
7
  originally designed for reducing the latency of page views in high-volume web applications by
6
8
  running time-consuming tasks asynchronously. Read the [yardocs](http://rdoc.info/github/beanstalkd/beaneater) and/or the
7
- [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for more details.
9
+ [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for more details.
8
10
 
11
+ **Important Note**: This README is **for branch 1.0.x which is under development**. Please switch to latest `0.x` branch for stable version.
9
12
 
10
13
  ## Why Beanstalk?
11
14
 
@@ -81,13 +84,25 @@ gem 'beaneater'
81
84
 
82
85
  and run `bundle install` to install the dependency.
83
86
 
87
+ ## Breaking Changes since 1.0!
88
+
89
+ Starting in 1.0, we removed the concept of the `Beaneater::Pool` which introduced considerable complexity into this gem.
90
+
91
+ * Beginning from version 1.0.0 the support for `Beaneater::Pool` has been dropped.
92
+ The specific feature may be supported again in the next versions as separate module
93
+ or through a separate gem. If you want to use the pool feature you should switch to
94
+ 0.x stable branches instead.
95
+ * `Jobs#find_all` method has been removed, since it is no longer necessary.
96
+
97
+ To manage a pool of beanstalkd instances, we'd prefer to leave the handling to the developer or other higher-level libraries.
98
+
84
99
  ## Quick Overview:
85
100
 
86
101
  The concise summary of how to use beaneater:
87
102
 
88
103
  ```ruby
89
104
  # Connect to pool
90
- @beanstalk = Beaneater::Pool.new(['localhost:11300'])
105
+ @beanstalk = Beaneater.new('localhost:11300')
91
106
  # Enqueue jobs to tube
92
107
  @tube = @beanstalk.tubes["my-tube"]
93
108
  @tube.put '{ "key" : "foo" }', :pri => 5
@@ -95,7 +110,7 @@ The concise summary of how to use beaneater:
95
110
  # Process jobs from tube
96
111
  while @tube.peek(:ready)
97
112
  job = @tube.reserve
98
- puts "job value is #{job.body["key"]}!"
113
+ puts "job value is #{JSON.parse(job.body)["key"]}!"
99
114
  job.delete
100
115
  end
101
116
  # Disconnect the pool
@@ -116,7 +131,8 @@ Beaneater.configure do |config|
116
131
  # config.default_put_pri = 65536
117
132
  # config.default_put_ttr = 120
118
133
  # config.job_parser = lambda { |body| body }
119
- # config.beanstalkd_url = ['localhost:11300']
134
+ # config.job_serializer = lambda { |body| body }
135
+ # config.beanstalkd_url = 'localhost:11300'
120
136
  end
121
137
  ```
122
138
 
@@ -124,18 +140,17 @@ The above options are all defaults, so only include a configuration block if you
124
140
 
125
141
  ### Connection
126
142
 
127
- To interact with a beanstalk queue, first establish a connection by providing a set of addresses:
143
+ To interact with a beanstalk queue, first establish a connection by providing an address:
128
144
 
129
145
  ```ruby
130
- @beanstalk = Beaneater::Pool.new(['10.0.1.5:11300'])
146
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
131
147
 
132
- # Or if ENV['BEANSTALKD_URL] == 'localhost:11300,127.0.0.1:11300'
133
- @beanstalk = Beaneater::Pool.new
134
- @beanstalk.connections.first # => localhost:11300
135
- @beanstalk.connections.last # => 127.0.0.1:11300
148
+ # Or if ENV['BEANSTALKD_URL'] == '127.0.0.1:11300'
149
+ @beanstalk = Beaneater.new
150
+ @beanstalk.connection # => localhost:11300
136
151
  ```
137
152
 
138
- You can conversely close and dispose of a pool at any time with:
153
+ You can conversely close and dispose of a connection at any time with:
139
154
 
140
155
  ```ruby
141
156
  @beanstalk.close
@@ -246,6 +261,16 @@ Beanstalkd can only stores strings as job bodies, but you can easily encode your
246
261
  @tube.put({:foo => 'bar'}.to_json)
247
262
  ```
248
263
 
264
+ Moreover, you can provide a default job serializer by setting the corresponding configuration
265
+ option (`job_serializer`), in order to apply the encoding on each job body which
266
+ is going to be send using the `put` command. For example, to encode a ruby object to JSON format:
267
+
268
+ ```ruby
269
+ Beaneater.configure do |config|
270
+ config.job_serializer = lambda { |body| JSON.dump(body) }
271
+ end
272
+ ```
273
+
249
274
  Each job has various metadata associated such as `priority`, `delay`, and `ttr` which can be
250
275
  specified as part of the `put` command:
251
276
 
@@ -264,7 +289,7 @@ In order to process jobs, the client should first specify the intended tubes to
264
289
  this will default to watching just the `default` tube.
265
290
 
266
291
  ```ruby
267
- @beanstalk = Beaneater::Connection.new(['10.0.1.5:11300'])
292
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
268
293
  @beanstalk.tubes.watch!('tube-name', 'other-tube')
269
294
  ```
270
295
 
@@ -328,13 +353,6 @@ inspected using the 'peek' commands. To find and peek at a particular job based
328
353
  # => <Beaneater::Job id=123 body="foo">
329
354
  ```
330
355
 
331
- You can also `find_all` jobs across all connections:
332
-
333
- ```ruby
334
- @beanstalk.jobs.find_all(123)
335
- # => [<Beaneater::Job id=123 body="foo">, <Beaneater::Job id=123 body="bar">]
336
- ```
337
-
338
356
  or you can peek at jobs within a tube:
339
357
 
340
358
  ```ruby
@@ -390,7 +408,9 @@ Processing runs the following steps:
390
408
  1. If other exception occurs, call 'bury' (error)
391
409
  1. Repeat steps 2-5
392
410
 
393
- The `process` command is ideally suited for a beanstalk job processing daemon.
411
+ The `process!` command is ideally suited for a beanstalk job processing daemon.
412
+ Even though `process!` is intended to be a long-running process, you can stop the loop at any time
413
+ by raising `AbortProcessingError` while processing is running.
394
414
 
395
415
  ### Handling Errors
396
416
 
@@ -409,7 +429,7 @@ are listed below:
409
429
  There are other exceptions that are less common such as `OutOfMemoryError`, `DrainingError`,
410
430
  `DeadlineSoonError`, `InternalError`, `BadFormatError`, `UnknownCommandError`,
411
431
  `ExpectedCRLFError`, `JobTooBigError`, `NotIgnoredError`. Be sure to check the
412
- [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for more information.
432
+ [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for more information.
413
433
 
414
434
 
415
435
  ### Stats
@@ -420,8 +440,9 @@ beanstalk overall:
420
440
  ```ruby
421
441
  # Get overall stats about the job processing that has occurred
422
442
  print @beanstalk.stats
423
- # => { 'current_connections': 1, 'current_jobs_buried': 0, ... }
424
- print @beanstalk.stats.current_connections
443
+ # => #<Beaneater::StatStruct current_jobs_urgent=0, current_jobs_ready=0, current_jobs_reserved=0, current_jobs_delayed=0, current_jobs_buried=0, ...
444
+
445
+ print @beanstalk.stats.current_tubes
425
446
  # => 1
426
447
  ```
427
448
 
@@ -441,7 +462,7 @@ print @beanstalk.jobs[some_job_id].stats
441
462
  # => {'age': 0, 'id': 2, 'state': 'reserved', 'tube': 'default', ... }
442
463
  ```
443
464
 
444
- Be sure to check the [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for
465
+ Be sure to check the [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt) for
445
466
  more details about the stats commands.
446
467
 
447
468
  ## Resources
@@ -452,11 +473,14 @@ There are other resources helpful when learning about beanstalk:
452
473
  * [Beaneater on Rubygems](https://rubygems.org/gems/beaneater)
453
474
  * [Beanstalkd homepage](http://kr.github.com/beanstalkd/)
454
475
  * [beanstalk on github](https://github.com/kr/beanstalkd)
455
- * [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md)
476
+ * [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt)
456
477
  * [Backburner](https://github.com/nesquena/backburner) - Ruby job queue for Rails/Sinatra
478
+ * [BeanCounter](https://github.com/gemeraldbeanstalk/bean_counter) - TestUnit/MiniTest assertions and RSpec matchers for testing code that relies on Beaneater
457
479
 
458
480
  ## Contributors
459
481
 
460
482
  - [Nico Taing](https://github.com/Nico-Taing) - Creator and co-maintainer
461
483
  - [Nathan Esquenazi](https://github.com/nesquena) - Contributor and co-maintainer
462
- - [Keith Rarick](https://github.com/kr) - Much code inspired and adapted from beanstalk-client
484
+ - [Keith Rarick](https://github.com/kr) - Much code inspired and adapted from beanstalk-client
485
+ - [Vidar Hokstad](https://github.com/vidarh) - Replaced telnet with correct TCP socket handling
486
+ - [Andreas Loupasakis](https://github.com/alup) - Improve test coverage, improve job configuration
data/Rakefile CHANGED
@@ -28,4 +28,6 @@ end
28
28
  YARD::Rake::YardocTask.new do |t|
29
29
  t.files = ['lib/beaneater/**/*.rb']
30
30
  t.options = []
31
- end
31
+ end
32
+
33
+ task :default => 'test:full'
data/beaneater.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
11
11
  gem.description = %q{Simple beanstalkd client for ruby}
12
12
  gem.summary = %q{Simple beanstalkd client for ruby.}
13
13
  gem.homepage = ""
14
+ gem.license = 'MIT'
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
16
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -19,7 +20,6 @@ Gem::Specification.new do |gem|
19
20
  gem.add_development_dependency 'minitest', "~> 4.1.0"
20
21
  gem.add_development_dependency 'rake'
21
22
  gem.add_development_dependency 'mocha'
22
- gem.add_development_dependency 'fakeweb'
23
23
  gem.add_development_dependency 'term-ansicolor'
24
24
  gem.add_development_dependency 'json'
25
25
  end
data/examples/demo.rb CHANGED
@@ -6,8 +6,7 @@ require 'beaneater'
6
6
 
7
7
  # Establish a pool of beanstalks
8
8
  puts step("Connecting to Beanstalk")
9
- bc = Beaneater::Pool.new('localhost')
10
- # bc = Beaneater::Pool.new(['localhost', 'localhost:11301', 'localhost:11302'])
9
+ bc = Beaneater.new('localhost')
11
10
  puts bc
12
11
 
13
12
  # Print out key stats
data/lib/beaneater.rb CHANGED
@@ -1,11 +1,64 @@
1
1
  require 'thread' unless defined?(Mutex)
2
2
 
3
- %w(version configuration errors pool_command pool connection stats tube job).each do |f|
3
+ %w(version configuration errors connection tube job stats).each do |f|
4
4
  require "beaneater/#{f}"
5
5
  end
6
6
 
7
- module Beaneater
8
- # Simple ruby client for beanstalkd.
7
+ class Beaneater
8
+
9
+ # @!attribute connection
10
+ # @return <Beaneater::Connection> returns the associated connection object
11
+ attr_reader :connection
12
+
13
+ # Initialize new instance of Beaneater
14
+ #
15
+ # @param [String] address in the form "host:port"
16
+ # @example
17
+ # Beaneater.new('127.0.0.1:11300')
18
+ #
19
+ # ENV['BEANSTALKD_URL'] = '127.0.0.1:11300'
20
+ # @b = Beaneater.new
21
+ # @b.connection.host # => '127.0.0.1'
22
+ # @b.connection.port # => '11300'
23
+ #
24
+ def initialize(address=nil)
25
+ @connection = Connection.new(address)
26
+ end
27
+
28
+ # Returns Beaneater::Tubes object for accessing tube related functions.
29
+ #
30
+ # @return [Beaneater::Tubes] tubes object
31
+ # @api public
32
+ def tubes
33
+ @tubes ||= Beaneater::Tubes.new(self)
34
+ end
35
+
36
+ # Returns Beaneater::Jobs object for accessing job related functions.
37
+ #
38
+ # @return [Beaneater::Jobs] jobs object
39
+ # @api public
40
+ def jobs
41
+ @jobs ||= Beaneater::Jobs.new(self)
42
+ end
43
+
44
+ # Returns Beaneater::Stats object for accessing beanstalk stats.
45
+ #
46
+ # @return [Beaneater::Stats] stats object
47
+ # @api public
48
+ def stats
49
+ @stats ||= Stats.new(self)
50
+ end
51
+
52
+ # Closes the related connection
53
+ #
54
+ # @example
55
+ # @beaneater_instance.close
56
+ #
57
+ def close
58
+ connection.close if connection
59
+ end
60
+
61
+ protected
9
62
 
10
63
  class << self
11
64
  # Yields a configuration block
@@ -29,4 +82,4 @@ module Beaneater
29
82
  @_configuration ||= Configuration.new
30
83
  end
31
84
  end
32
- end # Beaneater
85
+ end # Beaneater
@@ -1,9 +1,10 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  class Configuration
3
3
  attr_accessor :default_put_delay # default delay value to put a job
4
4
  attr_accessor :default_put_pri # default priority value to put a job
5
5
  attr_accessor :default_put_ttr # default ttr value to put a job
6
6
  attr_accessor :job_parser # default job_parser to parse job body
7
+ attr_accessor :job_serializer # default serializer for job body
7
8
  attr_accessor :beanstalkd_url # default beanstalkd url
8
9
 
9
10
  def initialize
@@ -11,6 +12,7 @@ module Beaneater
11
12
  @default_put_pri = 65536
12
13
  @default_put_ttr = 120
13
14
  @job_parser = lambda { |body| body }
15
+ @job_serializer = lambda { |body| body }
14
16
  @beanstalkd_url = nil
15
17
  end
16
18
  end # Configuration
@@ -1,10 +1,16 @@
1
1
  require 'yaml'
2
2
  require 'socket'
3
3
 
4
- module Beaneater
4
+ class Beaneater
5
5
  # Represents a connection to a beanstalkd instance.
6
6
  class Connection
7
7
 
8
+ # Default number of retries to send a command to a connection
9
+ MAX_RETRIES = 3
10
+
11
+ # Default retry interval
12
+ DEFAULT_RETRY_INTERVAL = 1
13
+
8
14
  # @!attribute address
9
15
  # @return [String] returns Beanstalkd server address
10
16
  # @example
@@ -21,6 +27,12 @@ module Beaneater
21
27
  # @return [Net::TCPSocket] returns connection object
22
28
  attr_reader :address, :host, :port, :connection
23
29
 
30
+ # @!attribute tubes_watched
31
+ # @returns [Array<String>] returns currently watched tube names
32
+ # @!attribute tube_used
33
+ # @returns [String] returns currently used tube name
34
+ attr_accessor :tubes_watched, :tube_used
35
+
24
36
  # Default port value for beanstalk connection
25
37
  DEFAULT_PORT = 11300
26
38
 
@@ -28,13 +40,23 @@ module Beaneater
28
40
  #
29
41
  # @param [String] address beanstalkd instance address.
30
42
  # @example
31
- # Beaneater::Connection.new('localhost')
32
- # Beaneater::Connection.new('localhost:11300')
43
+ # Beaneater::Connection.new('127.0.0.1')
44
+ # Beaneater::Connection.new('127.0.0.1:11300')
45
+ #
46
+ # ENV['BEANSTALKD_URL'] = '127.0.0.1:11300'
47
+ # @b = Beaneater.new
48
+ # @b.connection.host # => '127.0.0.1'
49
+ # @b.connection.port # => '11300'
33
50
  #
34
51
  def initialize(address)
35
- @address = address
36
- @connection = establish_connection
52
+ @address = address || _host_from_env || Beaneater.configuration.beanstalkd_url
37
53
  @mutex = Mutex.new
54
+ @tube_used = 'default'
55
+ @tubes_watched = ['default']
56
+
57
+ establish_connection
58
+ rescue
59
+ _raise_not_connected!
38
60
  end
39
61
 
40
62
  # Send commands to beanstalkd server via connection.
@@ -43,19 +65,21 @@ module Beaneater
43
65
  # @param [String] command Beanstalkd command
44
66
  # @return [Array<Hash{String => String, Number}>] Beanstalkd command response
45
67
  # @example
68
+ # @conn = Beaneater::Connection.new
46
69
  # @conn.transmit('bury 123')
70
+ # @conn.transmit('stats')
47
71
  #
48
- def transmit(command, options={})
49
- @mutex.lock
50
- if connection
51
- command = command.force_encoding('ASCII-8BIT') if command.respond_to?(:force_encoding)
52
- connection.write(command+"\r\n")
53
- parse_response(command, connection.gets)
54
- else # no connection
55
- raise NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!" unless connection
72
+ def transmit(command, **options)
73
+ _with_retry(**options.slice(:retry_interval, :init)) do
74
+ @mutex.synchronize do
75
+ _raise_not_connected! unless connection
76
+
77
+ command = command.force_encoding('ASCII-8BIT') if command.respond_to?(:force_encoding)
78
+ connection.write(command.to_s + "\r\n")
79
+ res = connection.readline
80
+ parse_response(command, res)
81
+ end
56
82
  end
57
- ensure
58
- @mutex.unlock
59
83
  end
60
84
 
61
85
  # Close connection with beanstalkd server.
@@ -64,8 +88,10 @@ module Beaneater
64
88
  # @conn.close
65
89
  #
66
90
  def close
67
- @connection.close
68
- @connection = nil
91
+ if @connection
92
+ @connection.close
93
+ @connection = nil
94
+ end
69
95
  end
70
96
 
71
97
  # Returns string representation of job.
@@ -78,23 +104,30 @@ module Beaneater
78
104
  end
79
105
  alias :inspect :to_s
80
106
 
107
+ def add_to_watched(tube_name)
108
+ @tubes_watched << tube_name
109
+ @tubes_watched.uniq
110
+ end
111
+
112
+ def remove_from_watched(tube_name)
113
+ @tubes_watched.delete(tube_name)
114
+ end
115
+
81
116
  protected
82
117
 
83
118
  # Establish a connection based on beanstalk address.
84
119
  #
85
120
  # @return [Net::TCPSocket] connection for specified address.
86
- # @raise [Beanstalk::NotConnected] Could not connect to specified beanstalkd instance.
121
+ # @raise [Beaneater::NotConnected] Could not connect to specified beanstalkd instance.
87
122
  # @example
88
123
  # establish_connection('localhost:3005')
89
124
  #
90
125
  def establish_connection
91
- @match = address.split(':')
92
- @host, @port = @match[0], Integer(@match[1] || DEFAULT_PORT)
93
- TCPSocket.new @host, @port
94
- rescue Errno::ECONNREFUSED => e
95
- raise NotConnected, "Could not connect to '#{@host}:#{@port}'"
96
- rescue Exception => ex
97
- raise NotConnected, "#{ex.class}: #{ex}"
126
+ @address = address.first if address.is_a?(Array)
127
+ match = address.split(':')
128
+ @host, @port = match[0], Integer(match[1] || DEFAULT_PORT)
129
+
130
+ @connection = TCPSocket.new @host, @port
98
131
  end
99
132
 
100
133
  # Parses the response and returns the useful beanstalk response.
@@ -102,11 +135,11 @@ module Beaneater
102
135
  #
103
136
  # @param [String] cmd Beanstalk command transmitted
104
137
  # @param [String] res Telnet command response
105
- # @return [Array<Hash{String => String, Number}>] Beanstalk response with `status`, `id`, `body`, and `connection`
138
+ # @return [Array<Hash{String => String, Number}>] Beanstalk response with `status`, `id`, `body`
106
139
  # @raise [Beaneater::UnexpectedResponse] Response from beanstalk command was an error status
107
140
  # @example
108
141
  # parse_response("delete 56", "DELETED 56\nFOO")
109
- # # => { :body => "FOO", :status => "DELETED", :id => 56, :connection => <Connection> }
142
+ # # => { :body => "FOO", :status => "DELETED", :id => 56 }
110
143
  #
111
144
  def parse_response(cmd, res)
112
145
  status = res.chomp
@@ -119,12 +152,12 @@ module Beaneater
119
152
  raw_body = connection.read(bytes_size)
120
153
  body = status == 'OK' ? YAML.load(raw_body) : config.job_parser.call(raw_body)
121
154
  crlf = connection.read(2) # \r\n
122
- raise ExpectedCRLFError if crlf != "\r\n"
155
+ raise ExpectedCrlfError.new('EXPECTED_CRLF', cmd) if crlf != "\r\n"
123
156
  end
124
157
  id = body_values[1]
125
- response = { :status => status, :body => body }
158
+ response = { :status => status }
126
159
  response[:id] = id if id
127
- response[:connection] = self
160
+ response[:body] = body if body
128
161
  response
129
162
  end
130
163
 
@@ -135,5 +168,77 @@ module Beaneater
135
168
  Beaneater.configuration
136
169
  end
137
170
 
171
+ private
172
+
173
+ def _initialize_tubes
174
+ if @tubes_watched != ['default']
175
+ tubes_watched.each do |t|
176
+ transmit("watch #{t}", init: false)
177
+ end
178
+
179
+ transmit("ignore default", init: false)
180
+ end
181
+
182
+ transmit("use #{tube_used}", init: false) if @tube_used != 'default'
183
+ end
184
+
185
+ # Wrapper method for capturing certain failures and retry the payload block
186
+ #
187
+ # @param [Proc] block The command to execute.
188
+ # @param [Integer] retry_interval The time to wait before the next retry
189
+ # @param [Integer] tries The maximum number of tries in draining mode
190
+ # @return [Object] Result of the block passed
191
+ #
192
+ def _with_retry(retry_interval: DEFAULT_RETRY_INTERVAL, init: true, tries: MAX_RETRIES, &block)
193
+ yield
194
+ rescue EOFError, Errno::ECONNRESET, Errno::EPIPE,
195
+ Errno::ECONNREFUSED => ex
196
+ _reconnect(ex, retry_interval)
197
+ _initialize_tubes if init
198
+ retry
199
+ rescue Beaneater::DrainingError
200
+ tries -= 1
201
+ if tries.zero?
202
+ close
203
+ raise
204
+ end
205
+ sleep(retry_interval)
206
+ retry
207
+ end
208
+
209
+ # Tries to re-establish connection to the beanstalkd
210
+ #
211
+ # @param [Exception] original_exception The exception caused the retry
212
+ # @param [Integer] retry_interval The time to wait before the next reconnect
213
+ # @param [Integer] tries The maximum number of attempts to reconnect
214
+ def _reconnect(original_exception, retry_interval, tries=MAX_RETRIES)
215
+ close
216
+ establish_connection
217
+ rescue Errno::ECONNREFUSED
218
+ tries -= 1
219
+ if tries.zero?
220
+ _raise_not_connected!
221
+ end
222
+ sleep(retry_interval || DEFAULT_RETRY_INTERVAL)
223
+ retry
224
+ end
225
+
226
+ # The host provided by BEANSTALKD_URL environment variable, if available.
227
+ #
228
+ # @return [String] A beanstalkd host address
229
+ # @example
230
+ # ENV['BEANSTALKD_URL'] = "localhost:1212"
231
+ # # => 'localhost:1212'
232
+ #
233
+ def _host_from_env
234
+ ENV['BEANSTALKD_URL'].respond_to?(:length) && ENV['BEANSTALKD_URL'].length > 0 && ENV['BEANSTALKD_URL'].strip
235
+ end
236
+
237
+ # Raises an error to be triggered when the connection has failed
238
+ # @raise [Beaneater::NotConnected] Beanstalkd is no longer connected
239
+ def _raise_not_connected!
240
+ raise Beaneater::NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!"
241
+ end
242
+
138
243
  end # Connection
139
244
  end # Beaneater