beaneater 0.3.0 → 1.1.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.
@@ -66,11 +66,11 @@ module Beaneater
66
66
  # Raises when a command was sent that is unknown.
67
67
  class UnknownCommandError < UnexpectedResponse; end
68
68
  # Raises when command does not have proper CRLF suffix.
69
- class ExpectedCRLFError < UnexpectedResponse; end
69
+ class ExpectedCrlfError < UnexpectedResponse; end
70
70
  # Raises when the body of a job was too large.
71
71
  class JobTooBigError < UnexpectedResponse; end
72
- # Raises when a job was attempted to be reserved but the timeout occured.
72
+ # Raises when a job was attempted to be reserved but the timeout occurred.
73
73
  class TimedOutError < UnexpectedResponse; end
74
74
  # Raises when a tube could not be ignored because it is the last watched tube.
75
75
  class NotIgnoredError < UnexpectedResponse; end
76
- end
76
+ end
@@ -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
@@ -16,39 +20,44 @@ module Beaneater
16
20
  # Delay in seconds before to make job ready again.
17
21
  RELEASE_DELAY = 1
18
22
 
19
- # Peek (or find) first job from beanstalkd pool.
23
+ # Number of seconds to wait for a job before checking a different server.
24
+ RESERVE_TIMEOUT = nil
25
+
26
+ # Creates new jobs instance.
20
27
  #
21
- # @param [Integer] id Job id to find
22
- # @return [Beaneater::Job] Job matching given id
28
+ # @param [Beaneater] client The beaneater client instance.
23
29
  # @example
24
- # @beaneater_pool.jobs[123] # => <Beaneater::Job>
25
- # @beaneater_pool.jobs.find(123) # => <Beaneater::Job>
26
- # @beaneater_pool.jobs.peek(123) # => <Beaneater::Job>
30
+ # Beaneater::Jobs.new(@client)
27
31
  #
28
- # @api public
29
- def find(id)
30
- res = transmit_until_res("peek #{id}", :status => "FOUND")
31
- Job.new(res)
32
- rescue Beaneater::NotFoundError => ex
33
- 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)
34
41
  end
35
- alias_method :peek, :find
36
- alias_method :[], :find
37
42
 
38
- # Find all jobs with specified id fromm all beanstalkd servers in pool.
43
+ # Peek (or find) job by id from beanstalkd.
39
44
  #
40
45
  # @param [Integer] id Job id to find
41
- # @return [Array<Beaneater::Job>] Jobs matching given id
46
+ # @return [Beaneater::Job] Job matching given id
42
47
  # @example
43
- # @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>
44
51
  #
45
52
  # @api public
46
- def find_all(id)
47
- res = transmit_to_all("peek #{id}")
48
- res.compact.map { |r| Job.new(r) }
49
- rescue Beaneater::NotFoundError => ex
50
- []
53
+ def find(id)
54
+ res = transmit("peek #{id}")
55
+ Job.new(client, res)
56
+ rescue Beaneater::NotFoundError
57
+ nil
51
58
  end
59
+ alias_method :peek, :find
60
+ alias_method :[], :find
52
61
 
53
62
  # Register a processor to handle beanstalkd job on particular tube.
54
63
  #
@@ -75,31 +84,52 @@ module Beaneater
75
84
  @processors[tube_name.to_s] = { :block => block, :retry_on => retry_on, :max_retries => max_retries }
76
85
  end
77
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
+
78
99
  # Watch, reserve, process and delete or bury or release jobs.
79
100
  #
80
101
  # @param [Hash{String => Integer}] options Settings for processing
81
102
  # @option options [Integer] release_delay Delay in seconds before to make job ready again
103
+ # @option options [Integer] reserve_timeout Number of seconds to wait for a job before checking a different server
82
104
  #
83
105
  # @api public
84
106
  def process!(options={})
85
107
  release_delay = options.delete(:release_delay) || RELEASE_DELAY
86
- tubes.watch!(*processors.keys)
87
- loop do
88
- job = tubes.reserve
89
- processor = processors[job.tube]
108
+ reserve_timeout = options.delete(:reserve_timeout) || RESERVE_TIMEOUT
109
+ client.tubes.watch!(*processors.keys)
110
+ while !stop? do
90
111
  begin
