From 79605105de931e9bc6488132916cdd37093768e0 Mon Sep 17 00:00:00 2001 From: JP Camara Date: Mon, 24 Aug 2026 13:19:09 -0400 Subject: [PATCH] Probe the completion check from the newest tracking row A batch drains roughly in id order, so the rows still outstanding are at the end of the range while the beginning is whatever finished jobs left behind. In isolation this halves the probe's tail latency (p95 2.49ms to 0.68ms on a 95%-drained batch). It did not move the end-to-end drain: 318s against 320s on a 120k batch, which is inside the run-to-run noise. Kept for the tail behaviour, but on this evidence it is not what is holding the completion path back. Co-Authored-By: Claude Fable 5 --- app/models/solid_queue/batch_execution.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/solid_queue/batch_execution.rb b/app/models/solid_queue/batch_execution.rb index adf07b92..1900a34d 100644 --- a/app/models/solid_queue/batch_execution.rb +++ b/app/models/solid_queue/batch_execution.rb @@ -31,7 +31,11 @@ def finish_batch_for(batch_id) # is. Asking the cheap question first means the batch row is only read # for the job that actually finishes the batch. #finish asks it again, so # this decides nothing on its own. - return if where(batch_id: batch_id).exists? + # + # Looking from the newest row backwards, because a batch drains roughly + # in id order: the rows still outstanding are the ones at the end, while + # the beginning of the range is whatever the finished jobs left behind. + return if where(batch_id: batch_id).order(id: :desc).pick(:id) # Skip the serialized callback and metadata columns on this hot path if batch = Batch.select(:id, :finished_at, :enqueued_at).find_by(id: batch_id)