beaneater 1.1.4 → 1.2.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.
@@ -111,7 +111,20 @@ class Beaneater
111
111
  client.tubes.watch!(*processors.keys)
112
112
  while !stop? do
113
113
  begin
114
- @current_job = client.tubes.reserve(reserve_timeout)
114
+ @current_job = nil # never take an earlier iteration's job for this one
115
+ begin
116
+ @current_job = client.tubes.reserve(reserve_timeout)
117
+ rescue Beaneater::JobParseFailure => ex
118
+ # The job_parser rejected the job just reserved, so there was no job to handle: it is
119
+ # ours to bury (the ensure below does), whatever the parser raised, rather than leave
120
+ # it reserved to be released and fail again when its TTR runs out.
121
+ # ceiling: a parser exception that can't be copied is raised untagged, so process!
122
+ # can't tell which job it came from; the job stays reserved, and near its TTR process!
123
+ # keeps reserving (DEADLINE_SOON) until beanstalkd releases it, then fails on it again.
124
+ # Upgrade path: have the connection record the id of the job whose parse failed.
125
+ @current_job = Job.new(client, :id => ex.job_id, :status => 'RESERVED')
126
+ raise
127
+ end
115
128
  processor = processors[@current_job.tube]
116
129
  begin
117
130
  processor[:block].call(@current_job)
@@ -124,14 +137,37 @@ class Beaneater
124
137
  rescue AbortProcessingError
125
138
  break
126
139
  rescue Beaneater::JobNotReserved, Beaneater::NotFoundError, Beaneater::TimedOutError
127
- retry
128
- rescue StandardError # handles unspecified errors
129
- @current_job.bury if @current_job
140
+ # No job yet (reserve timed out), or the job is gone (its handler deleted or released
141
+ # it, or its TTR ran out). If a handler or the job_parser raised this with the job still
142
+ # reserved, the ensure buries that job like any failure; then the loop goes on.
143
+ nil
144
+ rescue Beaneater::NotConnected
145
+ # Only when this client's own connection is gone (reconnecting failed, or it was
146
+ # closed): the server releases a job reserved on it. A NotConnected from a handler that
147
+ # uses another client is a failed job like any other, buried by the ensure below.
148
+ unless client.connection.connection
149
+ @current_job = nil
150
+ raise
151
+ end
152
+ rescue StandardError # handles unspecified errors: the ensure below buries the job
130
153
  ensure # bury if still reserved
131
- @current_job.bury if @current_job && @current_job.exists? && @current_job.reserved?
132
- @current_job = nil
154
+ begin
155
+ bury_if_reserved(@current_job)
156
+ ensure
157
+ @current_job = nil # even when burying fails, so no later loop sees this job
158
+ end
133
159
  end
134
160
  end
135
161
  end # process!
162
+
163
+ private
164
+
165
+ # Buries a job this worker still holds. NOT_FOUND means it no longer holds it (the handler
166
+ # deleted it, or its TTR ran out and the server released it), so there is nothing to bury.
167
+ def bury_if_reserved(job)
168
+ job.bury if job && job.exists? && job.reserved?
169
+ rescue Beaneater::NotFoundError, Beaneater::JobNotReserved
170
+ nil # gone, or no longer reserved by us by the time bury ran
171
+ end
136
172
  end # Jobs
137
173
  end # Beaneater
@@ -19,13 +19,18 @@ class Beaneater
19
19
 
20
20
  # Access value for stat with specified key.
21
21
  #
22
+ # Keys are stored underscored (see {.from_hash}), so hyphenated beanstalkd
23
+ # names like "current-jobs-ready" are underscored before lookup. This also
24
+ # avoids defining an invalid, hyphenated method name via method_missing.
25
+ #
22
26
  # @param [String] key Key to fetch from stats.
23
27
  # @return [String, Integer] Value for specified stat key.
24
28
  # @example
25
29
  # @stats['foo'] # => "bar"
30
+ # @stats['current-jobs-ready'] # => 5
26
31
  #
27
32
  def [](key)
28
- self.send(key.to_s)
33
+ self.send(key.to_s.gsub(/-/, '_'))
29
34
  end
30
35
 
31
36
  # Returns set of keys within this struct
@@ -2,5 +2,5 @@
2
2
 
3
3
  class Beaneater
4
4
  # Current version of gem.
5
- VERSION = "1.1.4"
5
+ VERSION = "1.2.0"
6
6
  end