X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=lib%2Fmcollective%2Fcache.rb;fp=lib%2Fmcollective%2Fcache.rb;h=50432d25b520cc4e8c2e80076a6fe1c0f6aa4d73;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/lib/mcollective/cache.rb b/lib/mcollective/cache.rb new file mode 100644 index 0000000..50432d2 --- /dev/null +++ b/lib/mcollective/cache.rb @@ -0,0 +1,149 @@ +module MCollective + # A class to manage a number of named caches. Each cache can have a unique + # timeout for keys in it and each cache has an independent Mutex protecting + # access to it. + # + # This cache is setup early in the process of loading the mcollective + # libraries, before any threads are created etc making it suitable as a + # cross thread cache or just a store for Mutexes you need to use across + # threads like in an Agent or something. + # + # # sets up a new cache, noop if it already exist + # Cache.setup(:ddl, 600) + # => true + # + # # writes an item to the :ddl cache, this item will + # # be valid on the cache for 600 seconds + # Cache.write(:ddl, :something, "value") + # => "value" + # + # # reads from the cache, read failures due to non existing + # # data or expired data will raise an exception + # Cache.read(:ddl, :something) + # => "value" + # + # # time left till expiry, raises if nothing is found + # Cache.ttl(:ddl, :something) + # => 500 + # + # # forcibly evict something from the cache + # Cache.invalidate!(:ddl, :something) + # => "value" + # + # # deletes an entire named cache and its mutexes + # Cache.delete!(:ddl) + # => true + # + # # you can also just use this cache as a global mutex store + # Cache.setup(:mylock) + # + # Cache.synchronize(:mylock) do + # do_something() + # end + # + module Cache + extend Translatable + + # protects access to @cache_locks and top level @cache + @locks_mutex = Mutex.new + + # stores a mutex per named cache + @cache_locks = {} + + # the named caches protected by items in @cache_locks + @cache = {} + + def self.setup(cache_name, ttl=300) + @locks_mutex.synchronize do + break if @cache_locks.include?(cache_name) + + @cache_locks[cache_name] = Mutex.new + + @cache_locks[cache_name].synchronize do + @cache[cache_name] = {:max_age => Float(ttl)} + end + end + + true + end + + def self.check_cache!(cache_name) + raise_code(:PLMC13, "Could not find a cache called '%{cache_name}'", :debug, :cache_name => cache_name) unless has_cache?(cache_name) + end + + def self.has_cache?(cache_name) + @locks_mutex.synchronize do + @cache.include?(cache_name) + end + end + + def self.delete!(cache_name) + check_cache!(cache_name) + + @locks_mutex.synchronize do + @cache_locks.delete(cache_name) + @cache.delete(cache_name) + end + + true + end + + def self.write(cache_name, key, value) + check_cache!(cache_name) + + @cache_locks[cache_name].synchronize do + @cache[cache_name][key] ||= {} + @cache[cache_name][key][:cache_create_time] = Time.now + @cache[cache_name][key][:value] = value + end + + value + end + + def self.read(cache_name, key) + check_cache!(cache_name) + + unless ttl(cache_name, key) > 0 + raise_code(:PLMC11, "Cache expired on '%{cache_name}' key '%{item}'", :debug, :cache_name => cache_name, :item => key) + end + + log_code(:PLMC12, "Cache hit on '%{cache_name}' key '%{item}'", :debug, :cache_name => cache_name, :item => key) + + @cache_locks[cache_name].synchronize do + @cache[cache_name][key][:value] + end + end + + def self.ttl(cache_name, key) + check_cache!(cache_name) + + @cache_locks[cache_name].synchronize do + unless @cache[cache_name].include?(key) + raise_code(:PLMC15, "No item called '%{item}' for cache '%{cache_name}'", :debug, :cache_name => cache_name, :item => key) + end + + @cache[cache_name][:max_age] - (Time.now - @cache[cache_name][key][:cache_create_time]) + end + end + + def self.synchronize(cache_name) + check_cache!(cache_name) + + raise_code(:PLMC14, "No block supplied to synchronize on cache '%{cache_name}'", :debug, :cache_name => cache_name) unless block_given? + + @cache_locks[cache_name].synchronize do + yield + end + end + + def self.invalidate!(cache_name, key) + check_cache!(cache_name) + + @cache_locks[cache_name].synchronize do + return false unless @cache[cache_name].include?(key) + + @cache[cache_name].delete(key) + end + end + end +end