beaneater 0.3.3 → 1.0.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.
@@ -1,4 +1,4 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Raises when a beanstalkd instance is no longer accessible.
3
3
  class NotConnected < RuntimeError; end
4
4
  # Raises when the tube name specified is invalid.
@@ -1,14 +1,18 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Exception to stop processing jobs during a `process!` loop.
3
3
  # Simply `raise AbortProcessingError` in any job process handler to stop the processing loop.
4
4
  class AbortProcessingError < RuntimeError; end
5
5
 
6
6
  # Represents collection of job-related commands.
7
- class Jobs < PoolCommand
7
+ class Jobs
8
8
 
9
9
  # @!attribute processors
10
10
  # @return [Array<Proc>] returns Collection of proc to handle beanstalkd jobs
11
- attr_reader :processors
11
+ # @!attribute client
12
+ # @return [Beaneater] returns the client instance
13
+ # @!attribute current_job
14
+ # @return [Beaneater] returns the currently processing job in the process loop
15
+ attr_reader :processors, :client, :current_job
12
16
 
13
17
  # Number of retries to process a job.
14
18
  MAX_RETRIES = 3
@@ -19,39 +23,41 @@ module Beaneater
19
23
  # Number of seconds to wait for a job before checking a different server.
20
24
  RESERVE_TIMEOUT = nil
21
25
 
22
- # Peek (or find) first job from beanstalkd pool.
26
+ # Creates new jobs instance.
23
27
  #
24
- # @param [Integer] id Job id to find
25
- # @return [Beaneater::Job] Job matching given id
28
+ # @param [Beaneater] client The beaneater client instance.
26
29
  # @example
27
- # @beaneater_pool.jobs[123] # => <Beaneater::Job>
28
- # @beaneater_pool.jobs.find(123) # => <Beaneater::Job>
29
- # @beaneater_pool.jobs.peek(123) # => <Beaneater::Job>
30
+ # Beaneater::Jobs.new(@client)
30
31
  #
31
- # @api public
32
- def find(id)
33
- res = transmit_until_res("peek #{id}", :status => "FOUND")
34
- Job.new(res)
35
- rescue Beaneater::NotFoundError => ex
36
- nil
32
+ def initialize(client)
33
+ @client = client
34
+ end
35
+
36
+ # Delegates transmit to the connection object.
37
+ #
38
+ # @see Beaneater::Connection#transmit
39
+ def transmit(command, options={})
40
+ client.connection.transmit(command, options)
37
41
  end
38
- alias_method :peek, :find
39
- alias_method :[], :find
40
42
 
41
- # Find all jobs with specified id fromm all beanstalkd servers in pool.
43
+ # Peek (or find) job by id from beanstalkd.
42
44
  #
43
45
  # @param [Integer] id Job id to find
44
- # @return [Array<Beaneater::Job>] Jobs matching given id
46
+ # @return [Beaneater::Job] Job matching given id
45
47
  # @example
46
- # @beaneater_pool.jobs.find_all(123) # => [<Beaneater::Job>, <Beaneater::Job>]
48
+ # @beaneater.jobs[123] # => <Beaneater::Job>
49
+ # @beaneater.jobs.find(123) # => <Beaneater::Job>
50
+ # @beaneater.jobs.peek(123) # => <Beaneater::Job>
47
51
  #
48
52
  # @api public
49
- def find_all(id)
50
- res = transmit_to_all("peek #{id}")
51
- res.compact.map { |r| Job.new(r) }
52
- rescue Beaneater::NotFoundError => ex
53
- []
53
+ def find(id)
54
+ res = transmit("peek #{id}")
55
+ Job.new(client, res)
56
+ rescue Beaneater::NotFoundError
57
+ nil
54
58
  end
59
+ alias_method :peek, :find
60
+ alias_method :[], :find
55
61
 
56
62
  # Register a processor to handle beanstalkd job on particular tube.
57
63
  #
@@ -78,6 +84,18 @@ module Beaneater
78
84
  @processors[tube_name.to_s] = { :block => block, :retry_on => retry_on, :max_retries => max_retries }
