beaneater 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2177aa90f9a3a7017a9df9295ce6105f9d40e22
4
- data.tar.gz: 5f77980dcbee480728ee7723c2c67d2cd0138f25
3
+ metadata.gz: fe3f3a885f7fd1d205f508d48e2ce588bb56d2e4
4
+ data.tar.gz: 0d5578662d16fa1ad5da7f48e85f1ebf2a13d66d
5
5
  SHA512:
6
- metadata.gz: 8246d9cf93f51481b0d8cdd16129aa44fe6d47c9b0ce702735a44396c364bf5f200e75e4da84857fd9b302fe89dce64f22345e0f0e79b1242c235b73bda7026c
7
- data.tar.gz: ca040b82353afa029e60554d45747dc63a14bce3f8fd0e40a7d455e42873a1bebcd05441959a789c89f7dc615a8826164f300ae1e98bcf83e1fe0d68be9016ef
6
+ metadata.gz: 532de361e18a96f6acbbb152edc7ada7e83c2793fb494e03ea8e9f0064c6d81b20dfd296fbec26731d61a944cbdc43986492ce77f3c5e4fa30a7a3158e27a185
7
+ data.tar.gz: 1838fcddd8e169470a13dac5305ed0dec7b84a5fdd79289626f05a45862b4b7bce4e45cb82f455ea46ec0ed738e9003e6922b751c8f85f57aaa386e2e5b25c0f
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG for Beaneater
2
2
 
3
+ ## 1.0.0 (April 26th 2015)
4
+
5
+ * Beginning from version 1.0.0 the support for `Beaneater::Pool` has been dropped (@alup)
6
+ * `Jobs#find_all` method has been removed, since it is no longer necessary after removing pool (@alup)
7
+ * `Tubes` is now an enumerable allowing `tubes` to be handled as a collection (@Aethelflaed)
8
+
3
9
  ## 0.3.3 (August 16th 2014)
4
10
 
5
11
  * Fix failure when job is not defined and fix exception handling for jobs (@nicholasorenrawlings)
@@ -7,7 +13,6 @@
7
13
  * Add travis-ci badge (@tdg5)
8
14
  * Fix tests to run more reliably (@tdg5)
9
15
 
10
-
11
16
  ## 0.3.2 (Sept 15 2013)
12
17
 
13
18
  * Fix #29 ExpectedCrlfError name and invocation (@tdg5)
