solid_queue_tui 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/exe/sqtui +20 -1
- data/lib/solid_queue_tui/actions/discard_job.rb +4 -21
- data/lib/solid_queue_tui/actions/discard_scheduled_job.rb +5 -21
- data/lib/solid_queue_tui/actions/dispatch_scheduled_job.rb +4 -23
- data/lib/solid_queue_tui/actions/enqueue_recurring_task.rb +12 -0
- data/lib/solid_queue_tui/actions/retry_job.rb +14 -59
- data/lib/solid_queue_tui/actions/toggle_queue_pause.rb +19 -0
- data/lib/solid_queue_tui/application.rb +73 -17
- data/lib/solid_queue_tui/cli.rb +15 -8
- data/lib/solid_queue_tui/components/header.rb +26 -27
- data/lib/solid_queue_tui/components/help_bar.rb +1 -0
- data/lib/solid_queue_tui/components/job_table.rb +14 -2
- data/lib/solid_queue_tui/data/failed_query.rb +37 -91
- data/lib/solid_queue_tui/data/jobs_query.rb +90 -123
- data/lib/solid_queue_tui/data/processes_query.rb +10 -34
- data/lib/solid_queue_tui/data/queues_query.rb +6 -15
- data/lib/solid_queue_tui/data/recurring_tasks_query.rb +36 -0
- data/lib/solid_queue_tui/data/stats.rb +9 -27
- data/lib/solid_queue_tui/railtie.rb +9 -0
- data/lib/solid_queue_tui/version.rb +1 -1
- data/lib/solid_queue_tui/views/blocked_view.rb +126 -46
- data/lib/solid_queue_tui/views/concerns/filterable.rb +128 -0
- data/lib/solid_queue_tui/views/dashboard_view.rb +2 -1
- data/lib/solid_queue_tui/views/failed_view.rb +62 -72
- data/lib/solid_queue_tui/views/finished_view.rb +54 -67
- data/lib/solid_queue_tui/views/in_progress_view.rb +124 -44
- data/lib/solid_queue_tui/views/queues_view.rb +119 -35
- data/lib/solid_queue_tui/views/recurring_tasks_view.rb +202 -0
- data/lib/solid_queue_tui/views/scheduled_view.rb +74 -8
- data/lib/solid_queue_tui.rb +15 -4
- data/lib/tasks/solid_queue_tui.rake +8 -0
- metadata +16 -24
- data/lib/solid_queue_tui/connection.rb +0 -58
|
@@ -10,109 +10,55 @@ module SolidQueueTui
|
|
|
10
10
|
keyword_init: true
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
def self.fetch(filter: nil, limit:
|
|
14
|
-
|
|
13
|
+
def self.fetch(filter: nil, queue: nil, limit: 100, offset: 0)
|
|
14
|
+
scope = SolidQueue::FailedExecution.joins(:job).includes(:job)
|
|
15
|
+
scope = scope.merge(SolidQueue::Job.where("class_name LIKE ?", "%#{filter}%")) if filter.present?
|
|
16
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue.present?
|
|
17
|
+
scope = scope.order(created_at: :desc).offset(offset).limit(limit)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
SELECT
|
|
18
|
-
fe.id,
|
|
19
|
-
fe.job_id,
|
|
20
|
-
j.queue_name,
|
|
21
|
-
j.class_name,
|
|
22
|
-
j.priority,
|
|
23
|
-
j.active_job_id,
|
|
24
|
-
j.arguments,
|
|
25
|
-
j.created_at AS job_created_at,
|
|
26
|
-
fe.error,
|
|
27
|
-
fe.created_at AS failed_at
|
|
28
|
-
FROM solid_queue_failed_executions fe
|
|
29
|
-
JOIN solid_queue_jobs j ON j.id = fe.job_id
|
|
30
|
-
SQL
|
|
31
|
-
|
|
32
|
-
if filter && !filter.empty?
|
|
33
|
-
sql += " WHERE j.class_name LIKE #{conn.quote("%#{filter}%")}"
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
sql += " ORDER BY fe.created_at DESC LIMIT #{limit.to_i}"
|
|
37
|
-
|
|
38
|
-
rows = conn.select_all(sql)
|
|
39
|
-
rows.map do |row|
|
|
40
|
-
error = parse_json(row["error"])
|
|
41
|
-
|
|
42
|
-
FailedJob.new(
|
|
43
|
-
id: row["id"].to_i,
|
|
44
|
-
job_id: row["job_id"].to_i,
|
|
45
|
-
queue_name: row["queue_name"],
|
|
46
|
-
class_name: row["class_name"],
|
|
47
|
-
priority: row["priority"].to_i,
|
|
48
|
-
error_class: error["exception_class"] || error["class"] || "Unknown",
|
|
49
|
-
error_message: error["message"] || "No message",
|
|
50
|
-
backtrace: error["backtrace"] || [],
|
|
51
|
-
active_job_id: row["active_job_id"],
|
|
52
|
-
arguments: parse_json(row["arguments"]),
|
|
53
|
-
failed_at: parse_time(row["failed_at"]),
|
|
54
|
-
created_at: parse_time(row["job_created_at"])
|
|
55
|
-
)
|
|
56
|
-
end
|
|
19
|
+
scope.map { |fe| build_failed_job(fe) }
|
|
57
20
|
rescue => e
|
|
21
|
+
Rails.logger.tagged("SQTUI") { Rails.logger.error("FailedQuery.fetch error: #{e.class}: #{e.message}") } if defined?(Rails) && Rails.logger
|
|
58
22
|
[]
|
|
59
23
|
end
|
|
60
24
|
|
|
61
|
-
def self.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
j.priority,
|
|
71
|
-
j.active_job_id,
|
|
72
|
-
j.arguments,
|
|
73
|
-
j.created_at AS job_created_at,
|
|
74
|
-
fe.error,
|
|
75
|
-
fe.created_at AS failed_at
|
|
76
|
-
FROM solid_queue_failed_executions fe
|
|
77
|
-
JOIN solid_queue_jobs j ON j.id = fe.job_id
|
|
78
|
-
WHERE fe.id = #{conn.quote(id.to_i)}
|
|
79
|
-
SQL
|
|
80
|
-
|
|
81
|
-
return nil unless row
|
|
25
|
+
def self.count(filter: nil, queue: nil)
|
|
26
|
+
scope = SolidQueue::FailedExecution.joins(:job)
|
|
27
|
+
scope = scope.merge(SolidQueue::Job.where("class_name LIKE ?", "%#{filter}%")) if filter.present?
|
|
28
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue.present?
|
|
29
|
+
scope.count
|
|
30
|
+
rescue => e
|
|
31
|
+
Rails.logger.tagged("SQTUI") { Rails.logger.error("FailedQuery.count error: #{e.class}: #{e.message}") } if defined?(Rails) && Rails.logger
|
|
32
|
+
0
|
|
33
|
+
end
|
|
82
34
|
|
|
83
|
-
|
|
35
|
+
def self.fetch_one(id)
|
|
36
|
+
fe = SolidQueue::FailedExecution.includes(:job).find_by(id: id)
|
|
37
|
+
return nil unless fe
|
|
84
38
|
|
|
85
|
-
|
|
86
|
-
id: row["id"].to_i,
|
|
87
|
-
job_id: row["job_id"].to_i,
|
|
88
|
-
queue_name: row["queue_name"],
|
|
89
|
-
class_name: row["class_name"],
|
|
90
|
-
priority: row["priority"].to_i,
|
|
91
|
-
error_class: error["exception_class"] || error["class"] || "Unknown",
|
|
92
|
-
error_message: error["message"] || "No message",
|
|
93
|
-
backtrace: error["backtrace"] || [],
|
|
94
|
-
active_job_id: row["active_job_id"],
|
|
95
|
-
arguments: parse_json(row["arguments"]),
|
|
96
|
-
failed_at: parse_time(row["failed_at"]),
|
|
97
|
-
created_at: parse_time(row["job_created_at"])
|
|
98
|
-
)
|
|
39
|
+
build_failed_job(fe)
|
|
99
40
|
rescue => e
|
|
100
41
|
nil
|
|
101
42
|
end
|
|
102
43
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
44
|
+
def self.build_failed_job(fe)
|
|
45
|
+
job = fe.job
|
|
46
|
+
FailedJob.new(
|
|
47
|
+
id: fe.id,
|
|
48
|
+
job_id: job.id,
|
|
49
|
+
queue_name: job.queue_name,
|
|
50
|
+
class_name: job.class_name,
|
|
51
|
+
priority: job.priority,
|
|
52
|
+
error_class: fe.exception_class || "Unknown",
|
|
53
|
+
error_message: fe.message || "No message",
|
|
54
|
+
backtrace: fe.backtrace || [],
|
|
55
|
+
active_job_id: job.active_job_id,
|
|
56
|
+
arguments: job.arguments,
|
|
57
|
+
failed_at: fe.created_at,
|
|
58
|
+
created_at: job.created_at
|
|
59
|
+
)
|
|
115
60
|
end
|
|
61
|
+
private_class_method :build_failed_job
|
|
116
62
|
end
|
|
117
63
|
end
|
|
118
64
|
end
|
|
@@ -11,167 +11,134 @@ module SolidQueueTui
|
|
|
11
11
|
keyword_init: true
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
def self.fetch(status:, filter: nil, queue: nil, limit:
|
|
14
|
+
def self.fetch(status:, filter: nil, queue: nil, limit: 100, offset: 0)
|
|
15
15
|
case status
|
|
16
|
-
when "claimed" then fetch_claimed(filter: filter, queue: queue, limit: limit)
|
|
17
|
-
when "blocked" then fetch_blocked(filter: filter, queue: queue, limit: limit)
|
|
18
|
-
when "scheduled" then fetch_scheduled(filter: filter, queue: queue, limit: limit)
|
|
19
|
-
when "completed" then fetch_finished(filter: filter, queue: queue, limit: limit)
|
|
16
|
+
when "claimed" then fetch_claimed(filter: filter, queue: queue, limit: limit, offset: offset)
|
|
17
|
+
when "blocked" then fetch_blocked(filter: filter, queue: queue, limit: limit, offset: offset)
|
|
18
|
+
when "scheduled" then fetch_scheduled(filter: filter, queue: queue, limit: limit, offset: offset)
|
|
19
|
+
when "completed" then fetch_finished(filter: filter, queue: queue, limit: limit, offset: offset)
|
|
20
20
|
else []
|
|
21
21
|
end
|
|
22
22
|
rescue => e
|
|
23
23
|
[]
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def self.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
SQL
|
|
38
|
-
|
|
39
|
-
conditions = []
|
|
40
|
-
conditions << "j.queue_name = #{conn.quote(queue)}" if queue
|
|
41
|
-
conditions << "j.class_name LIKE #{conn.quote("%#{filter}%")}" if filter && !filter.empty?
|
|
26
|
+
def self.count(status:, filter: nil, queue: nil)
|
|
27
|
+
case status
|
|
28
|
+
when "claimed" then count_scope(SolidQueue::ClaimedExecution.joins(:job), filter: filter, queue: queue)
|
|
29
|
+
when "blocked" then count_scope(SolidQueue::BlockedExecution.joins(:job), filter: filter, queue: queue)
|
|
30
|
+
when "scheduled" then count_scope(SolidQueue::ScheduledExecution.joins(:job), filter: filter, queue: queue)
|
|
31
|
+
when "completed" then count_finished(filter: filter, queue: queue)
|
|
32
|
+
else 0
|
|
33
|
+
end
|
|
34
|
+
rescue => e
|
|
35
|
+
0
|
|
36
|
+
end
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
def self.fetch_claimed(filter: nil, queue: nil, limit: 100, offset: 0)
|
|
39
|
+
scope = SolidQueue::ClaimedExecution.joins(:job)
|
|
40
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue
|
|
41
|
+
scope = apply_class_name_filter(scope, filter)
|
|
42
|
+
scope = scope.order(job_id: :asc).offset(offset).limit(limit)
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
scope.includes(:job).map do |ce|
|
|
45
|
+
job = ce.job
|
|
47
46
|
Job.new(
|
|
48
|
-
id:
|
|
49
|
-
queue_name:
|
|
50
|
-
class_name:
|
|
51
|
-
priority:
|
|
47
|
+
id: job.id,
|
|
48
|
+
queue_name: job.queue_name,
|
|
49
|
+
class_name: job.class_name,
|
|
50
|
+
priority: job.priority,
|
|
52
51
|
status: "claimed",
|
|
53
|
-
active_job_id:
|
|
54
|
-
concurrency_key:
|
|
55
|
-
created_at:
|
|
56
|
-
worker_id:
|
|
57
|
-
started_at:
|
|
52
|
+
active_job_id: job.active_job_id,
|
|
53
|
+
concurrency_key: job.concurrency_key,
|
|
54
|
+
created_at: job.created_at,
|
|
55
|
+
worker_id: ce.process_id,
|
|
56
|
+
started_at: ce.created_at
|
|
58
57
|
)
|
|
59
58
|
end
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
def self.fetch_blocked(filter: nil, queue: nil, limit:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
j.id, j.queue_name, j.class_name, j.priority,
|
|
68
|
-
j.active_job_id, j.concurrency_key, j.created_at,
|
|
69
|
-
be.expires_at,
|
|
70
|
-
be.created_at AS blocked_since
|
|
71
|
-
FROM solid_queue_blocked_executions be
|
|
72
|
-
JOIN solid_queue_jobs j ON j.id = be.job_id
|
|
73
|
-
SQL
|
|
74
|
-
|
|
75
|
-
conditions = []
|
|
76
|
-
conditions << "j.queue_name = #{conn.quote(queue)}" if queue
|
|
77
|
-
conditions << "j.class_name LIKE #{conn.quote("%#{filter}%")}" if filter && !filter.empty?
|
|
61
|
+
def self.fetch_blocked(filter: nil, queue: nil, limit: 100, offset: 0)
|
|
62
|
+
scope = SolidQueue::BlockedExecution.joins(:job)
|
|
63
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue
|
|
64
|
+
scope = apply_class_name_filter(scope, filter)
|
|
65
|
+
scope = scope.order(job_id: :asc).offset(offset).limit(limit)
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
conn.select_all(sql).map do |row|
|
|
67
|
+
scope.includes(:job).map do |be|
|
|
68
|
+
job = be.job
|
|
83
69
|
Job.new(
|
|
84
|
-
id:
|
|
85
|
-
queue_name:
|
|
86
|
-
class_name:
|
|
87
|
-
priority:
|
|
70
|
+
id: job.id,
|
|
71
|
+
queue_name: job.queue_name,
|
|
72
|
+
class_name: job.class_name,
|
|
73
|
+
priority: job.priority,
|
|
88
74
|
status: "blocked",
|
|
89
|
-
active_job_id:
|
|
90
|
-
concurrency_key:
|
|
91
|
-
created_at:
|
|
92
|
-
expires_at:
|
|
75
|
+
active_job_id: job.active_job_id,
|
|
76
|
+
concurrency_key: job.concurrency_key,
|
|
77
|
+
created_at: be.created_at,
|
|
78
|
+
expires_at: be.expires_at
|
|
93
79
|
)
|
|
94
80
|
end
|
|
95
81
|
end
|
|
96
82
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
SELECT
|
|
103
|
-
j.id, j.queue_name, j.class_name, j.priority,
|
|
104
|
-
j.active_job_id, j.arguments, j.created_at,
|
|
105
|
-
se.scheduled_at
|
|
106
|
-
FROM solid_queue_scheduled_executions se
|
|
107
|
-
JOIN solid_queue_jobs j ON j.id = se.job_id
|
|
108
|
-
SQL
|
|
109
|
-
|
|
110
|
-
conditions = []
|
|
111
|
-
conditions << "j.queue_name = #{conn.quote(queue)}" if queue
|
|
112
|
-
conditions << "j.class_name LIKE #{conn.quote("%#{filter}%")}" if filter && !filter.empty?
|
|
113
|
-
|
|
114
|
-
sql += " WHERE #{conditions.join(' AND ')}" unless conditions.empty?
|
|
115
|
-
sql += " ORDER BY se.scheduled_at ASC, se.priority ASC LIMIT #{limit.to_i}"
|
|
83
|
+
def self.fetch_scheduled(filter: nil, queue: nil, limit: 100, offset: 0)
|
|
84
|
+
scope = SolidQueue::ScheduledExecution.joins(:job)
|
|
85
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue
|
|
86
|
+
scope = apply_class_name_filter(scope, filter)
|
|
87
|
+
scope = scope.order(scheduled_at: :asc, priority: :asc).offset(offset).limit(limit)
|
|
116
88
|
|
|
117
|
-
|
|
89
|
+
scope.includes(:job).map do |se|
|
|
90
|
+
job = se.job
|
|
118
91
|
Job.new(
|
|
119
|
-
id:
|
|
120
|
-
queue_name:
|
|
121
|
-
class_name:
|
|
122
|
-
priority:
|
|
92
|
+
id: job.id,
|
|
93
|
+
queue_name: job.queue_name,
|
|
94
|
+
class_name: job.class_name,
|
|
95
|
+
priority: job.priority,
|
|
123
96
|
status: "scheduled",
|
|
124
|
-
active_job_id:
|
|
125
|
-
arguments:
|
|
126
|
-
scheduled_at:
|
|
127
|
-
created_at:
|
|
97
|
+
active_job_id: job.active_job_id,
|
|
98
|
+
arguments: job.arguments,
|
|
99
|
+
scheduled_at: se.scheduled_at,
|
|
100
|
+
created_at: job.created_at
|
|
128
101
|
)
|
|
129
102
|
end
|
|
130
103
|
end
|
|
131
104
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
105
|
+
def self.fetch_finished(filter: nil, queue: nil, limit: 100, offset: 0)
|
|
106
|
+
scope = SolidQueue::Job.finished
|
|
107
|
+
scope = scope.where(queue_name: queue) if queue
|
|
108
|
+
scope = scope.where("class_name LIKE ?", "%#{filter}%") if filter.present?
|
|
109
|
+
scope = scope.order(finished_at: :desc).offset(offset).limit(limit)
|
|
135
110
|
|
|
136
|
-
|
|
137
|
-
SELECT
|
|
138
|
-
j.id, j.queue_name, j.class_name, j.priority,
|
|
139
|
-
j.active_job_id, j.arguments, j.finished_at, j.created_at
|
|
140
|
-
FROM solid_queue_jobs j
|
|
141
|
-
WHERE j.finished_at IS NOT NULL
|
|
142
|
-
SQL
|
|
143
|
-
|
|
144
|
-
sql += " AND j.queue_name = #{conn.quote(queue)}" if queue
|
|
145
|
-
sql += " AND j.class_name LIKE #{conn.quote("%#{filter}%")}" if filter && !filter.empty?
|
|
146
|
-
sql += " ORDER BY j.finished_at DESC LIMIT #{limit.to_i}"
|
|
147
|
-
|
|
148
|
-
conn.select_all(sql).map do |row|
|
|
111
|
+
scope.map do |job|
|
|
149
112
|
Job.new(
|
|
150
|
-
id:
|
|
151
|
-
queue_name:
|
|
152
|
-
class_name:
|
|
153
|
-
priority:
|
|
113
|
+
id: job.id,
|
|
114
|
+
queue_name: job.queue_name,
|
|
115
|
+
class_name: job.class_name,
|
|
116
|
+
priority: job.priority,
|
|
154
117
|
status: "completed",
|
|
155
|
-
active_job_id:
|
|
156
|
-
arguments:
|
|
157
|
-
finished_at:
|
|
158
|
-
created_at:
|
|
118
|
+
active_job_id: job.active_job_id,
|
|
119
|
+
arguments: job.arguments,
|
|
120
|
+
finished_at: job.finished_at,
|
|
121
|
+
created_at: job.created_at
|
|
159
122
|
)
|
|
160
123
|
end
|
|
161
124
|
end
|
|
162
125
|
|
|
163
|
-
private_class_method def self.
|
|
164
|
-
return
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
126
|
+
private_class_method def self.apply_class_name_filter(scope, filter)
|
|
127
|
+
return scope if filter.blank?
|
|
128
|
+
scope.merge(SolidQueue::Job.where("class_name LIKE ?", "%#{filter}%"))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private_class_method def self.count_scope(scope, filter: nil, queue: nil)
|
|
132
|
+
scope = scope.merge(SolidQueue::Job.where(queue_name: queue)) if queue
|
|
133
|
+
scope = apply_class_name_filter(scope, filter)
|
|
134
|
+
scope.count
|
|
168
135
|
end
|
|
169
136
|
|
|
170
|
-
private_class_method def self.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
137
|
+
private_class_method def self.count_finished(filter: nil, queue: nil)
|
|
138
|
+
scope = SolidQueue::Job.finished
|
|
139
|
+
scope = scope.where(queue_name: queue) if queue
|
|
140
|
+
scope = scope.where("class_name LIKE ?", "%#{filter}%") if filter.present?
|
|
141
|
+
scope.count
|
|
175
142
|
end
|
|
176
143
|
end
|
|
177
144
|
end
|
|
@@ -30,46 +30,22 @@ module SolidQueueTui
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.fetch
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
rows = conn.select_all(
|
|
36
|
-
"SELECT id, kind, pid, hostname, name, last_heartbeat_at, " \
|
|
37
|
-
"supervisor_id, metadata, created_at " \
|
|
38
|
-
"FROM solid_queue_processes WHERE kind = 'Worker' ORDER BY id"
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
rows.map do |row|
|
|
42
|
-
metadata = parse_json(row["metadata"])
|
|
43
|
-
|
|
33
|
+
SolidQueue::Process.where(kind: "Worker").order(:id).map do |proc|
|
|
44
34
|
Process.new(
|
|
45
|
-
id:
|
|
46
|
-
kind:
|
|
47
|
-
pid:
|
|
48
|
-
hostname:
|
|
49
|
-
name:
|
|
50
|
-
last_heartbeat_at:
|
|
51
|
-
supervisor_id:
|
|
52
|
-
metadata: metadata,
|
|
53
|
-
created_at:
|
|
35
|
+
id: proc.id,
|
|
36
|
+
kind: proc.kind,
|
|
37
|
+
pid: proc.pid,
|
|
38
|
+
hostname: proc.hostname,
|
|
39
|
+
name: proc.name,
|
|
40
|
+
last_heartbeat_at: proc.last_heartbeat_at,
|
|
41
|
+
supervisor_id: proc.supervisor_id,
|
|
42
|
+
metadata: proc.metadata,
|
|
43
|
+
created_at: proc.created_at
|
|
54
44
|
)
|
|
55
45
|
end
|
|
56
46
|
rescue => e
|
|
57
47
|
[]
|
|
58
48
|
end
|
|
59
|
-
|
|
60
|
-
private_class_method def self.parse_json(value)
|
|
61
|
-
return {} if value.nil?
|
|
62
|
-
value.is_a?(Hash) ? value : JSON.parse(value.to_s)
|
|
63
|
-
rescue JSON::ParserError
|
|
64
|
-
{}
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
private_class_method def self.parse_time(value)
|
|
68
|
-
return nil if value.nil?
|
|
69
|
-
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
70
|
-
rescue
|
|
71
|
-
nil
|
|
72
|
-
end
|
|
73
49
|
end
|
|
74
50
|
end
|
|
75
51
|
end
|
|
@@ -9,23 +9,14 @@ module SolidQueueTui
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
def self.fetch
|
|
12
|
-
|
|
12
|
+
queues = SolidQueue::Queue.all
|
|
13
|
+
pauses = SolidQueue::Pause.where(queue_name: queues.map(&:name)).index_by(&:queue_name)
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
"SELECT queue_name, COUNT(*) FROM solid_queue_ready_executions GROUP BY queue_name ORDER BY queue_name"
|
|
16
|
-
).to_h { |name, count| [name, count.to_i] }
|
|
17
|
-
|
|
18
|
-
all_queues = conn.select_values(
|
|
19
|
-
"SELECT DISTINCT queue_name FROM solid_queue_jobs WHERE queue_name IS NOT NULL ORDER BY queue_name"
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
paused = conn.select_values("SELECT queue_name FROM solid_queue_pauses")
|
|
23
|
-
|
|
24
|
-
all_queues.map do |name|
|
|
15
|
+
queues.map do |queue|
|
|
25
16
|
QueueInfo.new(
|
|
26
|
-
name: name,
|
|
27
|
-
size:
|
|
28
|
-
paused:
|
|
17
|
+
name: queue.name,
|
|
18
|
+
size: queue.size,
|
|
19
|
+
paused: pauses[queue.name].present?
|
|
29
20
|
)
|
|
30
21
|
end
|
|
31
22
|
rescue => e
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidQueueTui
|
|
4
|
+
module Data
|
|
5
|
+
class RecurringTasksQuery
|
|
6
|
+
Task = Struct.new(
|
|
7
|
+
:key, :class_name, :command, :schedule, :queue_name,
|
|
8
|
+
:priority, :last_enqueued_at, :next_time,
|
|
9
|
+
keyword_init: true
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
def self.fetch
|
|
13
|
+
tasks = SolidQueue::RecurringTask.all.to_a
|
|
14
|
+
return [] if tasks.empty?
|
|
15
|
+
|
|
16
|
+
last_enqueued = SolidQueue::RecurringExecution
|
|
17
|
+
.where(task_key: tasks.map(&:key))
|
|
18
|
+
.group(:task_key)
|
|
19
|
+
.maximum(:run_at)
|
|
20
|
+
|
|
21
|
+
tasks.map do |task|
|
|
22
|
+
Task.new(
|
|
23
|
+
key: task.key,
|
|
24
|
+
class_name: task.class_name,
|
|
25
|
+
command: task.command,
|
|
26
|
+
schedule: task.schedule,
|
|
27
|
+
queue_name: task.queue_name,
|
|
28
|
+
priority: task.priority,
|
|
29
|
+
last_enqueued_at: last_enqueued[task.key],
|
|
30
|
+
next_time: task.next_time
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -20,18 +20,16 @@ module SolidQueueTui
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.fetch
|
|
23
|
-
conn = ActiveRecord::Base.connection
|
|
24
|
-
|
|
25
23
|
new(
|
|
26
|
-
ready:
|
|
27
|
-
claimed:
|
|
28
|
-
failed:
|
|
29
|
-
scheduled:
|
|
30
|
-
blocked:
|
|
31
|
-
total_jobs:
|
|
32
|
-
completed_jobs:
|
|
33
|
-
process_count:
|
|
34
|
-
processes_by_kind:
|
|
24
|
+
ready: SolidQueue::ReadyExecution.count,
|
|
25
|
+
claimed: SolidQueue::ClaimedExecution.count,
|
|
26
|
+
failed: SolidQueue::FailedExecution.count,
|
|
27
|
+
scheduled: SolidQueue::ScheduledExecution.count,
|
|
28
|
+
blocked: SolidQueue::BlockedExecution.count,
|
|
29
|
+
total_jobs: SolidQueue::Job.count,
|
|
30
|
+
completed_jobs: SolidQueue::Job.finished.count,
|
|
31
|
+
process_count: SolidQueue::Process.count,
|
|
32
|
+
processes_by_kind: SolidQueue::Process.group(:kind).count
|
|
35
33
|
)
|
|
36
34
|
rescue => e
|
|
37
35
|
empty(error: e.message)
|
|
@@ -44,22 +42,6 @@ module SolidQueueTui
|
|
|
44
42
|
processes_by_kind: {}
|
|
45
43
|
)
|
|
46
44
|
end
|
|
47
|
-
|
|
48
|
-
private_class_method def self.count_table(conn, table)
|
|
49
|
-
conn.select_value("SELECT COUNT(*) FROM #{table}").to_i
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private_class_method def self.count_where(conn, table, condition)
|
|
53
|
-
conn.select_value("SELECT COUNT(*) FROM #{table} WHERE #{condition}").to_i
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
private_class_method def self.processes_by_kind(conn)
|
|
57
|
-
rows = conn.select_rows(
|
|
58
|
-
"SELECT kind, COUNT(*) FROM solid_queue_processes GROUP BY kind"
|
|
59
|
-
)
|
|
60
|
-
rows.to_h { |kind, count| [kind, count.to_i] }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
45
|
end
|
|
64
46
|
end
|
|
65
47
|
end
|