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
|
require 'digest'
module PuppetForge
# Implements a simple LRU cache. This is used internally by the
# {PuppetForge::V3::Base} class to cache API responses.
class LruCache
# Takes a list of strings (or objects that respond to #to_s) and
# returns a SHA256 hash of the strings joined with colons. This is
# a convenience method for generating cache keys. Cache keys do not
# have to be SHA256 hashes, but they must be unique.
def self.new_key(*string_args)
Digest(:SHA256).hexdigest(string_args.map(&:to_s).join(':'))
end
# @return [Integer] the maximum number of items to cache.
attr_reader :max_size
# @param max_size [Integer] the maximum number of items to cache. This can
# be overridden by setting the PUPPET_FORGE_MAX_CACHE_SIZE environment
# variable.
def initialize(max_size = 30)
raise ArgumentError, "max_size must be a positive integer" unless max_size.is_a?(Integer) && max_size > 0
@max_size = ENV['PUPPET_FORGE_MAX_CACHE_SIZE'] ? ENV['PUPPET_FORGE_MAX_CACHE_SIZE'].to_i : max_size
@cache = {}
@lru = []
@semaphore = Mutex.new
end
# Retrieves a value from the cache.
# @param key [Object] the key to look up in the cache
# @return [Object] the cached value for the given key, or nil if
# the key is not present in the cache.
def get(key)
if cache.key?(key)
semaphore.synchronize do
# If the key is present, move it to the front of the LRU
# list.
lru.delete(key)
lru.unshift(key)
end
cache[key]
end
end
# Adds a value to the cache.
# @param key [Object] the key to add to the cache
# @param value [Object] the value to add to the cache
def put(key, value)
semaphore.synchronize do
if cache.key?(key)
# If the key is already present, delete it from the LRU list.
lru.delete(key)
elsif cache.size >= max_size
# If the cache is full, remove the least recently used item.
cache.delete(lru.pop)
end
# Add the key to the front of the LRU list and add the value
# to the cache.
lru.unshift(key)
cache[key] = value
end
end
# Clears the cache.
def clear
semaphore.synchronize do
cache.clear
lru.clear
end
end
private
# Makes testing easier as these can be accessed directly with #send.
attr_reader :cache, :lru, :semaphore
end
end
|