forked from instructure/canvas-lms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmulti_cache.rb
More file actions
103 lines (94 loc) · 2.9 KB
/
Copy pathmulti_cache.rb
File metadata and controls
103 lines (94 loc) · 2.9 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
#
# Copyright (C) 2014 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class MultiCache < ActiveSupport::Cache::Store
def self.cache
if defined?(ActiveSupport::Cache::RedisStore) && Rails.cache.is_a?(ActiveSupport::Cache::RedisStore) &&
defined?(Redis::DistributedStore) && (store = Rails.cache.instance_variable_get(:@data)).is_a?(Redis::DistributedStore)
store.instance_variable_get(:@multi_cache) || store.instance_variable_set(:@multi_cache, MultiCache.new(store.ring.nodes))
else
Rails.cache
end
end
def initialize(ring)
@ring = ring
super()
end
def fetch(key, options = nil, &block)
options ||= {}
# an option to allow populating all nodes in the ring with the
# same data
if options[:node] == :all
calculated_value = nil
did_calculate = false
result = nil
@ring.each do |node|
options[:node] = node
if block
result = super(key, options) do
calculated_value = yield unless did_calculate
did_calculate = true
calculated_value
end
else
result ||= []
result << super(key, options)
end
end
result
else
# this makes the node "sticky" for read/write
options[:node] = @ring[rand(@ring.length)]
super(key, options, &block)
end
end
# for compatibility
def self.copies(key)
nil
end
def self.fetch(key, options = nil, &block)
cache.fetch(key, options, &block)
end
def self.delete(key, options = nil)
cache.delete(key, options)
end
private
def write_entry(key, entry, options)
method = options && options[:unless_exist] ? :setnx : :set
options[:node].send method, key, entry, options
rescue Errno::ECONNREFUSED, Redis::CannotConnectError
false
end
def read_entry(key, options)
entry = options[:node].get key, options
if entry
entry.is_a?(ActiveSupport::Cache::Entry) ? entry : ActiveSupport::Cache::Entry.new(entry)
end
rescue Errno::ECONNREFUSED, Redis::CannotConnectError
nil
end
def delete_entry(key, options)
nodes = options[:node] ? [options[:node]] : @ring
nodes.inject(false) do |result, node|
begin
node.del(key) || result
rescue Errno::ECONNREFUSED, Redis::CannotConnectError
result
end
end
end
end