-
Notifications
You must be signed in to change notification settings - Fork 173
Expand file tree
/
Copy pathcache_key_loader.rb
More file actions
129 lines (108 loc) · 4.5 KB
/
cache_key_loader.rb
File metadata and controls
129 lines (108 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# frozen_string_literal: true
module IdentityCache
# A generic cache key loader that supports different types of
# cache fetchers, each of which can use their own cache key
# format and have their own cache miss resolvers.
#
# Here is the interface of a cache fetcher in the
# [ruby-signature](https://github.com/ruby/ruby-signature)'s
# format.
#
# ```
# interface _CacheFetcher[DbKey, DbValue, CacheableValue]
# def cache_key: (DbKey) -> String
# def cache_encode: (DbValue) -> CacheableValue
# def cache_decode: (CacheableValue) -> DbValue
# def load_one_from_db: (DbKey) -> DbValue
# def load_multi_from_db: (Array[DbKey]) -> Hash[DbKey, DbValue]
# end
# ```
module CacheKeyLoader
class << self
# Load a single key for a cache fetcher.
#
# @param cache_fetcher [_CacheFetcher]
# @param db_key Reference to what to load from the database.
# @return The database value corresponding to the database key.
def load(cache_fetcher, db_key)
cache_key = cache_fetcher.cache_key(db_key)
db_value = nil
cache_value = IdentityCache.fetch(cache_key) do
IdentityCache.logger.debug "Resolving miss key=#{self.name} db_key=#{db_key}"
db_value = cache_fetcher.load_one_from_db(db_key)
cache_fetcher.cache_encode(db_value)
end
db_value || cache_fetcher.cache_decode(cache_value)
end
# Load multiple keys for a cache fetcher.
#
# @param cache_fetcher [_CacheFetcher]
# @param db_key [Array] Reference to what to load from the database.
# @return [Hash] A hash mapping each database key to its corresponding value
def load_multi(cache_fetcher, db_keys)
load_batch(cache_fetcher => db_keys).fetch(cache_fetcher)
end
# Load multiple keys for multiple cache fetchers
def load_batch(cache_fetcher_to_db_keys_hash)
cache_key_to_db_key_hash = {}
cache_key_to_cache_fetcher_hash = {}
batch_load_result = {}
cache_fetcher_to_db_keys_hash.each do |cache_fetcher, db_keys|
if db_keys.empty?
batch_load_result[cache_fetcher] = {}
next
end
db_keys.each do |db_key|
cache_key = cache_fetcher.cache_key(db_key)
cache_key_to_db_key_hash[cache_key] = db_key
cache_key_to_cache_fetcher_hash[cache_key] = cache_fetcher
end
end
cache_keys = cache_key_to_db_key_hash.keys
cache_result = cache_fetch_multi(cache_keys) do |unresolved_cache_keys|
cache_fetcher_to_unresolved_keys_hash = unresolved_cache_keys.group_by do |cache_key|
cache_key_to_cache_fetcher_hash.fetch(cache_key)
end
resolve_miss_result = {}
db_keys_buffer = []
cache_fetcher_to_unresolved_keys_hash.each do |cache_fetcher, unresolved_cache_fetcher_keys|
batch_load_result[cache_fetcher] = resolve_multi_on_miss(cache_fetcher, unresolved_cache_fetcher_keys,
cache_key_to_db_key_hash, resolve_miss_result, db_keys_buffer: db_keys_buffer)
end
resolve_miss_result
end
cache_result.each do |cache_key, cache_value|
cache_fetcher = cache_key_to_cache_fetcher_hash.fetch(cache_key)
load_result = (batch_load_result[cache_fetcher] ||= {})
db_key = cache_key_to_db_key_hash.fetch(cache_key)
load_result[db_key] ||= cache_fetcher.cache_decode(cache_value)
end
batch_load_result
end
private
def cache_fetch_multi(cache_keys)
IdentityCache.fetch_multi(cache_keys) do |unresolved_cache_keys|
cache_key_to_cache_value_hash = yield unresolved_cache_keys
cache_key_to_cache_value_hash.fetch_values(*unresolved_cache_keys)
end
end
def resolve_multi_on_miss(
cache_fetcher, unresolved_cache_keys, cache_key_to_db_key_hash, resolve_miss_result,
db_keys_buffer: []
)
db_keys_buffer.clear
unresolved_cache_keys.each do |cache_key|
db_keys_buffer << cache_key_to_db_key_hash.fetch(cache_key)
end
load_result = cache_fetcher.load_multi_from_db(db_keys_buffer)
unresolved_cache_keys.each do |cache_key|
db_key = cache_key_to_db_key_hash.fetch(cache_key)
db_value = load_result[db_key]
resolve_miss_result[cache_key] = cache_fetcher.cache_encode(db_value)
end
load_result
end
end
end
private_constant :CacheKeyLoader
end