forked from instructure/canvas-lms
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdynamic_settings.rb
More file actions
111 lines (95 loc) · 3.45 KB
/
Copy pathdynamic_settings.rb
File metadata and controls
111 lines (95 loc) · 3.45 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
require 'diplomat'
module Canvas
class DynamicSettings
class ConsulError < StandardError
end
KV_NAMESPACE = "/config/canvas".freeze
class << self
attr_accessor :config, :cache
def config=(conf_hash)
@config = conf_hash
if conf_hash.present?
Diplomat.configure do |diplomat_conf|
protocol = conf_hash.fetch("ssl", true) ? "https" : "http"
host_and_port = "#{conf_hash.fetch('host')}:#{conf_hash.fetch('port')}"
diplomat_conf.url = "#{protocol}://#{host_and_port}"
diplomat_conf.acl_token = conf_hash.fetch("acl_token", nil)
end
init_data = conf_hash.fetch("init_values", {})
init_values(init_data)
end
end
def find(key)
raise(ConsulError, "Unable to contact consul without config") if config.nil?
config_records = store_get(key)
config_records.each_with_object({}) do |node, hash|
hash[node[:key].split("/").last] = node[:value]
end
end
# settings found this way with nil expiry will be cached in the process
# the first time they're asked for, and then can only be cleared with a SIGHUP
# or restart of the process. Make sure that's the behavior you want before
# you use this method, or specify a timeout
def from_cache(key, expires_in: nil)
reset_cache! if cache.nil?
cached_value = get_from_cache(key, expires_in)
return cached_value if cached_value.present?
# cache miss or timeout
value = self.find(key)
set_in_cache(key, value)
value
end
def reset_cache!(hard: false)
@cache = {}
@strategic_reserve = {} if hard
end
private
def get_from_cache(key, timeout)
return nil unless cache.key?(key)
cache_entry = cache[key]
return cache_entry[:value] if timeout.nil?
threshold = (Time.zone.now - timeout).to_i
return cache_entry[:value] if cache_entry[:timestamp] > threshold
end
def set_in_cache(key, value)
cache[key] = {value: value, timestamp: Time.zone.now.to_i}
end
def init_values(hash)
hash.each do |parent_key, settings|
settings.each do |child_key, value|
store_put("#{parent_key}/#{child_key}", value)
end
end
end
def store_get(key)
begin
# store all values that we get here to
# kind-of recover in case of big failure
@strategic_reserve ||= {}
consul_value = Canvas.timeout_protection('consul', {raise_on_timeout: true}) do
Diplomat::Kv.get("#{KV_NAMESPACE}/#{key}", {recurse: true, consistency: 'stale'})
end
@strategic_reserve[key] = consul_value
consul_value
rescue Faraday::ConnectionFailed,
Faraday::ClientError,
Timeout::Error,
TimeoutCutoff => exception
if @strategic_reserve.key?(key)
# we have an old value for this key, log the error but recover
Canvas::Errors.capture_exception(:consul, exception)
return @strategic_reserve[key]
else
# didn't have an old value cached, raise the error
raise
end
end
end
def store_put(key, value)
Canvas.timeout_protection('consul') do
Diplomat::Kv.put("#{KV_NAMESPACE}/#{key}", value)
end
end
end
end
end