79
85
  end
80
86
 
87
+ # Sets flag to indicate that process loop should stop after current job
88
+ def stop!
89
+ @stop = true
90
+ end
91
+
92
+ # Returns whether the process loop should stop
93
+ #
94
+ # @return [Boolean] if true the loop should stop after current processing
95
+ def stop?
96
+ !!@stop
97
+ end
98
+
81
99
  # Watch, reserve, process and delete or bury or release jobs.
82
100
  #
83
101
  # @param [Hash{String => Integer}] options Settings for processing
@@ -88,27 +106,30 @@ module Beaneater
88
106
  def process!(options={})
89
107
  release_delay = options.delete(:release_delay) || RELEASE_DELAY
90
108
  reserve_timeout = options.delete(:reserve_timeout) || RESERVE_TIMEOUT
91
- tubes.watch!(*processors.keys)
92
- loop do
109
+ client.tubes.watch!(*processors.keys)
110
+ while !stop? do
93
111
  begin
94
- job = tubes.reserve(reserve_timeout)
95
- processor = processors[job.tube]
112
+ @current_job = client.tubes.reserve(reserve_timeout)
113
+ processor = processors[@current_job.tube]
96
114
  begin
97
- processor[:block].call(job)
98
- job.delete
115
+ processor[:block].call(@current_job)
116
+ @current_job.delete
99
117
  rescue *processor[:retry_on]
100
- job.release(:delay => release_delay) if job.stats.releases < processor[:max_retries]
118
+ if @current_job.stats.releases < processor[:max_retries]
119
+ @current_job.release(:delay => release_delay)
120
+ end
101
121
  end
102
122
  rescue AbortProcessingError
103
123
  break
104
124
  rescue Beaneater::JobNotReserved, Beaneater::NotFoundError, Beaneater::TimedOutError
105
125
  retry
106
- rescue StandardError => e # handles unspecified errors
107
- job.bury
126
+ rescue StandardError # handles unspecified errors
127
+ @current_job.bury if @current_job
108
128
  ensure # bury if still reserved
109
- job.bury if job && job.exists? && job.reserved?
129
+ @current_job.bury if @current_job && @current_job.exists? && @current_job.reserved?
130
+ @current_job = nil
110
131
  end
111
132
  end
112
133
  end # process!
113
134
  end # Jobs
114
- end # Beaneater
135
+ end # Beaneater
@@ -1,4 +1,4 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Represents job related commands.
3
3
  class Job
4
4
 
@@ -6,21 +6,21 @@ module Beaneater
6
6
  # @return [Integer] id for the job.
7
7
  # @!attribute body
8
8
  # @return [String] the job's body.
9
- # @!attribute connection
10
- # @return [Beaneater::Connection] connection which has retrieved job.
11
9
  # @!attribute reserved
12
10
  # @return [Boolean] whether the job has been reserved.
13
- attr_reader :id, :body, :connection, :reserved
11
+ # @!attribute client
12
+ # @return [Beaneater] returns the client instance
13
+ attr_reader :id, :body, :reserved, :client
14
14
 
15
15
 
16
16
  # Initializes a new job object.
17
17
  #
18
18
  # @param [Hash{Symbol => String,Number}] res Result from beanstalkd response
19
19
  #
20
- def initialize(res)
20
+ def initialize(client, res)
21
+ @client = client
21
22
  @id = res[:id]
22
23
  @body = res[:body]
23
- @connection = res[:connection]
24
24
  @reserved = res[:status] == 'RESERVED'
25
25
  end
26
26
 
@@ -31,8 +31,8 @@ module Beaneater
31
31
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
32
32
  #
33
33
  # @example
34
- # @beaneater_connection.bury({:pri => 100})
35
- # # => {:status=>"BURIED", :body=>nil, :connection=>#<Beaneater::Connection host="localhost" port=11300>}
34
+ # @job.bury({:pri => 100})
35
+ # # => {:status=>"BURIED", :body=>nil}
36
36
  #
37
37
  # @api public
38
38
  def bury(options={})
