Class: Flightdeck::JobsQuery

Inherits:
Object
  • Object
show all
Defined in:
app/models/flightdeck/jobs_query.rb

Overview

Builds every jobs list in Flightdeck.

Rule zero: everything goes through SolidQueue's own models, so multi-database routing comes for free and the host's connection is never touched.

Two shapes of query, chosen by state:

* an execution-backed state drives from its own table (that is what makes
the state true) and then loads the matching jobs;
* "all" and "finished" drive from solid_queue_jobs and have their state
annotated afterwards.

The arguments and error columns are never in a list's driving query. Both are fetched afterwards for the current page only, and truncated in SQL so a page of pathological payloads still costs a bounded number of bytes.

Defined Under Namespace

Classes: InvalidState

Constant Summary collapse

STATES =
%i[all ready scheduled in_progress blocked finished failed].freeze
ARGUMENTS_PREVIEW_BYTES =

Enough that the ActiveJob envelope (job_class, job_id, queue_name…) is comfortably past and the arguments themselves are in the prefix.

800
ERROR_PREVIEW_BYTES =
1_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(state: :all, class_name: nil, queue_name: nil, q: nil, before_id: nil, limit: nil, count_cap: nil) ⇒ JobsQuery

Returns a new instance of JobsQuery.



30
31
32
33
34
35
36
37
38
39
# File 'app/models/flightdeck/jobs_query.rb', line 30

def initialize(state: :all, class_name: nil, queue_name: nil, q: nil, before_id: nil,
               limit: nil, count_cap: nil)
  @state = normalize_state(state)
  @class_name = class_name.presence
  @queue_name = queue_name.presence
  @q = q.presence
  @before_id = before_id.presence && Integer(before_id, exception: false)
  @limit = (limit || Flightdeck.config.per_page).to_i.clamp(1, 200)
  @count_cap = (count_cap || Flightdeck.config.count_cap).to_i
end

Instance Attribute Details

#before_idObject (readonly)

Returns the value of attribute before_id.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def before_id
  @before_id
end

#class_nameObject (readonly)

Returns the value of attribute class_name.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def class_name
  @class_name
end

#count_capObject (readonly)

Returns the value of attribute count_cap.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def count_cap
  @count_cap
end

#limitObject (readonly)

Returns the value of attribute limit.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def limit
  @limit
end

#qObject (readonly)

Returns the value of attribute q.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def q
  @q
end

#queue_nameObject (readonly)

Returns the value of attribute queue_name.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def queue_name
  @queue_name
end

#stateObject (readonly)

Returns the value of attribute state.



28
29
30
# File 'app/models/flightdeck/jobs_query.rb', line 28

def state
  @state
end

Class Method Details

.state_counts(**filters) ⇒ Object

Counts for the state tabs, each capped independently.



85
86
87
88
89
90
# File 'app/models/flightdeck/jobs_query.rb', line 85

def self.state_counts(**filters)
  STATES.index_with do |state|
    query = new(**filters.except(:state, :before_id), state: state)
    { count: query.count, capped: query.count_capped? }
  end
end

Instance Method Details

#countObject

Capped so that a jobs table with tens of millions of rows cannot turn a page render into a full scan. Views render count_capped? as "500,000+".

Cached for one poll interval: the state tabs and the sidebar issue several of these per render, and a count that is at most one poll stale is exactly as fresh as the page it appears on. Mutations wrap themselves in Cache.bypass so an action's own response never shows pre-action numbers.



59
60
61
62
63
64
# File 'app/models/flightdeck/jobs_query.rb', line 59

def count
  @count ||= Flightdeck::Cache.fetch("count", state, count_cap, cache_filters,
                                     expires_in: Flightdeck.config.poll_interval) do
    filtered_relation.limit(count_cap).count
  end
end

#count_capped?Boolean

Returns:

  • (Boolean)


66
# File 'app/models/flightdeck/jobs_query.rb', line 66

def count_capped? = count >= count_cap

#execution_backed?Boolean

Returns:

  • (Boolean)


82
# File 'app/models/flightdeck/jobs_query.rb', line 82

def execution_backed? = model != SolidQueue::Job

#filtered_relationObject

The filtered relation with no ordering, limit or cursor — what bulk "apply to all matching" re-runs server-side.



74
75
76
# File 'app/models/flightdeck/jobs_query.rb', line 74

def filtered_relation
  @filtered_relation ||= apply_filters(model.all)
end

#filters?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'app/models/flightdeck/jobs_query.rb', line 68

def filters?
  class_name.present? || queue_name.present? || q.present?
end

#modelObject



78
79
80
# File 'app/models/flightdeck/jobs_query.rb', line 78

def model
  @model ||= JobRow.model_for(state) || SolidQueue::Job
end

#next_cursorObject



45
46
47
48
# File 'app/models/flightdeck/jobs_query.rb', line 45

def next_cursor
  rows
  @next_cursor
end

#next_page?Boolean

Returns:

  • (Boolean)


50
# File 'app/models/flightdeck/jobs_query.rb', line 50

def next_page? = next_cursor.present?

#rowsObject



41
42
43
# File 'app/models/flightdeck/jobs_query.rb', line 41

def rows
  @rows ||= build_rows
end