diff --git a/CHANGELOG.md b/CHANGELOG.md index 3159558..c6c2a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Slow job webhook alert — set `alert_slow_job_count_threshold` (integer) to fire a webhook whenever the number of currently-running slow jobs meets or exceeds the configured count; requires `slow_job_threshold` to define what "slow" means; uses the same `alert_webhook_url` and `alert_webhook_cooldown` settings as other alert types; event name `slow_job_threshold_exceeded` - Stale process webhook alert — set `alert_stale_process_threshold` (integer) to fire a webhook whenever the number of stale workers meets or exceeds the configured count; a process is stale when its heartbeat has not been updated within `SolidQueue.process_alive_threshold`; uses the same `alert_webhook_url` and `alert_webhook_cooldown` settings; event name `stale_process_detected` - Job wait time column — the Running tab now shows a "Wait Time" column displaying how long each claimed job waited in the queue from enqueue to pickup; also included as `wait_time_seconds` in the CSV export for the claimed status +- Eliminate N+1 queries on the queues index — `queue.size` and `queue.paused?` previously fired one query each per queue; both are now pre-computed in the controller with single batch aggregations (`GROUP BY queue_name` for sizes, one `Pause` query for paused state), reducing query count from O(2N+constant) to O(constant) regardless of queue count ## [1.3.0] - 2026-05-27 diff --git a/ROADMAP.md b/ROADMAP.md index 993f312..5224b1b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -17,7 +17,7 @@ Pull requests for any of these are welcome. See [Contributing](README.md#contrib | ~~**Slow job webhook alert**~~ | ✅ Shipped in v1.4 — `alert_slow_job_count_threshold` fires when slow-job count meets or exceeds the threshold. | | ~~**Process stale webhook alert**~~ | ✅ Shipped in v1.4 — `alert_stale_process_threshold` fires when stale worker count meets or exceeds the threshold. | | ~~**Job wait time column**~~ | ✅ Shipped in v1.4 — "Wait Time" column on the Running tab; `wait_time_seconds` in the claimed CSV export. | -| **Eliminate N+1 queries** | Queues index fires 2 extra queries per queue: `queue.size` (COUNT per queue) and `queue.paused?` (EXISTS per queue). Replace with single batch aggregations pre-computed in the controller. | +| ~~**Eliminate N+1 queries**~~ | ✅ Shipped in v1.4 — queue size and paused state now pre-computed with single batch aggregations; query count reduced from O(2N) to O(1) per page load. | --- diff --git a/app/controllers/solid_queue_web/queues_controller.rb b/app/controllers/solid_queue_web/queues_controller.rb index f8909ba..bd91a32 100644 --- a/app/controllers/solid_queue_web/queues_controller.rb +++ b/app/controllers/solid_queue_web/queues_controller.rb @@ -7,6 +7,11 @@ def index @failed_24h = stats.failed_24h @oldest_ready = stats.oldest_ready @failure_sparklines = stats.failure_sparklines + @queue_sizes = SolidQueue::ReadyExecution + .joins(:job) + .group("solid_queue_jobs.queue_name") + .count + @paused_queue_names = SolidQueue::Pause.pluck(:queue_name).to_set end end end diff --git a/app/views/solid_queue_web/queues/index.html.erb b/app/views/solid_queue_web/queues/index.html.erb index 3abc8d8..2b21229 100644 --- a/app/views/solid_queue_web/queues/index.html.erb +++ b/app/views/solid_queue_web/queues/index.html.erb @@ -21,7 +21,7 @@ <% @queues.each do |queue| %>