Class: Wurk::Middleware::Status
- Inherits:
-
Object
- Object
- Wurk::Middleware::Status
- Includes:
- ServerMiddleware
- Defined in:
- lib/wurk/middleware/status.rb
Overview
Drives the status:<jid> lifecycle for a worker class that opted in with
sidekiq_options track: true. Every other class — the default — costs one
Hash lookup and a yield.
running ──┬─▶ complete perform returned; its value is captured
├─▶ interrupted cooperative stop (IterableJob)
└─▶ failed ──┬─▶ retrying a retry was scheduled
└─▶ dead retries exhausted / discarded
(enqueued is written at push time by the client, not here.)
failed is written the moment the attempt raises, because that much is
always true. Whether the job then retries or dies is decided one frame
further out, in JobRetry — which is why retrying is written from
there via Status.retrying and dead from DeathHandler, instead of being
guessed at here. Guessing would mean a second implementation of retry
policy (retry_for, sidekiq_retry_in returning :discard/:kill,
dead: false, poison-pill kills), and the two would drift.
Registered last of the built-ins, inside Metrics::History: the stored
result is perform's own return value, so none of them may wrap or
replace it before this one sees it.
Defined Under Namespace
Modules: DeathHandler
Constant Summary collapse
- MAX_RESULT_BYTES =
Hard cap on a stored result, in bytes. A job that returns an ActiveRecord relation must cost Redis 8 KB, not a table dump — past the cap the head is kept and
result_truncatedsays the rest was dropped. 8 * 1024
- MAX_ERROR_BYTES =
Same reasoning for the error text: a parser whose message quotes the whole offending document would otherwise store the document.
1024
Instance Attribute Summary
Attributes included from ServerMiddleware
Class Method Summary collapse
- .dead(job) ⇒ Object
- .report(error, context) ⇒ Object private
-
.retrying(job) ⇒ Object
Called by JobRetry#schedule_retry once the retry is on the ZSET — the first moment anyone knows this failure is a retry rather than a death.
-
.truncate(text, limit) ⇒ Object
private
byteslice, not slice: the cap is a byte budget, and 8192 characters is 32 KB of UTF-8 in the worst case.
Instance Method Summary collapse
-
#call(worker, job, queue) ⇒ Object
The lifecycle is inline rather than split across a helper so the untracked path is a Hash lookup and a bare
yield: taking the block as&block, or handing it to another method, allocates a Proc for every job in the process, tracked or not.
Methods included from ServerMiddleware
Class Method Details
.dead(job) ⇒ Object
102 |
# File 'lib/wurk/middleware/status.rb', line 102 def dead(job) = record_outcome(job, 'dead') |
.report(error, context) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
116 117 118 |
# File 'lib/wurk/middleware/status.rb', line 116 def report(error, context) Wurk.configuration.handle_exception(error, context: "Wurk::Middleware::Status: #{context}") end |
.retrying(job) ⇒ Object
Called by JobRetry#schedule_retry once the retry is on the ZSET — the first moment anyone knows this failure is a retry rather than a death.
100 |
# File 'lib/wurk/middleware/status.rb', line 100 def (job) = record_outcome(job, 'retrying') |
.truncate(text, limit) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
byteslice, not slice: the cap is a byte budget, and 8192 characters is 32 KB of UTF-8 in the worst case. Cutting on a byte boundary can split a multi-byte character, so the dangling tail is scrubbed away.
109 110 111 112 113 |
# File 'lib/wurk/middleware/status.rb', line 109 def truncate(text, limit) return text if text.bytesize <= limit text.byteslice(0, limit).scrub('') end |
Instance Method Details
#call(worker, job, queue) ⇒ Object
The lifecycle is inline rather than split across a helper so the
untracked path is a Hash lookup and a bare yield: taking the block as
&block, or handing it to another method, allocates a Proc for every
job in the process, tracked or not.
Rescue taxonomy mirrors Batch::ServerMiddleware — a handled skip and a
cooperative interruption are not failures. A job cut by its absolute
deadline deliberately is one here, where the two metric emitters count
it as processed-and-expired instead: those feed an aggregate an operator
subtracts (executed = processed - failed - expired), while this row
answers "what happened to this job" — it did not finish, and
error_class/error_message say why. interrupted would promise a
resume that never comes. Caveat inherited from the
chain position it shares with Metrics::History: a Limiter::OverLimit
raised inside the job body unwinds through here before Limiter (outer)
converts it into a reschedule, so a rate-limited job books failed for
an attempt it never ran. Pinned for History in
test/unit/metrics_history_test.rb; fixing it needs the chain-order
decision that test defers, and Status must not answer it differently
from its two siblings in the meantime.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/wurk/middleware/status.rb', line 64 def call(worker, job, queue) jid = job['jid'] return yield unless ::Wurk::Status.tracked?(job) && jid && !jid.empty? progress = attach_progress(worker, jid) started(jid, job, queue) begin result = yield complete(jid, job, progress, result) result rescue ::Wurk::Job::Interrupted interrupted(jid, progress) raise rescue ::Wurk::JobRetry::Handled # Neither success nor failure: the job has been put back on a queue # and will run again. Leave the row `running` and keep the progress it # reported, so the next attempt continues the same story. safely { progress.flush } raise rescue StandardError => e failed(jid, progress, e) raise end end |