Skip to content

Fix superfluous SELECT when scheduling a single future job - #807

Open
Mohammadalkhassawneh wants to merge 1 commit into
rails:mainfrom
Mohammadalkhassawneh:fix-superfluous-select-on-single-job-schedule
Open

Mohammadalkhassawneh wants to merge 1 commit into
rails:mainfrom
Mohammadalkhassawneh:fix-superfluous-select-on-single-job-schedule

Conversation

@Mohammadalkhassawneh

@Mohammadalkhassawneh Mohammadalkhassawneh commented Sep 24, 2026 •

Copy link
Copy Markdown

Problem

When a single job is enqueued, Execution subclasses (ScheduledExecution, ReadyExecution, BlockedExecution) each fire an extra SELECT solid_queue_jobs WHERE id = ? immediately before their INSERT.

The root cause is assume_attributes_from_job — a before_create callback that copies queue_name, priority, and any subclass-specific attributes from the job record:

def assume_attributes_from_job
  self.class.assumable_attributes_from_job.each do |attribute|
    send("\#{attribute}=", job.send(attribute))
  end
end

Because create_or_find_by!(job_id: id) only sets the job_id FK, the job association is not loaded. Calling job.send(attribute) lazy-loads it, triggering a SELECT. The bulk path (enqueue_all) already avoids this because insert_all bypasses ActiveRecord callbacks entirely. The single-job paths do not.

Fix

Pass job: self via the create_or_find_by! block so the association is already loaded in memory when assume_attributes_from_job fires:

# before
ScheduledExecution.create_or_find_by!(job_id: id)

# after
ScheduledExecution.create_or_find_by!(job_id: id) { |e| e.job = self }

Applied to all three single-job dispatch paths: schedule, ready, and block.

When a single job is enqueued, each Execution subclass fires a SELECT
on solid_queue_jobs before INSERT. This happens because:

1. assumes_attributes_from_job sets a before_create callback that calls
   job.send(attribute) for each attribute to copy from the job record.
2. Since create_or_find_by!(job_id: id) only sets the job_id foreign key,
   the job association isn't loaded — so accessing it triggers a SELECT.

The bulk path (enqueue_all) already avoids this by using insert_all,
which bypasses callbacks entirely.

Fix: pass job: self via the create_or_find_by! block so the association
is already loaded in memory when both the before_create callback and the
belongs_to validation run. No DB round-trip needed.

Applied to all three single-job dispatch paths: schedule (ScheduledExecution),
ready (ReadyExecution), and block (BlockedExecution).
@Mohammadalkhassawneh
Mohammadalkhassawneh force-pushed the fix-superfluous-select-on-single-job-schedule branch from 6e2bffa to cfad0ed Compare September 24, 2026 10:15
@Mohammadalkhassawneh
Mohammadalkhassawneh marked this pull request as ready for review September 24, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant