Skip to content

Commit f6ae3be

Browse files
committed
improve queue name select performance for postgresql
1 parent ab89cc1 commit f6ae3be

File tree

1 file changed

+51
-2
lines changed

1 file changed

+51
-2
lines changed

app/models/solid_queue/queue.rb

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,63 @@ class Queue
66

77
class << self
88
def all
9-
Job.select(:queue_name).distinct.collect do |job|
10-
new(job.queue_name)
9+
queue_names.collect do |queue_name|
10+
new(queue_name)
1111
end
1212
end
1313

1414
def find_by_name(name)
1515
new(name)
1616
end
17+
18+
private
19+
20+
def queue_names
21+
# PostgreSQL doesn't perform well with SELECT DISTINCT
22+
# => Use recursive common table expressions if possible for better performance (https://wiki.postgresql.org/wiki/Loose_indexscan)
23+
if SolidQueue::Record.connection.adapter_name.downcase == "postgresql" && SolidQueue::Record.connection.supports_common_table_expressions?
24+
Job.connection.execute(queue_names_recursive_cte_sql).to_a.map { |row| row["queue_name"] }
25+
else
26+
Job.select(:queue_name).distinct.map(&:queue_name)
27+
end
28+
end
29+
30+
def queue_names_recursive_cte_sql
31+
# This relies on the fact that queue_name in solid_queue_jobs is NOT NULL
32+
# The sql looks something like below:
33+
# WITH RECURSIVE t AS (
34+
# (SELECT queue_name FROM solid_queue_jobs ORDER BY queue_name LIMIT 1) -- parentheses required
35+
# UNION ALL
36+
# SELECT (SELECT queue_name FROM solid_queue_jobs WHERE queue_name > t.queue_name ORDER BY queue_name LIMIT 1)
37+
# FROM t
38+
# WHERE t.queue_name IS NOT NULL
39+
# )
40+
# SELECT queue_name FROM t WHERE queue_name IS NOT NULL;
41+
42+
cte_table = Arel::Table.new(:t)
43+
jobs_table = Job.arel_table
44+
45+
cte_base_case = jobs_table.project(jobs_table[:queue_name]).order(jobs_table[:queue_name]).take(1)
46+
47+
subquery = jobs_table
48+
.project(jobs_table[:queue_name])
49+
.where(jobs_table[:queue_name].gt(cte_table[:queue_name]))
50+
.order(jobs_table[:queue_name])
51+
.take(1)
52+
cte_recursive_case = cte_table.project(subquery)
53+
.where(cte_table[:queue_name].not_eq(nil))
54+
55+
cte_definition = Arel::Nodes::Cte.new(
56+
Arel.sql("t"),
57+
Arel::Nodes::UnionAll.new(cte_base_case, cte_recursive_case),
58+
)
59+
60+
cte_table
61+
.project(cte_table[:queue_name])
62+
.where(cte_table[:queue_name].not_eq(nil))
63+
.with(:recursive, cte_definition)
64+
.to_sql
65+
end
1766
end
1867

1968
def initialize(name)

0 commit comments

Comments
 (0)