Class: Flightdeck::JobDetail
- Inherits:
-
Object
- Object
- Flightdeck::JobDetail
- Defined in:
- app/models/flightdeck/job_detail.rb
Overview
Everything the job show page needs, assembled from Solid Queue's own models.
Arguments are shown exactly as they were stored. They are never handed to ActiveJob for deserialization: that would load host application classes (and run their code) just to render a dashboard.
Defined Under Namespace
Classes: Event
Constant Summary collapse
- ARGUMENTS_DISPLAY_LIMIT =
10.kilobytes
- VENDOR_FRAME =
A frame belongs to the application unless it lives in an installed gem or in Ruby's own library directories.
%r{ (?:\A|/)(?:gems|ruby|rubygems|bundler|vendor/bundle)/ | \A<internal: | \A/usr/lib/ruby/ }x
Instance Attribute Summary collapse
-
#execution ⇒ Object
readonly
Returns the value of attribute execution.
-
#job ⇒ Object
readonly
Returns the value of attribute job.
-
#process ⇒ Object
readonly
Returns the value of attribute process.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
Class Method Summary collapse
Instance Method Summary collapse
- #active_job_id ⇒ Object
- #app_frame?(line) ⇒ Boolean
- #arguments_bytes ⇒ Object
-
#arguments_json ⇒ Object
job.argumentsis already a Ruby structure (Solid Queue stores it with a JSON coder); pretty-printing it is a formatting step, not deserialization. - #arguments_preview ⇒ Object
- #arguments_truncated? ⇒ Boolean
- #backtrace ⇒ Object
- #backtrace_frames ⇒ Object
- #backtrace_total ⇒ Object
- #backtrace_truncated? ⇒ Boolean
- #class_name ⇒ Object
- #concurrency_key ⇒ Object
- #discardable? ⇒ Boolean
- #enqueued_at ⇒ Object
-
#error_class ⇒ Object
--- error --------------------------------------------------------------.
- #error_message ⇒ Object
- #failed? ⇒ Boolean
- #failed_execution ⇒ Object
- #finished_at ⇒ Object
- #id ⇒ Object
-
#initialize(job:, annotation: nil) ⇒ JobDetail
constructor
A new instance of JobDetail.
- #priority ⇒ Object
- #process_label ⇒ Object
- #queue_name ⇒ Object
- #retryable? ⇒ Boolean
- #scheduled_at ⇒ Object
-
#timeline ⇒ Object
Built only from timestamps Solid Queue actually keeps.
Constructor Details
#initialize(job:, annotation: nil) ⇒ JobDetail
Returns a new instance of JobDetail.
28 29 30 31 32 33 |
# File 'app/models/flightdeck/job_detail.rb', line 28 def initialize(job:, annotation: nil) @job = job @state = JobRow.state_for(job, annotation) @execution = load_execution(annotation) @process = load_process end |
Instance Attribute Details
#execution ⇒ Object (readonly)
Returns the value of attribute execution.
19 20 21 |
# File 'app/models/flightdeck/job_detail.rb', line 19 def execution @execution end |
#job ⇒ Object (readonly)
Returns the value of attribute job.
19 20 21 |
# File 'app/models/flightdeck/job_detail.rb', line 19 def job @job end |
#process ⇒ Object (readonly)
Returns the value of attribute process.
19 20 21 |
# File 'app/models/flightdeck/job_detail.rb', line 19 def process @process end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
19 20 21 |
# File 'app/models/flightdeck/job_detail.rb', line 19 def state @state end |
Class Method Details
Instance Method Details
#active_job_id ⇒ Object
39 |
# File 'app/models/flightdeck/job_detail.rb', line 39 def active_job_id = job.active_job_id |
#app_frame?(line) ⇒ Boolean
99 100 101 |
# File 'app/models/flightdeck/job_detail.rb', line 99 def app_frame?(line) !VENDOR_FRAME.match?(line.to_s) end |
#arguments_bytes ⇒ Object
75 |
# File 'app/models/flightdeck/job_detail.rb', line 75 def arguments_bytes = arguments_json.bytesize |
#arguments_json ⇒ Object
job.arguments is already a Ruby structure (Solid Queue stores it with a
JSON coder); pretty-printing it is a formatting step, not deserialization.
57 58 59 60 61 62 63 |
# File 'app/models/flightdeck/job_detail.rb', line 57 def arguments_json @arguments_json ||= begin JSON.pretty_generate(job.arguments) rescue StandardError job.arguments.to_s end end |
#arguments_preview ⇒ Object
69 70 71 72 73 |
# File 'app/models/flightdeck/job_detail.rb', line 69 def arguments_preview return arguments_json unless arguments_truncated? arguments_json.byteslice(0, ARGUMENTS_DISPLAY_LIMIT).scrub("") end |
#arguments_truncated? ⇒ Boolean
65 66 67 |
# File 'app/models/flightdeck/job_detail.rb', line 65 def arguments_truncated? arguments_json.bytesize > ARGUMENTS_DISPLAY_LIMIT end |
#backtrace ⇒ Object
87 88 89 |
# File 'app/models/flightdeck/job_detail.rb', line 87 def backtrace @backtrace ||= Array(failed_execution&.backtrace).first(Flightdeck.config.backtrace_lines) end |
#backtrace_frames ⇒ Object
95 96 97 |
# File 'app/models/flightdeck/job_detail.rb', line 95 def backtrace_frames backtrace.map { |line| { line: line, app: app_frame?(line) } } end |
#backtrace_total ⇒ Object
91 |
# File 'app/models/flightdeck/job_detail.rb', line 91 def backtrace_total = Array(failed_execution&.backtrace).size |
#backtrace_truncated? ⇒ Boolean
93 |
# File 'app/models/flightdeck/job_detail.rb', line 93 def backtrace_truncated? = backtrace_total > backtrace.size |
#class_name ⇒ Object
36 |
# File 'app/models/flightdeck/job_detail.rb', line 36 def class_name = job.class_name |
#concurrency_key ⇒ Object
40 |
# File 'app/models/flightdeck/job_detail.rb', line 40 def concurrency_key = job.concurrency_key |
#discardable? ⇒ Boolean
47 |
# File 'app/models/flightdeck/job_detail.rb', line 47 def discardable? = state != :in_progress && state != :finished |
#enqueued_at ⇒ Object
41 |
# File 'app/models/flightdeck/job_detail.rb', line 41 def enqueued_at = job.created_at |
#error_class ⇒ Object
--- error --------------------------------------------------------------
79 80 81 |
# File 'app/models/flightdeck/job_detail.rb', line 79 def error_class failed_execution&.exception_class end |
#error_message ⇒ Object
83 84 85 |
# File 'app/models/flightdeck/job_detail.rb', line 83 def failed_execution&. end |
#failed? ⇒ Boolean
45 |
# File 'app/models/flightdeck/job_detail.rb', line 45 def failed? = state == :failed |
#failed_execution ⇒ Object
49 50 51 |
# File 'app/models/flightdeck/job_detail.rb', line 49 def failed_execution execution if failed? end |
#finished_at ⇒ Object
43 |
# File 'app/models/flightdeck/job_detail.rb', line 43 def finished_at = job.finished_at |
#id ⇒ Object
35 |
# File 'app/models/flightdeck/job_detail.rb', line 35 def id = job.id |
#priority ⇒ Object
38 |
# File 'app/models/flightdeck/job_detail.rb', line 38 def priority = job.priority |
#process_label ⇒ Object
136 137 138 139 140 |
# File 'app/models/flightdeck/job_detail.rb', line 136 def process_label return nil unless process [ process.name, ("pid #{process.pid}" if process.pid) ].compact.join(" ยท ") end |
#queue_name ⇒ Object
37 |
# File 'app/models/flightdeck/job_detail.rb', line 37 def queue_name = job.queue_name |
#retryable? ⇒ Boolean
46 |
# File 'app/models/flightdeck/job_detail.rb', line 46 def retryable? = failed? |
#scheduled_at ⇒ Object
42 |
# File 'app/models/flightdeck/job_detail.rb', line 42 def scheduled_at = job.scheduled_at |
#timeline ⇒ Object
Built only from timestamps Solid Queue actually keeps. Steps whose row has already been consumed (a finished job's claim, say) are simply absent rather than invented.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'app/models/flightdeck/job_detail.rb', line 110 def timeline events = [ Event.new(label: "Enqueued", at: enqueued_at, detail: "queue #{queue_name}", status: :ok) ] if scheduled_at.present? && scheduled_at > enqueued_at events << Event.new(label: "Scheduled", at: scheduled_at, detail: scheduled_at.future? ? "not yet due" : "became due", status: :ok) end case state when :ready events << Event.new(label: "Ready", at: execution&.created_at, detail: "waiting for a worker", status: :now) when :blocked events << Event.new(label: "Blocked", at: execution&.created_at, detail: "concurrency key #{job.concurrency_key}", status: :now) when :in_progress events << Event.new(label: "Claimed", at: execution&.created_at, detail: process_label, status: :now) when :failed events << Event.new(label: "Failed", at: execution&.created_at, detail: error_class, status: :bad) events << Event.new(label: "Awaiting decision", at: nil, detail: "retry or discard", status: :now) end events << Event.new(label: "Finished", at: finished_at, detail: nil, status: :ok) if finished_at.present? events.select { |event| event.at.present? || event.status == :now } end |