sidekiq_queue_manager 1.0.0 → 1.0.1
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/app/assets/javascripts/sidekiq_queue_manager/application.js +51 -0
- data/app/controllers/sidekiq_queue_manager/application_controller.rb +14 -1
- data/app/controllers/sidekiq_queue_manager/dashboard_controller.rb +12 -1
- data/app/services/sidekiq_queue_manager/queue_service.rb +39 -6
- data/app/views/layouts/sidekiq_queue_manager/application.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/lib/sidekiq_queue_manager/logging_middleware.rb +17 -10
- data/lib/sidekiq_queue_manager/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 643e7129093c9ce8ed3d1c44b0066249f339ba8e05619644e35500b481f8fff6
|
4
|
+
data.tar.gz: fb0ca16a5c9df55ad9e384dd1c36c04adef2e88fe97dcd218896cde084f96cde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 878fe78f4d684a661a143de01fd6d16624ae0dbe7ab1b38d9fac4d96673e16a3615aa5f947142c8237ca6bf6841d10bfb25fb9d4e2b013835af1a57169300d79
|
7
|
+
data.tar.gz: ec5751c4bf9ca6be1b9b310438a22aefde78bf7c5c566cf8478cc5c9760dbd1384e12f26dd24c7b9cdfcbb60bfaccfa1f26849dd0e6f147f96becf416254fbd8
|
@@ -755,6 +755,9 @@ class SidekiqQueueManagerUI {
|
|
755
755
|
<button class="sqm-actions-menu-item sqm-actions-danger" data-action="clear" data-queue="${queueName}">
|
756
756
|
🗑️ Clear All Jobs
|
757
757
|
</button>
|
758
|
+
<button class="sqm-actions-menu-item sqm-actions-danger" data-action="delete_queue" data-queue="${queueName}">
|
759
|
+
❌ Delete Queue
|
760
|
+
</button>
|
758
761
|
</div>
|
759
762
|
</div>
|
760
763
|
`;
|
@@ -847,6 +850,8 @@ class SidekiqQueueManagerUI {
|
|
847
850
|
return this.unblockQueue(queueName);
|
848
851
|
case 'clear':
|
849
852
|
return this.clearQueue(queueName);
|
853
|
+
case 'delete_queue':
|
854
|
+
return this.deleteQueue(queueName);
|
850
855
|
default:
|
851
856
|
throw new Error(`Unknown action: ${action}`);
|
852
857
|
}
|
@@ -1391,6 +1396,52 @@ class SidekiqQueueManagerUI {
|
|
1391
1396
|
}
|
1392
1397
|
}
|
1393
1398
|
|
1399
|
+
async deleteQueue(queueName) {
|
1400
|
+
const confirmed = await this.showCustomConfirm(
|
1401
|
+
'Delete Queue Completely',
|
1402
|
+
`Are you sure you want to PERMANENTLY DELETE the "${queueName}" queue?`,
|
1403
|
+
'danger',
|
1404
|
+
'This action will remove the queue completely from Sidekiq, along with all its jobs. This cannot be undone.'
|
1405
|
+
);
|
1406
|
+
|
1407
|
+
if (!confirmed) return;
|
1408
|
+
|
1409
|
+
// Triple confirmation for queue deletion (more destructive than clearing)
|
1410
|
+
const doubleConfirmed = await this.showCustomConfirm(
|
1411
|
+
'Final Warning',
|
1412
|
+
`This will PERMANENTLY DELETE the entire "${queueName}" queue!`,
|
1413
|
+
'danger',
|
1414
|
+
'The queue will be completely removed from Sidekiq and cannot be recovered.'
|
1415
|
+
);
|
1416
|
+
|
1417
|
+
if (!doubleConfirmed) return;
|
1418
|
+
|
1419
|
+
const confirmation = await this.showCustomPrompt(
|
1420
|
+
'Type Queue Name to Confirm',
|
1421
|
+
`Type "${queueName}" exactly to confirm queue deletion:`,
|
1422
|
+
queueName,
|
1423
|
+
'This is your last chance to cancel this destructive operation.'
|
1424
|
+
);
|
1425
|
+
|
1426
|
+
if (confirmation !== queueName) {
|
1427
|
+
this.showNotification('Queue deletion cancelled', 'info');
|
1428
|
+
return;
|
1429
|
+
}
|
1430
|
+
|
1431
|
+
try {
|
1432
|
+
const response = await this.apiCall(`/queues/${queueName}`, { method: 'DELETE' });
|
1433
|
+
|
1434
|
+
if (response.success) {
|
1435
|
+
this.showNotification(`Queue "${queueName}" deleted permanently`, 'success');
|
1436
|
+
await this.refreshQueues();
|
1437
|
+
} else {
|
1438
|
+
throw new Error(response.message || 'Failed to delete queue');
|
1439
|
+
}
|
1440
|
+
} catch (error) {
|
1441
|
+
this.showNotification(`Failed to delete queue: ${error.message}`, 'error');
|
1442
|
+
}
|
1443
|
+
}
|
1444
|
+
|
1394
1445
|
async pauseAllQueues() {
|
1395
1446
|
if (!await this.showCustomConfirm('Pause All Queues', 'Are you sure you want to pause all non-critical queues?')) {
|
1396
1447
|
return;
|
@@ -13,6 +13,9 @@ module SidekiqQueueManager
|
|
13
13
|
# Engine-specific layout
|
14
14
|
layout 'sidekiq_queue_manager/application'
|
15
15
|
|
16
|
+
# Make mount path available to views
|
17
|
+
helper_method :engine_mount_path
|
18
|
+
|
16
19
|
# Authentication and security using Ruby's declarative approach
|
17
20
|
before_action :authenticate_access
|
18
21
|
before_action :set_security_headers
|
@@ -164,7 +167,7 @@ module SidekiqQueueManager
|
|
164
167
|
end
|
165
168
|
|
166
169
|
def direct_serving_strategy
|
167
|
-
mount_path =
|
170
|
+
mount_path = engine_mount_path
|
168
171
|
|
169
172
|
{
|
170
173
|
css_path: "#{mount_path}/assets/sidekiq_queue_manager/application.css",
|
@@ -174,6 +177,16 @@ module SidekiqQueueManager
|
|
174
177
|
}
|
175
178
|
end
|
176
179
|
|
180
|
+
# Get the mount path from the engine's routes
|
181
|
+
def engine_mount_path
|
182
|
+
# Extract mount path from the current request path
|
183
|
+
if request.path =~ %r{^(/[^/]+)}
|
184
|
+
Regexp.last_match(1)
|
185
|
+
else
|
186
|
+
'/sidekiq_manager' # Default fallback
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
177
190
|
# Common helper for accessing main application methods
|
178
191
|
def main_app
|
179
192
|
@main_app ||= Rails.application.routes.url_helpers
|
@@ -17,7 +17,7 @@ module SidekiqQueueManager
|
|
17
17
|
before_action :set_queue_name, only: %i[
|
18
18
|
queue_status pause_queue resume_queue jobs delete_job
|
19
19
|
set_limit remove_limit set_process_limit remove_process_limit
|
20
|
-
block unblock clear
|
20
|
+
block unblock clear delete_queue
|
21
21
|
]
|
22
22
|
|
23
23
|
# Comprehensive error handling for all controller actions
|
@@ -250,6 +250,17 @@ module SidekiqQueueManager
|
|
250
250
|
), status: result[:success] ? :ok : :unprocessable_entity
|
251
251
|
end
|
252
252
|
|
253
|
+
# Delete a queue completely
|
254
|
+
# DELETE /queues/:name
|
255
|
+
def delete_queue
|
256
|
+
result = QueueService.delete_queue(@queue_name)
|
257
|
+
render json: api_response(
|
258
|
+
success: result[:success],
|
259
|
+
message: result[:message],
|
260
|
+
data: result[:data]
|
261
|
+
), status: result[:success] ? :ok : :unprocessable_entity
|
262
|
+
end
|
263
|
+
|
253
264
|
private
|
254
265
|
|
255
266
|
# ========================================
|
@@ -202,7 +202,16 @@ module SidekiqQueueManager
|
|
202
202
|
|
203
203
|
queue = Sidekiq::Queue[queue_name]
|
204
204
|
operation = method_name.split('_')[0] # 'set', 'remove', 'block', 'unblock'
|
205
|
-
|
205
|
+
|
206
|
+
# Extract the correct attribute name based on the method
|
207
|
+
attribute = case method_name
|
208
|
+
when /set_queue_limit|remove_queue_limit/
|
209
|
+
'limit' # queue.limit for queue limits
|
210
|
+
when /set_process_limit|remove_process_limit/
|
211
|
+
'process_limit' # queue.process_limit for process limits
|
212
|
+
else
|
213
|
+
method_name.split('_', 2)[1] # fallback for block/unblock operations
|
214
|
+
end
|
206
215
|
|
207
216
|
perform_queue_operation(queue, queue_name, operation, attribute, *args)
|
208
217
|
rescue StandardError => e
|
@@ -224,18 +233,42 @@ module SidekiqQueueManager
|
|
224
233
|
handle_service_error(e, "clear queue '#{queue_name}'")
|
225
234
|
end
|
226
235
|
|
236
|
+
def delete_queue(queue_name)
|
237
|
+
return failure_response('Invalid queue name') unless valid_queue?(queue_name)
|
238
|
+
return failure_response('Cannot delete critical queue') if critical_queue?(queue_name)
|
239
|
+
|
240
|
+
queue = Sidekiq::Queue[queue_name]
|
241
|
+
jobs_count = queue.size
|
242
|
+
|
243
|
+
# Clear all jobs first
|
244
|
+
queue.clear
|
245
|
+
|
246
|
+
# Complete cleanup of queue from Redis
|
247
|
+
Sidekiq.redis do |conn|
|
248
|
+
# Remove queue from the queues set
|
249
|
+
conn.srem('queues', queue_name)
|
250
|
+
|
251
|
+
# Delete the actual queue key from Redis
|
252
|
+
conn.del("queue:#{queue_name}")
|
253
|
+
|
254
|
+
# Clean up any related keys (like paused queue markers)
|
255
|
+
conn.del("queue:#{queue_name}:paused")
|
256
|
+
end
|
257
|
+
|
258
|
+
log_operation("Queue '#{queue_name}' deleted completely - #{jobs_count} jobs removed")
|
259
|
+
success_response("Queue '#{queue_name}' deleted successfully", jobs_cleared: jobs_count)
|
260
|
+
rescue StandardError => e
|
261
|
+
handle_service_error(e, "delete queue '#{queue_name}'")
|
262
|
+
end
|
263
|
+
|
227
264
|
private
|
228
265
|
|
229
266
|
# ========================================
|
230
267
|
# Queue Information Helpers
|
231
268
|
# ========================================
|
232
269
|
|
233
|
-
def available_queues
|
234
|
-
@available_queues ||= discover_queues
|
235
|
-
end
|
236
|
-
|
237
270
|
# Ruby's functional approach to queue discovery
|
238
|
-
def
|
271
|
+
def available_queues
|
239
272
|
[
|
240
273
|
# Method 1: Sidekiq's built-in discovery
|
241
274
|
-> { Sidekiq::Queue.all.map(&:name) },
|
@@ -54,7 +54,7 @@
|
|
54
54
|
<!-- Configuration for JavaScript -->
|
55
55
|
<script type="text/javascript">
|
56
56
|
window.SidekiqQueueManagerConfig = {
|
57
|
-
mountPath: '<%=
|
57
|
+
mountPath: '<%= engine_mount_path %>',
|
58
58
|
refreshInterval: <%= SidekiqQueueManager.configuration.refresh_interval %>,
|
59
59
|
theme: '<%= SidekiqQueueManager.configuration.theme %>',
|
60
60
|
criticalQueues: <%= SidekiqQueueManager.configuration.critical_queues.to_json.html_safe %>,
|
data/config/routes.rb
CHANGED
@@ -31,6 +31,7 @@ SidekiqQueueManager::Engine.routes.draw do
|
|
31
31
|
get '/queues/:name/jobs', to: 'dashboard#jobs', as: :queue_jobs
|
32
32
|
delete '/queues/:name/delete_job', to: 'dashboard#delete_job', as: :delete_queue_job
|
33
33
|
post '/queues/:name/clear', to: 'dashboard#clear', as: :clear_queue
|
34
|
+
delete '/queues/:name', to: 'dashboard#delete_queue', as: :delete_queue
|
34
35
|
|
35
36
|
# Queue limits and configuration
|
36
37
|
post '/queues/:name/set_limit', to: 'dashboard#set_limit', as: :set_queue_limit
|
@@ -8,21 +8,28 @@ module SidekiqQueueManager
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def call(env)
|
11
|
-
|
12
|
-
|
13
|
-
start_time = Time.current
|
14
|
-
|
15
|
-
status, headers, body = @app.call(env)
|
11
|
+
start_time = Time.current
|
12
|
+
status, headers, body = @app.call(env)
|
16
13
|
|
14
|
+
# Only log if this request was handled by our engine
|
15
|
+
if handled_by_sidekiq_queue_manager?(env)
|
17
16
|
end_time = Time.current
|
18
17
|
duration = (end_time - start_time) * 1000 # milliseconds
|
18
|
+
path_info = env['PATH_INFO'] || ''
|
19
19
|
|
20
|
-
Rails.logger.info "[SidekiqQueueManager] #{env['REQUEST_METHOD']} #{
|
21
|
-
|
22
|
-
[status, headers, body]
|
23
|
-
else
|
24
|
-
@app.call(env)
|
20
|
+
Rails.logger.info "[SidekiqQueueManager] #{env['REQUEST_METHOD']} #{path_info} - #{status} (#{duration.round(2)}ms)"
|
25
21
|
end
|
22
|
+
|
23
|
+
[status, headers, body]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Check if the request was handled by our engine controllers
|
29
|
+
def handled_by_sidekiq_queue_manager?(env)
|
30
|
+
# Check if the controller class is from our engine
|
31
|
+
controller_class = env['action_controller.instance']&.class&.name
|
32
|
+
controller_class&.start_with?('SidekiqQueueManager::')
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|