91
- processor[:block].call(job)
92
- job.delete
112
+ @current_job = client.tubes.reserve(reserve_timeout)
113
+ processor = processors[@current_job.tube]
114
+ begin
115
+ processor[:block].call(@current_job)
116
+ @current_job.delete
117
+ rescue *processor[:retry_on]
118
+ if @current_job.stats.releases < processor[:max_retries]
119
+ @current_job.release(:delay => release_delay)
120
+ end
121
+ end
93
122
  rescue AbortProcessingError
94
123
  break
95
- rescue *processor[:retry_on]
96
- job.release(:delay => release_delay) if job.stats.releases < processor[:max_retries]
97
- rescue StandardError => e # handles unspecified errors
98
- job.bury
124
+ rescue Beaneater::JobNotReserved, Beaneater::NotFoundError, Beaneater::TimedOutError
125
+ retry
126
+ rescue StandardError # handles unspecified errors
127
+ @current_job.bury if @current_job
99
128
  ensure # bury if still reserved
100
- job.bury if job.exists? && job.reserved?
129
+ @current_job.bury if @current_job && @current_job.exists? && @current_job.reserved?
130
+ @current_job = nil
101
131
  end
102
132
  end
103
133
  end # process!
104
134
  end # Jobs
105
- 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
 
@@ -28,9 +28,11 @@ module Beaneater
28
28
  #
29
29
  # @param [Hash{Symbol => Integer}] options Settings to bury job
30
30
  # @option options [Integer] pri Assign new priority to job
31
+ # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
31
32
  #
32
33
  # @example
33
- # @beaneater_connection.bury({:pri => 100})
34
+ # @job.bury({:pri => 100})
35
+ # # => {:status=>"BURIED", :body=>nil}
34
36
  #
35
37
  # @api public
36
38
  def bury(options={})
@@ -45,8 +47,10 @@ module Beaneater
45
47
  # @param [Hash{String => Integer}] options Settings to release job
46
48
  # @option options [Integer] pri Assign new priority to job
47
49
  # @option options [Integer] delay Assign new delay to job
50
+ # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
48
51
  # @example
49
- # @beaneater_connection.jobs.find(123).release(:pri => 10, :delay => 5)
52
+ # @beaneater.jobs.find(123).release(:pri => 10, :delay => 5)
53
+ # # => {:status=>"RELEASED", :body=>nil}
50
54
  #
51
55
  # @api public
52
56
  def release(options={})
@@ -58,8 +62,10 @@ module Beaneater
58
62
 
59
63
  # Sends command to touch job which extends the ttr.
60
64
  #
65
+ # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
61
66
  # @example
62
- # @beaneater_connection.jobs.find(123).touch
67
+ # @beaneater.jobs.find(123).touch
68
+ # # => {:status=>"TOUCHED", :body=>nil}
63
69
  #
64
70
  # @api public
65
71
  def touch
@@ -68,8 +74,10 @@ module Beaneater
68
74
 
69
75
  # Sends command to delete a job.
70
76
  #
77
+ # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
71
78
  # @example
72
- # @beaneater_connection.jobs.find(123).delete
79
+ # @beaneater.jobs.find(123).delete
80
+ # # => {:status=>"DELETED", :body=>nil}
73
81
  #
74
82
  # @api public
75
83
  def delete
@@ -78,8 +86,10 @@ module Beaneater
78
86
 
79
87
  # Sends command to kick a buried job.
80
88
  #
89
+ # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
81
90
  # @example
82
- # @beaneater_connection.jobs.find(123).kick
91
+ # @beaneater.jobs.find(123).kick
92
+ # # => {:status=>"KICKED", :body=>nil}
83
93
  #
84
94
  # @api public
85
95
  def kick
@@ -90,7 +100,7 @@ module Beaneater
90
100
  #
91
101
  # @return [Beaneater::StatStruct] struct filled with relevant job stats
92
102
  # @example
93
- # @beaneater_connection.jobs.find(123).stats
103
+ # @beaneater.jobs.find(123).stats
94
104
  # @job.stats.tube # => "some-tube"