@@ -49,8 +49,8 @@ module Beaneater
49
49
  # @option options [Integer] delay Assign new delay to job
50
50
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
51
51
  # @example
52
- # @beaneater_connection.jobs.find(123).release(:pri => 10, :delay => 5)
53
- # # => {:status=>"RELEASED", :body=>nil, :connection=>#<Beaneater::Connection host="localhost" port=11300>}
52
+ # @beaneater.jobs.find(123).release(:pri => 10, :delay => 5)
53
+ # # => {:status=>"RELEASED", :body=>nil}
54
54
  #
55
55
  # @api public
56
56
  def release(options={})
@@ -64,8 +64,8 @@ module Beaneater
64
64
  #
65
65
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
66
66
  # @example
67
- # @beaneater_connection.jobs.find(123).touch
68
- # # => {:status=>"TOUCHED", :body=>nil, :connection=>#<Beaneater::Connection host="localhost" port=11300>}
67
+ # @beaneater.jobs.find(123).touch
68
+ # # => {:status=>"TOUCHED", :body=>nil}
69
69
  #
70
70
  # @api public
71
71
  def touch
@@ -76,8 +76,8 @@ module Beaneater
76
76
  #
77
77
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
78
78
  # @example
79
- # @beaneater_connection.jobs.find(123).delete
80
- # # => {:status=>"DELETED", :body=>nil, :connection=>#<Beaneater::Connection host="localhost" port=11300>}
79
+ # @beaneater.jobs.find(123).delete
80
+ # # => {:status=>"DELETED", :body=>nil}
81
81
  #
82
82
  # @api public
83
83
  def delete
@@ -88,8 +88,8 @@ module Beaneater
88
88
  #
89
89
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
90
90
  # @example
91
- # @beaneater_connection.jobs.find(123).kick
92
- # # => {:status=>"KICKED", :body=>nil, :connection=>#<Beaneater::Connection host="localhost" port=11300>}
91
+ # @beaneater.jobs.find(123).kick
92
+ # # => {:status=>"KICKED", :body=>nil}
93
93
  #
94
94
  # @api public
95
95
  def kick
@@ -100,7 +100,7 @@ module Beaneater
100
100
  #
101
101
  # @return [Beaneater::StatStruct] struct filled with relevant job stats
102
102
  # @example
103
- # @beaneater_connection.jobs.find(123).stats
103
+ # @beaneater.jobs.find(123).stats
104
104
  # @job.stats.tube # => "some-tube"
105
105
  #
106
106
  # @api public
@@ -113,7 +113,7 @@ module Beaneater
113
113
  #
114
114
  # @return [Boolean] Returns true if the job is in a reserved state
115
115
  # @example
116
- # @beaneater_connection.jobs.find(123).reserved?
116
+ # @beaneater.jobs.find(123).reserved?
117
117
  #
118
118
  # @api public
119
119
  def reserved?
@@ -124,7 +124,7 @@ module Beaneater
124
124
  #
125
125
  # @return [Boolean] Returns true if the job still exists
126
126
  # @example
127
- # @beaneater_connection.jobs.find(123).exists?
127
+ # @beaneater.jobs.find(123).exists?
128
128
  #
129
129
  # @api public
130
130
  def exists?
@@ -137,7 +137,7 @@ module Beaneater
137
137
  #
138
138
  # @return [String] The name of the tube for this job
139
139
  # @example
140
- # @beaneater_connection.jobs.find(123).tube
140
+ # @beaneater.jobs.find(123).tube
141
141
  # # => "some-tube"
142
142
  #
143
143
  # @api public
@@ -149,7 +149,7 @@ module Beaneater
149
149
  #
150
150
  # @return [Integer] The ttr of this job
151
151
  # @example
152
- # @beaneater_connection.jobs.find(123).ttr
152
+ # @beaneater.jobs.find(123).ttr
153
153
  # # => 123
154
154
  #
155
155
  # @api public
@@ -161,7 +161,7 @@ module Beaneater
161
161
  #
162
162
  # @return [Integer] The pri of this job
163
163
  # @example
