Class: SuperSettings::LocalCache
- Inherits:
-
Object
- Object
- SuperSettings::LocalCache
- Defined in:
- lib/super_settings/local_cache.rb
Overview
Cache that stores the settings in memory so they can be looked up without any network overhead. All of the settings will be loaded in the cache and the database will only be checked every few seconds for changes so that lookups are very fast.
The cache is thread safe and it ensures that only a single thread will ever be trying to update the cache at a time to avoid any dog piling effects.
Instance Attribute Summary collapse
-
#refresh_interval ⇒ Object
Number of seconds that the cache will be considered fresh.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Get a setting value from the cache.
-
#include?(key) ⇒ Boolean
private
Check if the cache includes a key.
-
#initialize(refresh_interval:) ⇒ LocalCache
constructor
A new instance of LocalCache.
-
#load_settings(asynchronous = false) ⇒ void
Load all the settings from the database into the cache.
-
#loaded? ⇒ Boolean
Return true if the cache has already been loaded from the database.
-
#refresh(asynchronous = false) ⇒ void
Load only settings that have changed since the last load.
-
#reset ⇒ void
Reset the cache to an unloaded state.
-
#size ⇒ Object
private
Get the number of entries in the cache.
-
#to_h ⇒ Hash
Return the cached settings as a key/value hash.
-
#update_setting(setting) ⇒ Object
private
Update a single setting directly into the cache.
-
#wait_for_load ⇒ Object
private
Wait for the settings to be loaded if a new load was triggered.
Constructor Details
#initialize(refresh_interval:) ⇒ LocalCache
Returns a new instance of LocalCache.
24 25 26 27 28 |
# File 'lib/super_settings/local_cache.rb', line 24 def initialize(refresh_interval:) @refresh_interval = refresh_interval @lock = Mutex.new reset end |
Instance Attribute Details
#refresh_interval ⇒ Object
Number of seconds that the cache will be considered fresh. The database will only be checked for changed settings at most this often.
21 22 23 |
# File 'lib/super_settings/local_cache.rb', line 21 def refresh_interval @refresh_interval end |
Instance Method Details
#[](key) ⇒ Object
Get a setting value from the cache.
This method will periodically check the cache for freshness and update the cache from the database if there are any differences.
Cache misses will be stored in the cache so that a request for a missing setting does not hit the database every time. This does mean that that you should not call this method with a large number of dynamically generated keys since that could lead to memory bloat.
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 |
# File 'lib/super_settings/local_cache.rb', line 40 def [](key) ensure_cache_up_to_date! key = key.to_s value = @cache[key] if value.nil? && !@cache.include?(key) if @refreshing value = NOT_DEFINED else setting = Setting.find_by_key(key) value = (setting ? setting.value : NOT_DEFINED) # Guard against caching too many cache missees; at some point it's better to slam # the database rather than run out of memory. if setting || @cache.size < 100_000 @lock.synchronize do # For case where one thread could be iterating over the cache while it's updated causing an error @cache = @cache.merge(key => value).freeze end end end end return nil if value == NOT_DEFINED value end |
#include?(key) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if the cache includes a key. Note that this will return true if you have tried to fetch a non-existent key since the cache will store that as undefined. This method is provided for testing purposes.
74 75 76 |
# File 'lib/super_settings/local_cache.rb', line 74 def include?(key) @cache.include?(key.to_s) end |
#load_settings(asynchronous = false) ⇒ void
This method returns an undefined value.
Load all the settings from the database into the cache.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/super_settings/local_cache.rb', line 112 def load_settings(asynchronous = false) return if @refreshing @lock.synchronize do return if @refreshing @refreshing = true @next_check_at = Time.now + @refresh_interval end load_block = lambda do begin values = {} start_time = Time.now Setting.active.each do |setting| values[setting.key] = setting.value.freeze end set_cache_values(start_time) { values } ensure @refreshing = false end end if asynchronous Thread.new(&load_block) else load_block.call end end |
#loaded? ⇒ Boolean
Return true if the cache has already been loaded from the database.
104 105 106 |
# File 'lib/super_settings/local_cache.rb', line 104 def loaded? !!@last_refreshed end |
#refresh(asynchronous = false) ⇒ void
This method returns an undefined value.
Load only settings that have changed since the last load.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/super_settings/local_cache.rb', line 146 def refresh(asynchronous = false) last_refresh_time = @last_refreshed return if last_refresh_time.nil? return if @refreshing @lock.synchronize do return if @refreshing @next_check_at = Time.now + @refresh_interval return if @cache.empty? @refreshing = true end refresh_block = lambda do begin last_db_update = Setting.last_updated_at if last_db_update.nil? || last_db_update >= last_refresh_time - 1 merge_load(last_refresh_time) end ensure @refreshing = false end end if asynchronous Thread.new(&refresh_block) else refresh_block.call end end |
#reset ⇒ void
This method returns an undefined value.
Reset the cache to an unloaded state.
181 182 183 184 185 186 187 188 |
# File 'lib/super_settings/local_cache.rb', line 181 def reset @lock.synchronize do @cache = {}.freeze @last_refreshed = nil @next_check_at = Time.now + @refresh_interval @refreshing = false end end |
#size ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Get the number of entries in the cache. Note that this will include cache misses as well.
82 83 84 85 |
# File 'lib/super_settings/local_cache.rb', line 82 def size ensure_cache_up_to_date! @cache.size end |
#to_h ⇒ Hash
Return the cached settings as a key/value hash. Calling this method will load the cache with the settings if they have not already been loaded.
91 92 93 94 95 96 97 98 99 |
# File 'lib/super_settings/local_cache.rb', line 91 def to_h ensure_cache_up_to_date! hash = {} @cache.each do |key, data| value, _ = data hash[key] = value unless value == NOT_DEFINED end hash end |
#update_setting(setting) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Update a single setting directly into the cache.
202 203 204 205 206 207 208 |
# File 'lib/super_settings/local_cache.rb', line 202 def update_setting(setting) return if Coerce.blank?(setting.key) @lock.synchronize do @cache = @cache.merge(setting.key => setting.value) end end |
#wait_for_load ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Wait for the settings to be loaded if a new load was triggered. This can happen asynchronously.
212 213 214 215 216 217 218 |
# File 'lib/super_settings/local_cache.rb', line 212 def wait_for_load loop do return unless @refreshing sleep(0.001) end end |