File: memory_store.rb

package info (click to toggle)
ruby-circuitbox 2.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 140 kB
  • sloc: ruby: 533; makefile: 4
file content (83 lines) | stat: -rw-r--r-- 2,098 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
79
80
81
82
83
# frozen_string_literal: true

require_relative 'time_helper/monotonic'
require_relative 'memory_store/container'

class Circuitbox
  class MemoryStore
    include TimeHelper::Monotonic

    def initialize(compaction_frequency: 60)
      @store = {}
      @mutex = Mutex.new
      @compaction_frequency = compaction_frequency
      @compact_after = current_second + compaction_frequency
    end

    def store(key, value, opts = {})
      @mutex.synchronize do
        @store[key] = Container.new(value: value, expiry: opts.fetch(:expires, 0))
        value
      end
    end

    def increment(key, amount = 1, opts = {})
      seconds_to_expire = opts.fetch(:expires, 0)

      @mutex.synchronize do
        existing_container = fetch_container(key)

        # reusing the existing container is a small optimization
        # to reduce the amount of objects created
        if existing_container
          existing_container.expires_after(seconds_to_expire)
          existing_container.value += amount
        else
          @store[key] = Container.new(value: amount, expiry: seconds_to_expire)
          amount
        end
      end
    end

    def load(key, _opts = {})
      @mutex.synchronize { fetch_container(key)&.value }
    end

    def values_at(*keys, **_opts)
      @mutex.synchronize do
        current_time = current_second
        keys.map! { |key| fetch_container(key, current_time)&.value }
      end
    end

    def key?(key)
      @mutex.synchronize { !fetch_container(key).nil? }
    end

    def delete(key)
      @mutex.synchronize { @store.delete(key) }
    end

    private

    def fetch_container(key, current_time = current_second)
      compact(current_time) if @compact_after < current_time

      container = @store[key]

      return unless container

      if container.expired_at?(current_time)
        @store.delete(key)
        nil
      else
        container
      end
    end

    def compact(current_time)
      @store.delete_if { |_, value| value.expired_at?(current_time) }
      @compact_after = current_time + @compaction_frequency
    end
  end
end