data/README.md CHANGED
@@ -8,6 +8,7 @@ originally designed for reducing the latency of page views in high-volume web ap
8
8
  running time-consuming tasks asynchronously. Read the [yardocs](http://rdoc.info/github/beanstalkd/beaneater) and/or the
9
9
  [beanstalk protocol](https://github.com/kr/beanstalkd/blob/master/doc/protocol.md) for more details.
10
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.
11
12
 
12
13
  ## Why Beanstalk?
13
14
 
@@ -83,13 +84,25 @@ gem 'beaneater'
83
84
 
84
85
  and run `bundle install` to install the dependency.
85
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
+
86
99
  ## Quick Overview:
87
100
 
88
101
  The concise summary of how to use beaneater:
89
102
 
90
103
  ```ruby
91
104
  # Connect to pool
92
- @beanstalk = Beaneater::Pool.new(['localhost:11300'])
105
+ @beanstalk = Beaneater.new('localhost:11300')
93
106
  # Enqueue jobs to tube
94
107
  @tube = @beanstalk.tubes["my-tube"]
95
108
  @tube.put '{ "key" : "foo" }', :pri => 5
@@ -118,7 +131,8 @@ Beaneater.configure do |config|
118
131
  # config.default_put_pri = 65536
119
132
  # config.default_put_ttr = 120
120
133
  # config.job_parser = lambda { |body| body }
121
- # config.beanstalkd_url = ['localhost:11300']
134
+ # config.job_serializer = lambda { |body| body }
135
+ # config.beanstalkd_url = 'localhost:11300'
122
136
  end
123
137
  ```
124
138
 
@@ -126,18 +140,17 @@ The above options are all defaults, so only include a configuration block if you
126
140
 
127
141
  ### Connection
128
142
 
129
- 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:
130
144
 
131
145
  ```ruby
132
- @beanstalk = Beaneater::Pool.new(['10.0.1.5:11300'])
146
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
133
147
 
134
- # Or if ENV['BEANSTALKD_URL] == 'localhost:11300,127.0.0.1:11300'
135
- @beanstalk = Beaneater::Pool.new
136
- @beanstalk.connections.first # => localhost:11300
137
- @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.connectiont # => localhost:11300
138
151
  ```
139
152
 
140
- 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:
141
154
 
142
155
  ```ruby
143
156
  @beanstalk.close
@@ -248,6 +261,16 @@ Beanstalkd can only stores strings as job bodies, but you can easily encode your
248
261
  @tube.put({:foo => 'bar'}.to_json)
249
262
  ```
250
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
+
251
274
  Each job has various metadata associated such as `priority`, `delay`, and `ttr` which can be
252
275
  specified as part of the `put` command:
253
276
 
@@ -266,7 +289,7 @@ In order to process jobs, the client should first specify the intended tubes to
266
289
  this will default to watching just the `default` tube.
267
290
 
268
291
  ```ruby
269
- @beanstalk = Beaneater::Pool.new(['10.0.1.5:11300'])
292
+ @beanstalk = Beaneater.new('10.0.1.5:11300')
270
293
  @beanstalk.tubes.watch!('tube-name', 'other-tube')
271
294
  ```
272
295
 
@@ -330,13 +353,6 @@ inspected using the 'peek' commands. To find and peek at a particular job based
330
353
  # => <Beaneater::Job id=123 body="foo">
331
354
  ```
332
355
 
333
- You can also `find_all` jobs across all connections:
334
-
335
- ```ruby
336
- @beanstalk.jobs.find_all(123)
337
- # => [<Beaneater::Job id=123 body="foo">, <Beaneater::Job id=123 body="bar">]
338
- ```
339
-
340
356
  or you can peek at jobs within a tube:
341
357
 
342
358
  ```ruby
@@ -424,8 +440,9 @@ beanstalk overall:
424
440
  ```ruby
425
441
  # Get overall stats about the job processing that has occurred
426
442
  print @beanstalk.stats
427
- # => { 'current_connections': 1, 'current_jobs_buried': 0, ... }
428
- 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
429
446
  # => 1
430
447
  ```
431
448
 
@@ -466,3 +483,4 @@ There are other resources helpful when learning about beanstalk:
466
483
  - [Nathan Esquenazi](https://github.com/nesquena) - Contributor and co-maintainer
467
484
  - [Keith Rarick](https://github.com/kr) - Much code inspired and adapted from beanstalk-client
468
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
@@ -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
@@ -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)
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
@@ -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,18 +65,19 @@ 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
72
  def transmit(command, options={})
49
- @mutex.synchronize do
50
- if connection
73
+ _with_retry(options[:retry_interval], options[:init]) do
74
+ @mutex.synchronize do
75
+ _raise_not_connected! unless connection
76
+
51
77
  command = command.force_encoding('ASCII-8BIT') if command.respond_to?(:force_encoding)
52
78
  connection.write(command.to_s + "\r\n")
53
- res = connection.gets
54
- raise_not_connected! unless res
79
+ res = connection.readline
55
80
  parse_response(command, res)
56
- else # no connection
57
- raise_not_connected!
58
81
  end
59
82
  end
60
83
  end
@@ -65,8 +88,10 @@ module Beaneater
65
88
  # @conn.close
66
89
  #
67
90
  def close
68
- @connection.close
69
- @connection = nil
91
+ if @connection
92
+ @connection.close
93
+ @connection = nil
94
+ end
70
95
  end
71
96
 
72
97
  # Returns string representation of job.
@@ -79,6 +104,15 @@ module Beaneater
79
104
  end
80
105
  alias :inspect :to_s
81
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
+
82
116
  protected
83
117
 
84
118
  # Establish a connection based on beanstalk address.
@@ -89,13 +123,11 @@ module Beaneater
89
123
  # establish_connection('localhost:3005')
90
124
  #
91
125
  def establish_connection
92
- @match = address.split(':')
93
- @host, @port = @match[0], Integer(@match[1] || DEFAULT_PORT)
94
- TCPSocket.new @host, @port
95
- rescue Errno::ECONNREFUSED => e
96
- raise NotConnected, "Could not connect to '#{@host}:#{@port}'"
97
- rescue Exception => ex
98
- 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
99
131
  end
100
132
 
101
133
  # Parses the response and returns the useful beanstalk response.
@@ -103,11 +135,11 @@ module Beaneater
103
135
  #
104
136
  # @param [String] cmd Beanstalk command transmitted
105
137
  # @param [String] res Telnet command response
106
- # @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`
107
139
  # @raise [Beaneater::UnexpectedResponse] Response from beanstalk command was an error status
108
140
  # @example
109
141
  # parse_response("delete 56", "DELETED 56\nFOO")
110
- # # => { :body => "FOO", :status => "DELETED", :id => 56, :connection => <Connection> }
142
+ # # => { :body => "FOO", :status => "DELETED", :id => 56 }
111
143
  #
112
144
  def parse_response(cmd, res)
113
145
  status = res.chomp
@@ -123,9 +155,9 @@ module Beaneater
123
155
  raise ExpectedCrlfError.new('EXPECTED_CRLF', cmd) if crlf != "\r\n"
124
156
  end
125
157
  id = body_values[1]
126
- response = { :status => status, :body => body }
158
+ response = { :status => status }
127
159
  response[:id] = id if id
128
- response[:connection] = self
160
+ response[:body] = body if body
129
161
  response
130
162
  end
131
163
 
@@ -136,10 +168,75 @@ module Beaneater
136
168
  Beaneater.configuration
137
169
  end
138
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
+ end
179
+
180
+ transmit("use #{tube_used}", init: false) if @tube_used != 'default'
181
+ end
182
+
183
+ # Wrapper method for capturing certain failures and retry the payload block
184
+ #
185
+ # @param [Proc] block The command to execute.
186
+ # @param [Integer] retry_interval The time to wait before the next retry
187
+ # @param [Integer] tries The maximum number of tries in draining mode
188
+ # @return [Object] Result of the block passed
189
+ #
190
+ def _with_retry(retry_interval, init=true, tries=MAX_RETRIES, &block)
191
+ yield
192
+ rescue EOFError, Errno::ECONNRESET, Errno::EPIPE,
193
+ Errno::ECONNREFUSED => ex
194
+ _reconnect(ex, retry_interval)
195
+ _initialize_tubes if init
196
+ retry
197
+ rescue Beaneater::DrainingError
198
+ tries -= 1
199
+ if tries.zero?
200
+ close
201
+ raise
202
+ end
203
+ sleep(retry_interval || DEFAULT_RETRY_INTERVAL)
204
+ retry
205
+ end
206
+
207
+ # Tries to re-establish connection to the beanstalkd
208
+ #
209
+ # @param [Exception] original_exception The exception caused the retry
210
+ # @param [Integer] retry_interval The time to wait before the next reconnect
211
+ # @param [Integer] tries The maximum number of attempts to reconnect
212
+ def _reconnect(original_exception, retry_interval, tries=MAX_RETRIES)
213
+ close
214
+ establish_connection
215
+ rescue Errno::ECONNREFUSED
216
+ tries -= 1
217
+ if tries.zero?
218
+ _raise_not_connected!
219
+ end
220
+ sleep(retry_interval || DEFAULT_RETRY_INTERVAL)
221
+ retry
222
+ end
223
+
224
+ # The host provided by BEANSTALKD_URL environment variable, if available.
225
+ #
226
+ # @return [String] A beanstalkd host address
227
+ # @example
228
+ # ENV['BEANSTALKD_URL'] = "localhost:1212"
229
+ # # => 'localhost:1212'
230
+ #
231
+ def _host_from_env
232
+ ENV['BEANSTALKD_URL'].respond_to?(:length) && ENV['BEANSTALKD_URL'].length > 0 && ENV['BEANSTALKD_URL'].strip
233
+ end
234
+
139
235
  # Raises an error to be triggered when the connection has failed
140
236
  # @raise [Beaneater::NotConnected] Beanstalkd is no longer connected
141
- def raise_not_connected!
142
- raise NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!"
237
+ def _raise_not_connected!
238
+ raise Beaneater::NotConnected, "Connection to beanstalk '#{@host}:#{@port}' is closed!"
143
239
  end
240
+
144
241
  end # Connection
145
242
  end # Beaneater