File: lru_cache.rb

package info (click to toggle)
ruby-puppet-forge 5.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,196 kB
  • sloc: ruby: 2,397; makefile: 3
file content (78 lines) | stat: -rw-r--r-- 2,525 bytes parent folder | download
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