Class: RoundhouseUi::Backends::SolidQueue

Inherits:
Object
  • Object
show all
Defined in:
lib/roundhouse_ui/backends/solid_queue.rb

Overview

Solid Queue implementation of the backend port. Reads Solid Queue's ActiveRecord tables and normalizes them to the same shape the Sidekiq backend exposes, so controllers/views don't change.

Solid Queue has no "retry set" (retries are just re-scheduled executions), no Redis, and no capsules — the UI hides those via #supports?. Queue pause is native, so there's no fetcher to warn about.

Defined Under Namespace

Classes: Entry, JobSet, Stats

Constant Summary collapse

CAPABILITIES =

No retries/redis/capsules/workers (deferred); pause is native (no fetcher warning). dead/scheduled/busy are supported. retry_job: SolidQueue::FailedExecution#retry exists, so dead-set retry works. Absent on purpose: :enqueue and :enqueue_now (push and add_to_queue are not implemented), :snapshots and :redis (both need Redis).

%i[dead scheduled busy native_pause retry_job].freeze

Instance Method Summary collapse

Instance Method Details

#busyObject

Currently-executing jobs, normalized to the Busy view's shape.



88
89
90
91
92
93
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 88

def busy
  sq(:ClaimedExecution).includes(:job, :process).map do |claim|
    e = Entry.new(claim)
    { process: claim.process&.name, tid: claim.id, queue: e.queue, run_at: claim.created_at, job: e }
  end
end

#concurrencyObject

The jobs waiting on one queue, in the same JobSet shape the other sets use — so the queue detail page reuses the shared browse/search path rather than growing a second one. Solid Queue's processes table records threads per worker the same way.



42
43
44
45
46
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 42

def concurrency
  sq(:Process).where(kind: "Worker").sum { |p| p.&.dig("thread_pool_size").to_i }
rescue StandardError
  nil
end

#dead_setObject



84
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 84

def dead_set      = JobSet.new(sq(:FailedExecution).includes(:job).order(created_at: :desc))

#nameObject



13
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 13

def name = "Solid Queue"

#pause(name) ⇒ Object



104
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 104

def pause(name)   = ::SolidQueue::Queue.new(name).pause

#paused_queuesObject

--- pause (native) ---



103
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 103

def paused_queues = ::SolidQueue::Pause.pluck(:queue_name)

#process_setObject

Workers view is deferred (supports?(:workers) == false), and Solid Queue processes don't report thread concurrency the way Metrics expects, so the utilization signal reads as "no workers reporting" rather than crashing.



82
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 82

def process_set = []

#push(_payload) ⇒ Object

ActiveJob enqueues via perform_later; a raw push has no direct analogue.

Raises:

  • (NotImplementedError)


108
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 108

def push(_payload) = raise NotImplementedError, "enqueue is Sidekiq-only for now"

#queue(name) ⇒ Object



36
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 36

def queue(name) = ::SolidQueue::Queue.new(name)

#queue_jobs(name) ⇒ Object



48
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 48

def queue_jobs(name) = JobSet.new(sq(:ReadyExecution).queued_as(name))

#queue_summariesObject

Depth and latency for every queue in two queries, not two per queue.

SolidQueue::Queue#size and #latency each run their own COUNT and MIN, so rendering the Queues page over sixty queues meant well over a hundred round-trips to the queue database. One GROUP BY answers both for every queue at once.

Queue.all is still consulted, because it lists queues by distinct job name — including ones with nothing ready right now — and an operator has to be able to pause or clear a queue that happens to be empty.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 60

def queue_summaries
  counts = sq(:ReadyExecution).group(:queue_name)
                              .pluck(Arel.sql("queue_name, COUNT(*), MIN(created_at)"))
                              .to_h { |name, size, oldest| [ name, [ size, oldest ] ] }

  names = (::SolidQueue::Queue.all.map(&:name) | counts.keys).sort
  now = Time.current
  names.map do |name|
    size, oldest = counts[name]
    RoundhouseUi::QueueSummary.new(
      name: name, size: size.to_i,
      latency: oldest ? (now - oldest).to_i : 0
    )
  end
end

#queuesObject



35
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 35

def queues = ::SolidQueue::Queue.all

#resume(name) ⇒ Object



105
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 105

def resume(name)  = ::SolidQueue::Queue.new(name).resume

#retry_setObject

No retry set in Solid Queue — surfaced via supports?(:retries) => false.



77
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 77

def retry_set = EMPTY_SET

#scheduled_setObject



85
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 85

def scheduled_set = JobSet.new(sq(:ScheduledExecution).includes(:job).order(:scheduled_at))

#set(kind) ⇒ Object



95
96
97
98
99
100
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 95

def set(kind)
  case kind.to_s
  when "dead"      then dead_set
  when "scheduled" then scheduled_set
  end
end

#statsObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 23

def stats
  Stats.new(
    processed: sq(:Job).where.not(finished_at: nil).count,
    failed:    sq(:FailedExecution).count,
    enqueued:  sq(:ReadyExecution).count,
    scheduled: sq(:ScheduledExecution).count,
    busy:      sq(:ClaimedExecution).count,
    dead:      sq(:FailedExecution).count,
    retries:   0
  )
end

#supports?(capability) ⇒ Boolean

Returns:

  • (Boolean)


21
# File 'lib/roundhouse_ui/backends/solid_queue.rb', line 21

def supports?(capability) = CAPABILITIES.include?(capability)