Skip to content

Commit 129bc44

Browse files
committed
Batch multi queries
To avoid massive multi read or write queries, we'll upsert and select in batches of 1000 keys.
1 parent 34daa1d commit 129bc44

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

app/models/solid_cache/entry.rb

+13-5
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,20 @@ class Entry < Record
1414

1515
KEY_HASH_ID_RANGE = -(2**63)..(2**63 - 1)
1616

17+
MULTI_BATCH_SIZE = 1000
18+
1719
class << self
1820
def write(key, value)
1921
write_multi([ { key: key, value: value } ])
2022
end
2123

2224
def write_multi(payloads)
2325
without_query_cache do
24-
upsert_all \
25-
add_key_hash_and_byte_size(payloads),
26-
unique_by: upsert_unique_by, on_duplicate: :update, update_only: [ :key, :value, :byte_size ]
26+
payloads.each_slice(MULTI_BATCH_SIZE).each do |payload_batch|
27+
upsert_all \
28+
add_key_hash_and_byte_size(payload_batch),
29+
unique_by: upsert_unique_by, on_duplicate: :update, update_only: [ :key, :value, :byte_size ]
30+
end
2731
end
2832
end
2933

@@ -33,9 +37,13 @@ def read(key)
3337

3438
def read_multi(keys)
3539
without_query_cache do
36-
query = Arel.sql(select_sql(keys), *key_hashes_for(keys))
40+
{}.tap do |results|
41+
keys.each_slice(MULTI_BATCH_SIZE).each do |keys_batch|
42+
query = Arel.sql(select_sql(keys_batch), *key_hashes_for(keys_batch))
3743

38-
connection.select_all(query, "SolidCache::Entry Load").cast_values(attribute_types).to_h
44+
results.merge!(connection.select_all(query, "SolidCache::Entry Load").cast_values(attribute_types).to_h)
45+
end
46+
end
3947
end
4048
end
4149

test/models/solid_cache/entry_test.rb

+24
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,33 @@ class EntryTest < ActiveSupport::TestCase
7070
end
7171
end
7272

73+
test "batching multi queries" do
74+
with_multi_batch_size(2) do
75+
Entry.stubs(:const_get).with(:MULTI_BATCH_SIZE).returns("stubbed_value")
76+
77+
assert_queries_count(2) do
78+
Entry.write_multi([ { key: "hello".b, value: "there" }, { key: "foo".b, value: "bar" }, { key: "baz".b, value: "zab" } ])
79+
end
80+
81+
assert_queries_count(2) do
82+
assert_equal({ "foo" => "bar", "hello" => "there", "baz" => "zab" }, Entry.read_multi([ "hello".b, "foo".b, "baz".b, "bar".b ]))
83+
end
84+
end
85+
end
86+
7387
private
7488
def write_entries(count = 20)
7589
Entry.write_multi(count.times.map { |i| { key: "key#{i}", value: "value#{i}" } })
7690
end
91+
92+
def with_multi_batch_size(value)
93+
old_value = Entry::MULTI_BATCH_SIZE
94+
Entry.send(:remove_const, :MULTI_BATCH_SIZE)
95+
Entry.const_set(:MULTI_BATCH_SIZE, value)
96+
yield
97+
ensure
98+
Entry.send(:remove_const, :MULTI_BATCH_SIZE)
99+
Entry.const_set(:MULTI_BATCH_SIZE, old_value)
100+
end
77101
end
78102
end

0 commit comments

Comments
 (0)