-
Notifications
You must be signed in to change notification settings - Fork 173
Expand file tree
/
Copy pathmemoized_cache_proxy.rb
More file actions
128 lines (107 loc) · 3.43 KB
/
memoized_cache_proxy.rb
File metadata and controls
128 lines (107 loc) · 3.43 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
require 'monitor'
module IdentityCache
class MemoizedCacheProxy
attr_reader :cache_fetcher
def initialize(cache_adaptor = nil)
self.cache_backend = cache_adaptor || Rails.cache
@key_value_maps = Hash.new {|h, k| h[k] = {} }
end
def cache_backend=(cache_adaptor)
if cache_adaptor.respond_to?(:cas) && cache_adaptor.respond_to?(:cas_multi)
@cache_fetcher = CacheFetcher.new(cache_adaptor)
else
@cache_fetcher = FallbackFetcher.new(cache_adaptor)
end
end
def memoized_key_values
@key_value_maps[Thread.current]
end
def with_memoization(&block)
Thread.current[:memoizing_idc] = true
yield
ensure
clear_memoization
Thread.current[:memoizing_idc] = false
end
def write(key, value)
memoized_key_values[key] = value if memoizing?
@cache_fetcher.write(key, value)
end
def delete(key)
memoized_key_values.delete(key) if memoizing?
@cache_fetcher.delete(key)
end
def fetch(key)
used_cache_backend = true
missed = false
value = if memoizing?
used_cache_backend = false
memoized_key_values.fetch(key) do
used_cache_backend = true
memoized_key_values[key] = @cache_fetcher.fetch(key) do
missed = true
yield
end
end
else
@cache_fetcher.fetch(key) do
missed = true
yield
end
end
if missed
IdentityCache.logger.debug { "[IdentityCache] cache miss for #{key}" }
else
IdentityCache.logger.debug { "[IdentityCache] #{ used_cache_backend ? '(cache_backend)' : '(memoized)' } cache hit for #{key}" }
end
value
end
def fetch_multi(*keys)
memoized_keys, missed_keys = [], [] if IdentityCache.logger.debug?
result = if memoizing?
hash = {}
mkv = memoized_key_values
non_memoized_keys = keys.reject do |key|
if mkv.has_key?(key)
memoized_keys << key if IdentityCache.logger.debug?
hit = mkv[key]
hash[key] = hit unless hit.nil?
true
end
end
unless non_memoized_keys.empty?
results = @cache_fetcher.fetch_multi(non_memoized_keys) do |missing_keys|
missed_keys.concat(missing_keys) if IdentityCache.logger.debug?
yield missing_keys
end
mkv.merge! results
hash.merge! results
end
hash
else
@cache_fetcher.fetch_multi(keys) do |missing_keys|
missed_keys.concat(missing_keys) if IdentityCache.logger.debug?
yield missing_keys
end
end
log_multi_result(memoized_keys, keys - missed_keys - memoized_keys, missed_keys) if IdentityCache.logger.debug?
result
end
def clear
clear_memoization
@cache_fetcher.clear
end
private
def clear_memoization
@key_value_maps.delete(Thread.current)
end
def memoizing?
Thread.current[:memoizing_idc]
end
def log_multi_result(memoized_keys, backend_keys, missed_keys)
memoized_keys.each {|k| IdentityCache.logger.debug "[IdentityCache] (memoized) cache hit for #{k} (multi)" }
backend_keys.each {|k| IdentityCache.logger.debug "[IdentityCache] (backend) cache hit for #{k} (multi)" }
missed_keys.each {|k| IdentityCache.logger.debug "[IdentityCache] cache miss for #{k} (multi)" }
end
end
end