164
- # @beaneater_connection.jobs.find(123).pri
164
+ # @beaneater.jobs.find(123).pri
165
165
  # # => 1
166
166
  #
167
167
  def pri
@@ -172,7 +172,7 @@ module Beaneater
172
172
  #
173
173
  # @return [Integer] The delay of this job
174
174
  # @example
175
- # @beaneater_connection.jobs.find(123).delay
175
+ # @beaneater.jobs.find(123).delay
176
176
  # # => 5
177
177
  #
178
178
  def delay
@@ -183,8 +183,8 @@ module Beaneater
183
183
  #
184
184
  # @return [String] string representation
185
185
  # @example
186
- # @beaneater_connection.jobs.find(123).to_s
187
- # @beaneater_connection.jobs.find(123).inspect
186
+ # @beaneater.jobs.find(123).to_s
187
+ # @beaneater.jobs.find(123).inspect
188
188
  #
189
189
  def to_s
190
190
  "#<Beaneater::Job id=#{id} body=#{body.inspect}>"
@@ -193,7 +193,7 @@ module Beaneater
193
193
 
194
194
  protected
195
195
 
196
- # Transmit command to beanstalkd instances and fetch response.
196
+ # Transmit command to beanstalkd instance and fetch response.
197
197
  #
198
198
  # @param [String] cmd Beanstalkd command to send.
199
199
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
@@ -202,7 +202,7 @@ module Beaneater
202
202
  # transmit('stats') { 'success' }
203
203
  #
204
204
  def transmit(cmd, &block)
205
- res = connection.transmit(cmd)
205
+ res = client.connection.transmit(cmd)
206
206
  yield if block_given?
207
207
  res
208
208
  end
@@ -1,9 +1,24 @@
1
1
  require 'beaneater/stats/fast_struct'
2
2
  require 'beaneater/stats/stat_struct'
3
3
 
4
- module Beaneater
4
+ class Beaneater
5
5
  # Represents stats related to the beanstalkd pool.
6
- class Stats < PoolCommand
6
+ class Stats
7
+
8
+ # @!attribute client
9
+ # @return [Beaneater] returns the client instance
10
+ attr_reader :client
11
+
12
+ # Creates new stats instance.
13
+ #
14
+ # @param [Beaneater] client The beaneater client instance.
15
+ # @example
16
+ # Beaneater::Stats.new(@client)
17
+ #
18
+ def initialize(client)
19
+ @client = client
20
+ end
21
+
7
22
  # Returns keys for stats data
8
23
  #
9
24
  # @return [Array<String>] Set of keys for stats.
@@ -26,6 +41,14 @@ module Beaneater
26
41
  data[key]
27
42
  end
28
43
 
44
+ # Delegates inspection to the real data structure
45
+ #
46
+ # @return [String] returns a string containing a detailed stats summary
47
+ def inspect
48
+ data.to_s
49
+ end
50
+ alias :to_s :inspect
51
+
29
52
  # Defines a cached method for looking up data for specified key
30
53
  # Protects against infinite loops by checking stacktrace
31
54
  # @api public
@@ -42,15 +65,15 @@ module Beaneater
42
65
 
43
66
  protected
44
67
 
45
- # Returns struct based on stats data merged from all connections.
68
+ # Returns struct based on stats data from response.
46
69
  #
47
- # @return [Beaneater::StatStruct] the combined stats for all beanstalk connections in the pool
70
+ # @return [Beaneater::StatStruct] the stats
48
71
  # @example
49
72
  # self.data # => { 'version' : 1.7, 'total_connections' : 23 }
50
73
  # self.data.total_connections # => 23
51
74
  #
52
75
  def data
53
- StatStruct.from_hash(transmit_to_all('stats', :merge => true)[:body])
76
+ StatStruct.from_hash(client.connection.transmit('stats')[:body])
54
77
  end
55
78
  end # Stats
56
79
  end # Beaneater
@@ -1,4 +1,4 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  #
3
3
  # Borrowed from:
4
4
  # https://github.com/dolzenko/faster_open_struct/blob/master/lib/faster_open_struct.rb