95
105
  #
96
106
  # @api public
@@ -103,7 +113,7 @@ module Beaneater
103
113
  #
104
114
  # @return [Boolean] Returns true if the job is in a reserved state
105
115
  # @example
106
- # @beaneater_connection.jobs.find(123).reserved?
116
+ # @beaneater.jobs.find(123).reserved?
107
117
  #
108
118
  # @api public
109
119
  def reserved?
@@ -114,7 +124,7 @@ module Beaneater
114
124
  #
115
125
  # @return [Boolean] Returns true if the job still exists
116
126
  # @example
117
- # @beaneater_connection.jobs.find(123).exists?
127
+ # @beaneater.jobs.find(123).exists?
118
128
  #
119
129
  # @api public
120
130
  def exists?
@@ -127,52 +137,54 @@ module Beaneater
127
137
  #
128
138
  # @return [String] The name of the tube for this job
129
139
  # @example
130
- # @beaneater_connection.jobs.find(123).tube
140
+ # @beaneater.jobs.find(123).tube
131
141
  # # => "some-tube"
132
142
  #
143
+ # @api public
133
144
  def tube
134
- self.stats && self.stats.tube
145
+ @tube ||= self.stats.tube
135
146
  end
136
147
 
137
148
  # Returns the ttr of this job
138
149
  #
139
- # @return [String] The ttr of this job
150
+ # @return [Integer] The ttr of this job
140
151
  # @example
141
- # @beaneater_connection.jobs.find(123).ttr
152
+ # @beaneater.jobs.find(123).ttr
142
153
  # # => 123
143
154
  #
155
+ # @api public
144
156
  def ttr
145
- self.stats && self.stats.ttr
157
+ @ttr ||= self.stats.ttr
146
158
  end
147
159
 
148
160
  # Returns the pri of this job
149
161
  #
150
- # @return [String] The pri of this job
162
+ # @return [Integer] The pri of this job
151
163
  # @example
152
- # @beaneater_connection.jobs.find(123).pri
164
+ # @beaneater.jobs.find(123).pri
153
165
  # # => 1
154
166
  #
155
167
  def pri
156
- self.stats && self.stats.pri
168
+ self.stats.pri
157
169
  end
158
170
 
159
171
  # Returns the delay of this job
160
172
  #
161
- # @return [String] The delay of this job
173
+ # @return [Integer] The delay of this job
162
174
  # @example
163
- # @beaneater_connection.jobs.find(123).delay
175
+ # @beaneater.jobs.find(123).delay
164
176
  # # => 5
165
177
  #
166
178
  def delay
167
- self.stats && self.stats.delay
179
+ self.stats.delay
168
180
  end
169
181
 
170
182
  # Returns string representation of job
171
183
  #
172
184
  # @return [String] string representation
173
185
  # @example
174
- # @beaneater_connection.jobs.find(123).to_s
175
- # @beaneater_connection.jobs.find(123).inspect
186
+ # @beaneater.jobs.find(123).to_s
187
+ # @beaneater.jobs.find(123).inspect
176
188
  #
177
189
  def to_s
178
190
  "#<Beaneater::Job id=#{id} body=#{body.inspect}>"
@@ -181,7 +193,7 @@ module Beaneater
181
193
 
182
194
  protected
183
195
 
184
- # Transmit command to beanstalkd instances and fetch response.
196
+ # Transmit command to beanstalkd instance and fetch response.
185
197
  #
186
198
  # @param [String] cmd Beanstalkd command to send.
187
199
  # @return [Hash{Symbol => String,Number}] Beanstalkd response for the command.
@@ -190,7 +202,7 @@ module Beaneater
190
202
  # transmit('stats') { 'success' }
191
203
  #
192
204
  def transmit(cmd, &block)
193
- res = connection.transmit(cmd)
205
+ res = client.connection.transmit(cmd)
194
206
  yield if block_given?
195
207
  res
196
208
  end
@@ -209,4 +221,4 @@ module Beaneater
209
221
  end
210
222
 
211
223
  end # Job
212
- end # Beaneater
224
+ end # Beaneater
@@ -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