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.
- checksums.yaml +7 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +31 -1
- data/Gemfile +5 -1
- data/README.md +50 -26
- data/Rakefile +3 -1
- data/beaneater.gemspec +1 -1
- data/examples/demo.rb +1 -2
- data/lib/beaneater.rb +57 -4
- data/lib/beaneater/configuration.rb +3 -1
- data/lib/beaneater/connection.rb +135 -30
- data/lib/beaneater/errors.rb +4 -4
- data/lib/beaneater/job/collection.rb +67 -37
- data/lib/beaneater/job/record.rb +42 -30
- data/lib/beaneater/stats.rb +28 -5
- data/lib/beaneater/stats/fast_struct.rb +1 -1
- data/lib/beaneater/stats/stat_struct.rb +8 -2
- data/lib/beaneater/tube/collection.rb +67 -26
- data/lib/beaneater/tube/record.rb +45 -27
- data/lib/beaneater/version.rb +2 -2
- data/test/beaneater_test.rb +5 -11
- data/test/connection_test.rb +55 -1
- data/test/errors_test.rb +9 -2
- data/test/job_test.rb +8 -12
- data/test/jobs_test.rb +15 -43
- data/test/prompt_regexp_test.rb +14 -2
- data/test/stat_struct_test.rb +12 -2
- data/test/stats_test.rb +9 -9
- data/test/test_helper.rb +34 -10
- data/test/tube_test.rb +12 -23
- data/test/tubes_test.rb +61 -56
- metadata +25 -65
- data/lib/beaneater/pool.rb +0 -166
- data/lib/beaneater/pool_command.rb +0 -79
- data/test/pool_command_test.rb +0 -90
- data/test/pool_test.rb +0 -180
data/lib/beaneater/errors.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
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
|
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
|
-
|
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
|
7
|
+
class Jobs
|
8
8
|
|
9
9
|
# @!attribute processors
|
10
10
|
# @return [Array<Proc>] returns Collection of proc to handle beanstalkd jobs
|
11
|
-
|
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
|
-
#
|
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 [
|
22
|
-
# @return [Beaneater::Job] Job matching given id
|
28
|
+
# @param [Beaneater] client The beaneater client instance.
|
23
29
|
# @example
|
24
|
-
#
|
25
|
-
# @beaneater_pool.jobs.find(123) # => <Beaneater::Job>
|
26
|
-
# @beaneater_pool.jobs.peek(123) # => <Beaneater::Job>
|
30
|
+
# Beaneater::Jobs.new(@client)
|
27
31
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
#
|
43
|
+
# Peek (or find) job by id from beanstalkd.
|
39
44
|
#
|
40
45
|
# @param [Integer] id Job id to find
|
41
|
-
# @return [
|
46
|
+
# @return [Beaneater::Job] Job matching given id
|
42
47
|
# @example
|
43
|
-
# @
|
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
|
47
|
-
res =
|
48
|
-
|
49
|
-
rescue Beaneater::NotFoundError
|
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
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
92
|
-
|
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
|
96
|
-
|
97
|
-
rescue StandardError
|
98
|
-
|
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
|
-
|
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
|
data/lib/beaneater/job/record.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
140
|
+
# @beaneater.jobs.find(123).tube
|
131
141
|
# # => "some-tube"
|
132
142
|
#
|
143
|
+
# @api public
|
133
144
|
def tube
|
134
|
-
|
145
|
+
@tube ||= self.stats.tube
|
135
146
|
end
|
136
147
|
|
137
148
|
# Returns the ttr of this job
|
138
149
|
#
|
139
|
-
# @return [
|
150
|
+
# @return [Integer] The ttr of this job
|
140
151
|
# @example
|
141
|
-
# @
|
152
|
+
# @beaneater.jobs.find(123).ttr
|
142
153
|
# # => 123
|
143
154
|
#
|
155
|
+
# @api public
|
144
156
|
def ttr
|
145
|
-
|
157
|
+
@ttr ||= self.stats.ttr
|
146
158
|
end
|
147
159
|
|
148
160
|
# Returns the pri of this job
|
149
161
|
#
|
150
|
-
# @return [
|
162
|
+
# @return [Integer] The pri of this job
|
151
163
|
# @example
|
152
|
-
# @
|
164
|
+
# @beaneater.jobs.find(123).pri
|
153
165
|
# # => 1
|
154
166
|
#
|
155
167
|
def pri
|
156
|
-
self.stats
|
168
|
+
self.stats.pri
|
157
169
|
end
|
158
170
|
|
159
171
|
# Returns the delay of this job
|
160
172
|
#
|
161
|
-
# @return [
|
173
|
+
# @return [Integer] The delay of this job
|
162
174
|
# @example
|
163
|
-
# @
|
175
|
+
# @beaneater.jobs.find(123).delay
|
164
176
|
# # => 5
|
165
177
|
#
|
166
178
|
def delay
|
167
|
-
self.stats
|
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
|
-
# @
|
175
|
-
# @
|
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
|
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
|
data/lib/beaneater/stats.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
require 'beaneater/stats/fast_struct'
|
2
2
|
require 'beaneater/stats/stat_struct'
|
3
3
|
|
4
|
-
|
4
|
+
class Beaneater
|
5
5
|
# Represents stats related to the beanstalkd pool.
|
6
|
-
class Stats
|
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
|
68
|
+
# Returns struct based on stats data from response.
|
46
69
|
#
|
47
|
-
# @return [Beaneater::StatStruct] the
|
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(
|
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
|
-
|
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
|