pgbus 0.2.4 → 0.2.6
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/README.md +21 -1
- data/app/controllers/pgbus/application_controller.rb +13 -4
- data/app/helpers/pgbus/application_helper.rb +10 -0
- data/app/models/pgbus/application_record.rb +4 -1
- data/app/models/pgbus/batch_entry.rb +1 -1
- data/app/models/pgbus/blocked_execution.rb +1 -1
- data/app/models/pgbus/job_lock.rb +1 -1
- data/app/models/pgbus/job_stat.rb +1 -1
- data/app/models/pgbus/outbox_entry.rb +1 -1
- data/app/models/pgbus/process_entry.rb +1 -1
- data/app/models/pgbus/processed_event.rb +1 -1
- data/app/models/pgbus/queue_state.rb +1 -1
- data/app/models/pgbus/recurring_execution.rb +1 -1
- data/app/models/pgbus/recurring_task.rb +1 -1
- data/app/models/pgbus/semaphore.rb +1 -1
- data/app/views/layouts/pgbus/application.html.erb +104 -15
- data/app/views/pgbus/dashboard/_processes_table.html.erb +5 -5
- data/app/views/pgbus/dashboard/_queues_table.html.erb +6 -6
- data/app/views/pgbus/dashboard/_recent_failures.html.erb +4 -4
- data/app/views/pgbus/dead_letter/_messages_table.html.erb +1 -1
- data/app/views/pgbus/events/index.html.erb +8 -8
- data/app/views/pgbus/insights/show.html.erb +31 -29
- data/app/views/pgbus/jobs/_enqueued_table.html.erb +1 -1
- data/app/views/pgbus/jobs/_failed_table.html.erb +7 -7
- data/app/views/pgbus/locks/index.html.erb +7 -7
- data/app/views/pgbus/outbox/index.html.erb +7 -7
- data/app/views/pgbus/processes/_processes_table.html.erb +7 -7
- data/app/views/pgbus/queues/_queues_list.html.erb +8 -8
- data/app/views/pgbus/recurring_tasks/_tasks_table.html.erb +8 -8
- data/config/locales/da.yml +2 -0
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fi.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/ja.yml +2 -0
- data/config/locales/nb.yml +2 -0
- data/config/locales/nl.yml +2 -0
- data/config/locales/pt.yml +2 -0
- data/config/locales/sv.yml +2 -0
- data/lib/pgbus/bus_record.rb +16 -0
- data/lib/pgbus/configuration.rb +4 -2
- data/lib/pgbus/engine.rb +1 -1
- data/lib/pgbus/process/consumer_priority.rb +1 -1
- data/lib/pgbus/process/dispatcher.rb +1 -1
- data/lib/pgbus/process/queue_lock.rb +1 -1
- data/lib/pgbus/process/worker.rb +1 -1
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +1 -1
- data/lib/pgbus.rb +4 -0
- data/lib/tasks/pgbus_pgmq.rake +1 -1
- metadata +2 -1
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<div class="px-5 py-4 border-b border-gray-200 dark:border-gray-700">
|
|
69
69
|
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300"><%= t("pgbus.insights.show.slowest.title") %></h3>
|
|
70
70
|
</div>
|
|
71
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
71
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
72
72
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
73
73
|
<tr>
|
|
74
74
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500 dark:text-gray-400"><%= t("pgbus.insights.show.slowest.headers.job_class") %></th>
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
81
81
|
<% @slowest.each do |row| %>
|
|
82
82
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
83
|
-
<td class="px-4 py-3 text-sm font-medium text-gray-700 dark:text-gray-300"><%= row[:job_class] %></td>
|
|
84
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_number(row[:count]) %></td>
|
|
85
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:avg_ms]) %></td>
|
|
86
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:max_ms]) %></td>
|
|
83
|
+
<td data-label="Job Class" class="px-4 py-3 text-sm font-medium text-gray-700 dark:text-gray-300"><%= row[:job_class] %></td>
|
|
84
|
+
<td data-label="Count" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_number(row[:count]) %></td>
|
|
85
|
+
<td data-label="Avg" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:avg_ms]) %></td>
|
|
86
|
+
<td data-label="Max" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:max_ms]) %></td>
|
|
87
87
|
</tr>
|
|
88
88
|
<% end %>
|
|
89
89
|
<% if @slowest.empty? %>
|
|
@@ -95,12 +95,14 @@
|
|
|
95
95
|
|
|
96
96
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts@4"></script>
|
|
97
97
|
<script>
|
|
98
|
-
|
|
98
|
+
(function() {
|
|
99
|
+
// IIFE prevents "redeclaration of let" when Turbo re-executes on navigation
|
|
100
|
+
var throughputChart, statusChart;
|
|
99
101
|
|
|
100
102
|
function getThemeColors() {
|
|
101
|
-
|
|
103
|
+
var isDark = document.documentElement.classList.contains('dark');
|
|
102
104
|
return {
|
|
103
|
-
isDark,
|
|
105
|
+
isDark: isDark,
|
|
104
106
|
text: isDark ? '#9ca3af' : '#6b7280',
|
|
105
107
|
grid: isDark ? '#374151' : '#e5e7eb',
|
|
106
108
|
tooltip: isDark ? 'dark' : 'light',
|
|
@@ -109,16 +111,14 @@
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
function renderCharts(data) {
|
|
112
|
-
|
|
114
|
+
var t = getThemeColors();
|
|
113
115
|
|
|
114
|
-
// Destroy existing charts before re-rendering
|
|
115
116
|
if (throughputChart) throughputChart.destroy();
|
|
116
117
|
if (statusChart) statusChart.destroy();
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}));
|
|
119
|
+
var throughputData = data.throughput.map(function(p) {
|
|
120
|
+
return { x: new Date(p.time).getTime(), y: p.count };
|
|
121
|
+
});
|
|
122
122
|
|
|
123
123
|
throughputChart = new ApexCharts(document.querySelector('#throughput-chart'), {
|
|
124
124
|
series: [{ name: '<%= j(t("pgbus.insights.show.charts.series_name")) %>', data: throughputData }],
|
|
@@ -134,10 +134,9 @@
|
|
|
134
134
|
});
|
|
135
135
|
throughputChart.render();
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const statusColors = statusLabels.map(s => {
|
|
137
|
+
var statusLabels = Object.keys(data.status_counts);
|
|
138
|
+
var statusValues = Object.values(data.status_counts);
|
|
139
|
+
var statusColors = statusLabels.map(function(s) {
|
|
141
140
|
if (s === 'success') return '#10b981';
|
|
142
141
|
if (s === 'failed') return '#ef4444';
|
|
143
142
|
if (s === 'dead_lettered') return '#f97316';
|
|
@@ -161,21 +160,24 @@
|
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
let chartData = null;
|
|
163
|
+
var chartData = null;
|
|
166
164
|
fetch('<%= pgbus.api_insights_path(minutes: @minutes) %>')
|
|
167
|
-
.then(r
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
165
|
+
.then(function(r) {
|
|
166
|
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
167
|
+
return r.json();
|
|
168
|
+
})
|
|
169
|
+
.then(function(data) { chartData = data; renderCharts(data); })
|
|
170
|
+
.catch(function(err) {
|
|
171
|
+
if (err.name === 'AbortError') return;
|
|
172
|
+
var msg = '<p class="text-center text-sm text-gray-400 dark:text-gray-500 pt-24"><%= j(t("pgbus.insights.show.charts.failed_to_load")) %></p>';
|
|
173
|
+
var el1 = document.querySelector('#throughput-chart');
|
|
174
|
+
var el2 = document.querySelector('#status-chart');
|
|
175
|
+
if (el1) el1.innerHTML = msg;
|
|
176
|
+
if (el2) el2.innerHTML = msg;
|
|
173
177
|
});
|
|
174
178
|
|
|
175
|
-
// Re-render charts when dark mode toggles.
|
|
176
|
-
// Listen for class changes on <html> instead of wrapping the toggle function,
|
|
177
|
-
// so it works regardless of script loading order.
|
|
178
179
|
new MutationObserver(function() {
|
|
179
180
|
if (chartData) renderCharts(chartData);
|
|
180
181
|
}).observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
|
182
|
+
})();
|
|
181
183
|
</script>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3"><%= t("pgbus.jobs.enqueued_table.title") %></h2>
|
|
4
4
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
5
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
5
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
6
6
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
7
7
|
<tr>
|
|
8
8
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.jobs.enqueued_table.headers.id") %></th>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="mb-8">
|
|
3
3
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3"><%= t("pgbus.jobs.failed_table.title") %></h2>
|
|
4
4
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
5
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
5
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
6
6
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
7
7
|
<tr>
|
|
8
8
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.jobs.failed_table.headers.id") %></th>
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
17
17
|
<% @failed.each do |f| %>
|
|
18
18
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
19
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-900 dark:text-white">
|
|
19
|
+
<td data-label="ID" class="px-4 py-3 text-sm font-mono text-gray-900 dark:text-white">
|
|
20
20
|
<%= link_to f["id"], pgbus.job_path(f["id"]), class: "text-indigo-600 hover:text-indigo-500", data: { turbo_frame: "_top" } %>
|
|
21
21
|
</td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= f["queue_name"] %></td>
|
|
23
|
-
<td class="px-4 py-3 text-sm text-red-600 max-w-sm truncate">
|
|
22
|
+
<td data-label="Queue" class="px-4 py-3 text-sm text-gray-700"><%= f["queue_name"] %></td>
|
|
23
|
+
<td data-label="Error" class="px-4 py-3 text-sm text-red-600 max-w-sm truncate">
|
|
24
24
|
<span class="font-medium"><%= f["error_class"] %></span>: <%= truncate(f["error_message"].to_s, length: 80) %>
|
|
25
25
|
</td>
|
|
26
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= f["retry_count"] %></td>
|
|
27
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(f["failed_at"]) %></td>
|
|
28
|
-
<td class="px-4 py-3 text-sm text-right space-x-2">
|
|
26
|
+
<td data-label="Retries" class="px-4 py-3 text-sm text-gray-500"><%= f["retry_count"] %></td>
|
|
27
|
+
<td data-label="Failed" class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(f["failed_at"]) %></td>
|
|
28
|
+
<td data-label="Actions" class="px-4 py-3 text-sm text-right space-x-2">
|
|
29
29
|
<%= button_to t("pgbus.jobs.failed_table.retry"), pgbus.retry_job_path(f["id"]), method: :post,
|
|
30
30
|
class: "text-xs text-indigo-600 hover:text-indigo-800 font-medium",
|
|
31
31
|
data: { turbo_frame: "_top" } %>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
7
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
7
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
8
8
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
9
9
|
<tr>
|
|
10
10
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500 dark:text-gray-400"><%= t("pgbus.locks.index.headers.lock_key") %></th>
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
19
19
|
<% @locks.each do |lock| %>
|
|
20
20
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
21
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700 dark:text-gray-300 max-w-xs truncate"><%= lock[:lock_key] %></td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-700 dark:text-gray-300"><%= lock[:job_class] %></td>
|
|
23
|
-
<td class="px-4 py-3 text-sm">
|
|
21
|
+
<td data-label="Lock Key" class="px-4 py-3 text-sm font-mono text-gray-700 dark:text-gray-300 max-w-xs truncate"><%= lock[:lock_key] %></td>
|
|
22
|
+
<td data-label="Job Class" class="px-4 py-3 text-sm text-gray-700 dark:text-gray-300"><%= lock[:job_class] %></td>
|
|
23
|
+
<td data-label="State" class="px-4 py-3 text-sm">
|
|
24
24
|
<% if lock[:state] == "executing" %>
|
|
25
25
|
<span class="inline-flex items-center rounded-full bg-blue-100 dark:bg-blue-900/50 px-2.5 py-0.5 text-xs font-medium text-blue-800 dark:text-blue-300"><%= t("pgbus.locks.index.executing") %></span>
|
|
26
26
|
<% else %>
|
|
27
27
|
<span class="inline-flex items-center rounded-full bg-yellow-100 dark:bg-yellow-900/50 px-2.5 py-0.5 text-xs font-medium text-yellow-800 dark:text-yellow-300"><%= t("pgbus.locks.index.queued") %></span>
|
|
28
28
|
<% end %>
|
|
29
29
|
</td>
|
|
30
|
-
<td class="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
|
|
30
|
+
<td data-label="Owner" class="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
|
|
31
31
|
<% if lock[:owner_pid] %>
|
|
32
32
|
<span class="font-mono"><%= lock[:owner_pid] %></span>
|
|
33
33
|
<% if lock[:owner_hostname] %>
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
<span class="text-gray-400 dark:text-gray-500">—</span>
|
|
38
38
|
<% end %>
|
|
39
39
|
</td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400">
|
|
40
|
+
<td data-label="Age" class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400">
|
|
41
41
|
<% if lock[:age_seconds] %>
|
|
42
42
|
<%= pgbus_duration(lock[:age_seconds]) %>
|
|
43
43
|
<% end %>
|
|
44
44
|
</td>
|
|
45
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400"><%= pgbus_time_ago_future(lock[:expires_at]) %></td>
|
|
45
|
+
<td data-label="Expires" class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400"><%= pgbus_time_ago_future(lock[:expires_at]) %></td>
|
|
46
46
|
</tr>
|
|
47
47
|
<% end %>
|
|
48
48
|
<% if @locks.empty? %>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
22
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
22
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
23
23
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
24
24
|
<tr>
|
|
25
25
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.outbox.index.headers.id") %></th>
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
34
34
|
<% @entries.each do |entry| %>
|
|
35
35
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
36
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700"><%= entry.id %></td>
|
|
37
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= entry.routing_key || entry.queue_name %></td>
|
|
38
|
-
<td class="px-4 py-3 text-sm text-gray-500 max-w-xs truncate"><%= pgbus_json_preview(entry.payload) %></td>
|
|
39
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= entry.priority %></td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-right">
|
|
36
|
+
<td data-label="ID" class="px-4 py-3 text-sm font-mono text-gray-700"><%= entry.id %></td>
|
|
37
|
+
<td data-label="Routing Key" class="px-4 py-3 text-sm text-gray-700"><%= entry.routing_key || entry.queue_name %></td>
|
|
38
|
+
<td data-label="Payload" class="px-4 py-3 text-sm text-gray-500 max-w-xs truncate"><%= pgbus_json_preview(entry.payload) %></td>
|
|
39
|
+
<td data-label="Priority" class="px-4 py-3 text-sm text-right text-gray-500"><%= entry.priority %></td>
|
|
40
|
+
<td data-label="Status" class="px-4 py-3 text-sm text-right">
|
|
41
41
|
<% if entry.published_at %>
|
|
42
42
|
<span class="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"><%= t("pgbus.outbox.index.published") %></span>
|
|
43
43
|
<% else %>
|
|
44
44
|
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800"><%= t("pgbus.outbox.index.pending") %></span>
|
|
45
45
|
<% end %>
|
|
46
46
|
</td>
|
|
47
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_time_ago(entry.created_at) %></td>
|
|
47
|
+
<td data-label="Created" class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_time_ago(entry.created_at) %></td>
|
|
48
48
|
</tr>
|
|
49
49
|
<% end %>
|
|
50
50
|
<% if @entries.empty? %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<turbo-frame id="processes-list" data-auto-refresh data-src="<%= pgbus.processes_path(frame: 'list') %>">
|
|
2
2
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
3
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
3
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
4
4
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
5
5
|
<tr>
|
|
6
6
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.processes.processes_table.headers.kind") %></th>
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
15
15
|
<% @processes.each do |p| %>
|
|
16
16
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
17
|
-
<td class="px-4 py-3 text-sm font-medium text-gray-900 dark:text-white"><%= p[:kind] %></td>
|
|
18
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= p[:hostname] %></td>
|
|
19
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700"><%= p[:pid] %></td>
|
|
20
|
-
<td class="px-4 py-3 text-sm"><%= pgbus_status_badge(p[:healthy]) %></td>
|
|
21
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(p[:last_heartbeat_at]) %></td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-500 font-mono text-xs max-w-xs truncate">
|
|
17
|
+
<td data-label="Kind" class="px-4 py-3 text-sm font-medium text-gray-900 dark:text-white"><%= p[:kind] %></td>
|
|
18
|
+
<td data-label="Host" class="px-4 py-3 text-sm text-gray-700"><%= p[:hostname] %></td>
|
|
19
|
+
<td data-label="PID" class="px-4 py-3 text-sm font-mono text-gray-700"><%= p[:pid] %></td>
|
|
20
|
+
<td data-label="Status" class="px-4 py-3 text-sm"><%= pgbus_status_badge(p[:healthy]) %></td>
|
|
21
|
+
<td data-label="Last Heartbeat" class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(p[:last_heartbeat_at]) %></td>
|
|
22
|
+
<td data-label="Metadata" class="px-4 py-3 text-sm text-gray-500 font-mono text-xs max-w-xs truncate">
|
|
23
23
|
<% if p[:metadata].is_a?(Hash) %>
|
|
24
24
|
<% p[:metadata].each do |k, v| %>
|
|
25
25
|
<span class="inline-flex items-center rounded bg-gray-100 px-1.5 py-0.5 text-xs mr-1"><%= k %>: <%= v %></span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<turbo-frame id="queues-list" data-auto-refresh data-src="<%= pgbus.queues_path(frame: 'list') %>">
|
|
2
2
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
3
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
3
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
4
4
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
5
5
|
<tr>
|
|
6
6
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.queues_list.headers.queue") %></th>
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
16
16
|
<% @queues.each do |q| %>
|
|
17
17
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
18
|
-
<td class="px-4 py-3 text-sm">
|
|
18
|
+
<td data-label="Queue" class="px-4 py-3 text-sm">
|
|
19
19
|
<%= link_to q[:name], pgbus.queue_path(name: q[:name]), class: "font-medium text-indigo-600 hover:text-indigo-500", data: { turbo_frame: "_top" } %>
|
|
20
20
|
<%= pgbus_queue_badge(q[:name]) %>
|
|
21
21
|
<% if q[:paused] %>
|
|
22
22
|
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800"><%= t("pgbus.queues.queues_list.paused") %></span>
|
|
23
23
|
<% end %>
|
|
24
24
|
</td>
|
|
25
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_length]) %></td>
|
|
26
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_visible_length]) %></td>
|
|
27
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:oldest_msg_age_sec] || "—" %></td>
|
|
28
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:newest_msg_age_sec] || "—" %></td>
|
|
29
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_number(q[:total_messages]) %></td>
|
|
30
|
-
<td class="px-4 py-3 text-sm text-right space-x-2">
|
|
25
|
+
<td data-label="Depth" class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_length]) %></td>
|
|
26
|
+
<td data-label="Visible" class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_visible_length]) %></td>
|
|
27
|
+
<td data-label="Oldest" class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:oldest_msg_age_sec] || "—" %></td>
|
|
28
|
+
<td data-label="Newest" class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:newest_msg_age_sec] || "—" %></td>
|
|
29
|
+
<td data-label="Total" class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_number(q[:total_messages]) %></td>
|
|
30
|
+
<td data-label="Actions" class="px-4 py-3 text-sm text-right space-x-2">
|
|
31
31
|
<% if q[:paused] %>
|
|
32
32
|
<%= button_to t("pgbus.queues.queues_list.resume"), pgbus.resume_queue_path(name: q[:name]),
|
|
33
33
|
method: :post,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<p class="mt-1 text-sm"><%= t("pgbus.recurring_tasks.tasks_table.empty_hint") %></p>
|
|
7
7
|
</div>
|
|
8
8
|
<% else %>
|
|
9
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
9
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
10
10
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
11
11
|
<tr>
|
|
12
12
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase"><%= t("pgbus.recurring_tasks.tasks_table.headers.task") %></th>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
|
22
22
|
<% @recurring_tasks.each do |task| %>
|
|
23
23
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
24
|
-
<td class="px-4 py-3">
|
|
24
|
+
<td data-label="Task" class="px-4 py-3">
|
|
25
25
|
<div>
|
|
26
26
|
<%= link_to task[:key], pgbus.recurring_task_path(task[:id]),
|
|
27
27
|
class: "text-sm font-medium text-blue-600 hover:text-blue-800" %>
|
|
@@ -31,26 +31,26 @@
|
|
|
31
31
|
<div class="text-xs text-gray-400 mt-0.5"><%= task[:description] %></div>
|
|
32
32
|
<% end %>
|
|
33
33
|
</td>
|
|
34
|
-
<td class="px-4 py-3">
|
|
34
|
+
<td data-label="Schedule" class="px-4 py-3">
|
|
35
35
|
<div class="text-sm text-gray-900 dark:text-white"><%= task[:schedule] %></div>
|
|
36
36
|
<% if task[:human_schedule] && task[:human_schedule] != task[:schedule] %>
|
|
37
37
|
<div class="text-xs text-gray-400"><%= task[:human_schedule] %></div>
|
|
38
38
|
<% end %>
|
|
39
39
|
</td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
40
|
+
<td data-label="Queue" class="px-4 py-3 text-sm text-gray-600">
|
|
41
41
|
<%= task[:queue_name] || t("pgbus.recurring_tasks.tasks_table.default_queue") %>
|
|
42
42
|
</td>
|
|
43
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
43
|
+
<td data-label="Last Run" class="px-4 py-3 text-sm text-gray-600">
|
|
44
44
|
<%= task[:last_run_at] ? pgbus_time_ago(task[:last_run_at]) : t("pgbus.recurring_tasks.tasks_table.never") %>
|
|
45
45
|
</td>
|
|
46
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
46
|
+
<td data-label="Next Run" class="px-4 py-3 text-sm text-gray-600">
|
|
47
47
|
<% if task[:enabled] && task[:next_run_at] %>
|
|
48
48
|
<%= pgbus_time_ago_future(task[:next_run_at]) %>
|
|
49
49
|
<% else %>
|
|
50
50
|
<span class="text-gray-400">—</span>
|
|
51
51
|
<% end %>
|
|
52
52
|
</td>
|
|
53
|
-
<td class="px-4 py-3">
|
|
53
|
+
<td data-label="Status" class="px-4 py-3">
|
|
54
54
|
<% if task[:enabled] %>
|
|
55
55
|
<%= pgbus_recurring_health_badge(task) %>
|
|
56
56
|
<% else %>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
</span>
|
|
60
60
|
<% end %>
|
|
61
61
|
</td>
|
|
62
|
-
<td class="px-4 py-3 text-right space-x-1">
|
|
62
|
+
<td data-label="Actions" class="px-4 py-3 text-right space-x-1">
|
|
63
63
|
<%= button_to task[:enabled] ? t("pgbus.recurring_tasks.tasks_table.disable") : t("pgbus.recurring_tasks.tasks_table.enable"),
|
|
64
64
|
pgbus.toggle_recurring_task_path(task[:id]),
|
|
65
65
|
class: "inline-flex items-center rounded px-2 py-1 text-xs font-medium " \
|
data/config/locales/da.yml
CHANGED
|
@@ -217,8 +217,10 @@ da:
|
|
|
217
217
|
processes: Processer
|
|
218
218
|
queues: Køer
|
|
219
219
|
recurring: Gentagende
|
|
220
|
+
return_to_app: Tilbage til app
|
|
220
221
|
title: Pgbus Dashboard
|
|
221
222
|
toggle_dark_mode: Skift til mørk tilstand
|
|
223
|
+
toggle_menu: Skift menu
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Aktive unikke låse forhindrer duplikeret jobudførelse
|
data/config/locales/de.yml
CHANGED
|
@@ -217,8 +217,10 @@ de:
|
|
|
217
217
|
processes: Prozesse
|
|
218
218
|
queues: Warteschlangen
|
|
219
219
|
recurring: Wiederkehrend
|
|
220
|
+
return_to_app: Zurück zur App
|
|
220
221
|
title: Pgbus Dashboard
|
|
221
222
|
toggle_dark_mode: Dunkelmodus umschalten
|
|
223
|
+
toggle_menu: Menü umschalten
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Aktive Einzigartigkeitssperren verhindern doppelte Auftragserstellung
|
data/config/locales/en.yml
CHANGED
|
@@ -217,8 +217,10 @@ en:
|
|
|
217
217
|
processes: Processes
|
|
218
218
|
queues: Queues
|
|
219
219
|
recurring: Recurring
|
|
220
|
+
return_to_app: Back to app
|
|
220
221
|
title: Pgbus Dashboard
|
|
221
222
|
toggle_dark_mode: Toggle dark mode
|
|
223
|
+
toggle_menu: Toggle menu
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Active uniqueness locks preventing duplicate job execution
|
data/config/locales/es.yml
CHANGED
|
@@ -217,8 +217,10 @@ es:
|
|
|
217
217
|
processes: Procesos
|
|
218
218
|
queues: Colas
|
|
219
219
|
recurring: Recurrente
|
|
220
|
+
return_to_app: Volver a la app
|
|
220
221
|
title: Panel de Pgbus
|
|
221
222
|
toggle_dark_mode: Alternar modo oscuro
|
|
223
|
+
toggle_menu: Alternar menú
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Bloqueos de unicidad activos que impiden la ejecución duplicada del trabajo
|
data/config/locales/fi.yml
CHANGED
|
@@ -217,8 +217,10 @@ fi:
|
|
|
217
217
|
processes: Prosessit
|
|
218
218
|
queues: Jonot
|
|
219
219
|
recurring: Toistuva
|
|
220
|
+
return_to_app: Takaisin sovellukseen
|
|
220
221
|
title: Pgbus-hallintapaneeli
|
|
221
222
|
toggle_dark_mode: Vaihda tumma tila
|
|
223
|
+
toggle_menu: Vaihda valikko
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Aktiiviset ainutlaatuisuuden lukot estävät päällekkäisen työn suorittamisen
|
data/config/locales/fr.yml
CHANGED
|
@@ -217,8 +217,10 @@ fr:
|
|
|
217
217
|
processes: Processus
|
|
218
218
|
queues: Files d'attente
|
|
219
219
|
recurring: Récurrent
|
|
220
|
+
return_to_app: Retour à l'application
|
|
220
221
|
title: Tableau de bord Pgbus
|
|
221
222
|
toggle_dark_mode: Basculer en mode sombre
|
|
223
|
+
toggle_menu: Basculer le menu
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Verrous d'unicité actifs empêchant l'exécution de travaux en double
|
data/config/locales/it.yml
CHANGED
|
@@ -217,8 +217,10 @@ it:
|
|
|
217
217
|
processes: Processi
|
|
218
218
|
queues: Code
|
|
219
219
|
recurring: Ricorrente
|
|
220
|
+
return_to_app: Torna all'app
|
|
220
221
|
title: Cruscotto Pgbus
|
|
221
222
|
toggle_dark_mode: Attiva/disattiva modalità scura
|
|
223
|
+
toggle_menu: Attiva/disattiva menu
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Blocchi di unicità attivi che impediscono l'esecuzione duplicata del lavoro
|
data/config/locales/ja.yml
CHANGED
data/config/locales/nb.yml
CHANGED
|
@@ -217,8 +217,10 @@ nb:
|
|
|
217
217
|
processes: Prosesser
|
|
218
218
|
queues: Køer
|
|
219
219
|
recurring: Gjentakende
|
|
220
|
+
return_to_app: Tilbake til appen
|
|
220
221
|
title: Pgbus-dashbord
|
|
221
222
|
toggle_dark_mode: Bytt til mørk modus
|
|
223
|
+
toggle_menu: Veksle meny
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Aktive unike låser som forhindrer duplisert jobbkjøring
|
data/config/locales/nl.yml
CHANGED
|
@@ -217,8 +217,10 @@ nl:
|
|
|
217
217
|
processes: Processen
|
|
218
218
|
queues: Wachtrijen
|
|
219
219
|
recurring: Terugkerend
|
|
220
|
+
return_to_app: Terug naar app
|
|
220
221
|
title: Pgbus Dashboard
|
|
221
222
|
toggle_dark_mode: Donkere modus schakelen
|
|
223
|
+
toggle_menu: Menu wisselen
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Actieve uniekheidsvergrendelingen voorkomen dubbele taakuitvoering
|
data/config/locales/pt.yml
CHANGED
|
@@ -217,8 +217,10 @@ pt:
|
|
|
217
217
|
processes: Processos
|
|
218
218
|
queues: Filas
|
|
219
219
|
recurring: Recorrente
|
|
220
|
+
return_to_app: Voltar ao aplicativo
|
|
220
221
|
title: Painel Pgbus
|
|
221
222
|
toggle_dark_mode: Alternar modo escuro
|
|
223
|
+
toggle_menu: Alternar menu
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Bloqueios de exclusividade ativos impedindo a execução duplicada do trabalho
|
data/config/locales/sv.yml
CHANGED
|
@@ -217,8 +217,10 @@ sv:
|
|
|
217
217
|
processes: Processer
|
|
218
218
|
queues: Köer
|
|
219
219
|
recurring: Återkommande
|
|
220
|
+
return_to_app: Tillbaka till appen
|
|
220
221
|
title: Pgbus-instrumentpanel
|
|
221
222
|
toggle_dark_mode: Växla mörkt läge
|
|
223
|
+
toggle_menu: Växla meny
|
|
222
224
|
locks:
|
|
223
225
|
index:
|
|
224
226
|
description: Aktiva unika lås som förhindrar duplicerad jobbexekvering
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record"
|
|
4
|
+
|
|
5
|
+
module Pgbus
|
|
6
|
+
# Base class for all Pgbus ActiveRecord models.
|
|
7
|
+
#
|
|
8
|
+
# Lives in lib/pgbus/ so the main Zeitwerk gem loader picks it up
|
|
9
|
+
# regardless of Rails engine boot order. This avoids the NameError
|
|
10
|
+
# that occurs when a host app uses selective railtie requires
|
|
11
|
+
# (require "rails" + individual railties instead of require "rails/all")
|
|
12
|
+
# and the engine's app/models path isn't registered yet.
|
|
13
|
+
class BusRecord < ActiveRecord::Base
|
|
14
|
+
self.abstract_class = true
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/pgbus/configuration.rb
CHANGED
|
@@ -64,7 +64,7 @@ module Pgbus
|
|
|
64
64
|
|
|
65
65
|
# Web dashboard
|
|
66
66
|
attr_accessor :web_auth, :web_refresh_interval, :web_per_page, :web_live_updates, :web_data_source,
|
|
67
|
-
:insights_default_minutes
|
|
67
|
+
:insights_default_minutes, :base_controller_class, :return_to_app_url
|
|
68
68
|
|
|
69
69
|
def initialize
|
|
70
70
|
@database_url = nil
|
|
@@ -136,6 +136,8 @@ module Pgbus
|
|
|
136
136
|
@web_live_updates = true
|
|
137
137
|
@web_data_source = nil
|
|
138
138
|
@insights_default_minutes = 30 * 24 * 60 # 30 days
|
|
139
|
+
@base_controller_class = "::ActionController::Base"
|
|
140
|
+
@return_to_app_url = nil
|
|
139
141
|
end
|
|
140
142
|
|
|
141
143
|
def queue_name(name)
|
|
@@ -201,7 +203,7 @@ module Pgbus
|
|
|
201
203
|
connection_params
|
|
202
204
|
elsif defined?(ActiveRecord::Base)
|
|
203
205
|
if connects_to
|
|
204
|
-
-> { Pgbus::
|
|
206
|
+
-> { Pgbus::BusRecord.connection.raw_connection }
|
|
205
207
|
else
|
|
206
208
|
-> { ActiveRecord::Base.connection.raw_connection }
|
|
207
209
|
end
|
data/lib/pgbus/engine.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Pgbus
|
|
|
27
27
|
|
|
28
28
|
initializer "pgbus.db" do
|
|
29
29
|
ActiveSupport.on_load(:active_record) do
|
|
30
|
-
Pgbus::
|
|
30
|
+
Pgbus::BusRecord.connects_to(**Pgbus.configuration.connects_to) if Pgbus.configuration.connects_to
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ module Pgbus
|
|
|
27
27
|
# that share at least one queue with the given queue list,
|
|
28
28
|
# excluding the current worker (by PID).
|
|
29
29
|
def self.max_active_priority(queues, my_pid)
|
|
30
|
-
conn = Pgbus.configuration.connects_to ? Pgbus::
|
|
30
|
+
conn = Pgbus.configuration.connects_to ? Pgbus::BusRecord.connection : ActiveRecord::Base.connection
|
|
31
31
|
rows = conn.select_all(
|
|
32
32
|
"SELECT metadata FROM pgbus_processes WHERE kind = 'worker' AND pid != $1 AND last_heartbeat_at > $2",
|
|
33
33
|
"Pgbus ConsumerPriority",
|
|
@@ -180,7 +180,7 @@ module Pgbus
|
|
|
180
180
|
batch_size = config.archive_compaction_batch_size || 1000
|
|
181
181
|
prefix = config.queue_prefix
|
|
182
182
|
|
|
183
|
-
conn = config.connects_to ? Pgbus::
|
|
183
|
+
conn = config.connects_to ? Pgbus::BusRecord.connection : ActiveRecord::Base.connection
|
|
184
184
|
queue_names = conn.select_values("SELECT queue_name FROM pgmq.meta ORDER BY queue_name")
|
|
185
185
|
|
|
186
186
|
queue_names.each do |full_name|
|
data/lib/pgbus/process/worker.rb
CHANGED
|
@@ -191,7 +191,7 @@ module Pgbus
|
|
|
191
191
|
dlq_suffix = config.dead_letter_queue_suffix
|
|
192
192
|
prefix = "#{config.queue_prefix}_"
|
|
193
193
|
|
|
194
|
-
conn = Pgbus.configuration.connects_to ? Pgbus::
|
|
194
|
+
conn = Pgbus.configuration.connects_to ? Pgbus::BusRecord.connection : ActiveRecord::Base.connection
|
|
195
195
|
all_queues = conn.select_values("SELECT queue_name FROM pgmq.meta ORDER BY queue_name")
|
|
196
196
|
resolved = all_queues
|
|
197
197
|
.reject { |q| q.end_with?(dlq_suffix) }
|
data/lib/pgbus/version.rb
CHANGED