beaneater 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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