Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions app/models/solid_queue/queue_selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,23 @@ def exact_names
def prefixed_names
if prefixes.empty? then []
else
relation.where(([ "queue_name LIKE ?" ] * prefixes.count).join(" OR "), *prefixes).distinct_values_of(:queue_name)
pattern = "queue_name LIKE ? #{like_escape_clause}"
relation.where(([ pattern ] * prefixes.count).join(" OR "), *prefixes).distinct_values_of(:queue_name)
end
end

# MySQL string literals treat a backslash as an escape, so the single-character
# ESCAPE value is written with a doubled backslash. SQLite and PostgreSQL take one.
def like_escape_clause
if relation.connection.adapter_name.match?(/mysql|trilogy/i)
"ESCAPE '\\\\'"
else
"ESCAPE '\\'"
end
end

def prefixes
@prefixes ||= raw_queues.select { |queue| prefixed_name?(queue) }.map { |queue| queue.tr("*", "%") }
@prefixes ||= raw_queues.select { |queue| prefixed_name?(queue) }.map { |queue| relation.sanitize_sql_like(queue).tr("*", "%") }
end

def exact_name?(queue)
Expand All @@ -74,9 +85,9 @@ def paused_queues
end

def in_raw_order(queues)
# Only need to sort if we have prefixes and more than one queue name.
# Exact names are selected in the same order as they're found
if queues.one? || prefixes.empty?
# Exact names are already in configuration order. Prefix matches always
# go through the literal filter so a single false LIKE hit is not returned.
if prefixes.empty?
queues
else
queues = queues.dup
Expand Down
28 changes: 28 additions & 0 deletions test/models/solid_queue/ready_execution_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,34 @@ class SolidQueue::ReadyExecutionTest < ActiveSupport::TestCase
assert @jobs.none?(&:claimed?)
end

test "prefixes escape LIKE wildcards so only literal queue names match" do
AddToBufferJob.set(queue: "mailbox").perform_later(1)
AddToBufferJob.set(queue: "mail-ops").perform_later(1)

assert_claimed_jobs(0) do
SolidQueue::ReadyExecution.claim("mail_*", SolidQueue::Job.count + 1, 42)
end

AddToBufferJob.set(queue: "mail_ops").perform_later(1)
AddToBufferJob.set(queue: "100x_done").perform_later(1)

assert_claimed_jobs(1) do
SolidQueue::ReadyExecution.claim("mail_*", SolidQueue::Job.count + 1, 42)
end
assert_equal "mail_ops", SolidQueue::ClaimedExecution.last.job.queue_name

assert_claimed_jobs(0) do
SolidQueue::ReadyExecution.claim("100%_*", SolidQueue::Job.count + 1, 42)
end

AddToBufferJob.set(queue: "100%_done").perform_later(1)

assert_claimed_jobs(1) do
SolidQueue::ReadyExecution.claim("100%_*", 1, 42)
end
assert_equal "100%_done", SolidQueue::ClaimedExecution.last.job.queue_name
end

test "claim jobs using a wildcard and having paused queues" do
AddToBufferJob.perform_later("hey")

Expand Down
Loading