@@ -1,4 +1,4 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Represents a stats hash with proper underscored keys
3
3
  class StatStruct < FasterOpenStruct
4
4
  # Convert a stats hash into a struct.
@@ -35,5 +35,11 @@ module Beaneater
35
35
  def keys
36
36
  @hash.keys.map { |k| k.to_s }
37
37
  end
38
+
39
+ # Returns the initialization hash
40
+ #
41
+ def to_h
42
+ @hash
43
+ end
38
44
  end # StatStruct
39
- end # Beaneater
45
+ end # Beaneater
@@ -1,16 +1,36 @@
1
- module Beaneater
1
+ class Beaneater
2
2
  # Represents collection of tube related commands.
3
- class Tubes < PoolCommand
3
+
4
+ class Tubes
5
+ include Enumerable
6
+
7
+ # @!attribute client
8
+ # @return [Beaneater] returns the client instance
9
+ attr_reader :client
4
10
 
5
11
  # Creates new tubes instance.
6
12
  #
7
- # @param [Beaneater::Pool] pool The beaneater pool for this tube.
13
+ # @param [Beaneater] client The beaneater client instance.
8
14
  # @example
9
- # Beaneater::Tubes.new(@pool)
15
+ # Beaneater::Tubes.new(@client)
16
+ #
17
+ def initialize(client)
18
+ @client = client
19
+ end
20
+
21
+ def last_used
22
+ client.connection.tube_used
23
+ end
24
+
25
+ def last_used=(tube_name)
26
+ client.connection.tube_used = tube_name
27
+ end
28
+
29
+ # Delegates transmit to the connection object.
10
30
  #
11
- def initialize(pool)
12
- @last_used = 'default'
13
- super
31
+ # @see Beaneater::Connection#transmit
32
+ def transmit(command, options={})
33
+ client.connection.transmit(command, options)
14
34
  end
15
35
 
16
36
  # Finds the specified beanstalk tube.
@@ -24,7 +44,7 @@ module Beaneater
24
44
  #
25
45
  # @api public
26
46
  def find(tube_name)
27
- Tube.new(self.pool, tube_name)
47
+ Tube.new(client, tube_name)
28
48
  end
29
49
  alias_method :[], :find
30
50
 
@@ -35,13 +55,14 @@ module Beaneater
35
55
  # @yield [job] Reserved beaneater job.
36
56
  # @return [Beaneater::Job] Reserved beaneater job.
37
57
  # @example
38
- # @conn.tubes.reserve { |job| process(job) }
58
+ # @client.tubes.reserve { |job| process(job) }
39
59
  # # => <Beaneater::Job id=5 body="foo">
40
60
  #
41
61
  # @api public
42
62
  def reserve(timeout=nil, &block)
43
- res = transmit_to_rand(timeout ? "reserve-with-timeout #{timeout}" : 'reserve')
44
- job = Job.new(res)
63
+ res = transmit(
64
+ timeout ? "reserve-with-timeout #{timeout}" : 'reserve')
65
+ job = Job.new(client, res)
45
66
  block.call(job) if block_given?
46
67
  job
47
68
  end
@@ -50,36 +71,54 @@ module Beaneater
50
71
  #
51
72
  # @return [Array<Beaneater::Tube>] List of all beanstalk tubes.
52
73
  # @example
53
- # @pool.tubes.all
74
+ # @client.tubes.all
54
75
  # # => [<Beaneater::Tube name="tube2">, <Beaneater::Tube name="tube3">]
55
76
  #
56
77
  # @api public
57
78
  def all
58
- transmit_to_all('list-tubes', :merge => true)[:body].map { |tube_name| Tube.new(self.pool, tube_name) }
79
+ transmit('list-tubes')[:body].map do |tube_name|
80
+ Tube.new(client, tube_name)
81
+ end
82
+ end
83
+
84
+ # Calls the given block once for each known beanstalk tube, passing that element as a parameter.
85
+ #
86
+ # @return An Enumerator is returned if no block is given.
87
+ # @example
88
+ # @pool.tubes.each {|t| puts t.name}
89
+ #
90
+ # @api public
91
+ def each
92
+ block_given? ? all.each(&Proc.new) : all.each
59
93
  end
60
94
 
61
95
  # List of watched beanstalk tubes.
62
96
  #
63
97
  # @return [Array<Beaneater::Tube>] List of watched beanstalk tubes.
64
98
  # @example
65
- # @pool.tubes.watched
99
+ # @client.tubes.watched
66
100
  # # => [<Beaneater::Tube name="tube2">, <Beaneater::Tube name="tube3">]
67
101
  #
68
102
  # @api public
69
103
  def watched
70
- transmit_to_all('list-tubes-watched', :merge => true)[:body].map { |tube_name| Tube.new(self.pool, tube_name) }
104
+ last_watched = transmit('list-tubes-watched')[:body]
105
+ client.connection.tubes_watched = last_watched.dup
106
+ last_watched.map do |tube_name|
107
+ Tube.new(client, tube_name)
108
+ end
71
109
  end
72
110
 
73
111
  # Currently used beanstalk tube.
74
112
  #
75
113
  # @return [Beaneater::Tube] Currently used beanstalk tube.
76
114
  # @example
77
- # @pool.tubes.used
115
+ # @client.tubes.used
78
116
  # # => <Beaneater::Tube name="tube2">
79
117
  #
80
118
  # @api public
81
119
  def used
82
- Tube.new(self.pool, transmit_to_rand('list-tube-used')[:id])
120
+ last_used = transmit('list-tube-used')[:id]
121
+ Tube.new(client, last_used)
83
122
  end
84
123
 
85
124
  # Add specified beanstalkd tubes as watched.
@@ -87,12 +126,13 @@ module Beaneater
87
126
  # @param [*String] names Name of tubes to watch
88
127
  # @raise [Beaneater::InvalidTubeName] Tube to watch was invalid.
89
128
  # @example
90
- # @pool.tubes.watch('foo', 'bar')
129
+ # @client.tubes.watch('foo', 'bar')
91
130
  #
92
131
  # @api public
93
132
  def watch(*names)
94
133
  names.each do |t|
95
- transmit_to_all "watch #{t}"
134
+ transmit "watch #{t}"
135
+ client.connection.add_to_watched(t)
96
136
  end
97
137
  rescue BadFormatError => ex
98
138
  raise InvalidTubeName, "Tube in '#{ex.cmd}' is invalid!"
@@ -103,7 +143,7 @@ module Beaneater
103
143
  # @param [*String] names Name of tubes to watch
104
144
  # @raise [Beaneater::InvalidTubeName] Tube to watch was invalid.
105
145
  # @example
106
- # @pool.tubes.watch!('foo', 'bar')
146
+ # @client.tubes.watch!('foo', 'bar')
107
147
  #
108
148
  # @api public
109
149
  def watch!(*names)
@@ -116,12 +156,13 @@ module Beaneater
116
156
  #
117
157
  # @param [*String] names Name of tubes to ignore
118
158
  # @example
119
- # @pool.tubes.ignore('foo', 'bar')
159
+ # @client.tubes.ignore('foo', 'bar')
120
160
  #
121
161
  # @api public
122
162
  def ignore(*names)
123
163
  names.each do |w|
124
- transmit_to_all "ignore #{w}"
164
+ transmit "ignore #{w}"
165
+ client.connection.remove_from_watched(w)
125
166
  end
126
167
  end
127
168
 
@@ -132,11 +173,11 @@ module Beaneater
132
173
  # @conn.tubes.use("some-tube")
133
174
  #
134
175
  def use(tube)
135
- return tube if @last_used == tube
136
- res = transmit_to_all("use #{tube}")
137
- @last_used = tube
176
+ return tube if last_used == tube
177
+ transmit("use #{tube}")
178
+ last_used = tube
138
179
  rescue BadFormatError
139
180
  raise InvalidTubeName, "Tube cannot be named '#{tube}'"
140
181
  end
141
182
  end # Tubes
142
- end # Beaneater
183
+ end # Beaneater