Class: Caddy::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/caddy/cache.rb

Constant Summary collapse

DEFAULT_REFRESH_INTERVAL =

Default refresh interval, in seconds

60
REFRESH_INTERVAL_JITTER_PCT =

Percentage to randomly smooth the refresh interval to avoid stampeding herd on expiration

0.15

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key) ⇒ Cache

Create a new periodically updated cache.

Parameters:

  • key (Symbol)

    the name of this cache



25
26
27
28
29
30
# File 'lib/caddy/cache.rb', line 25

def initialize(key)
  @task = nil
  @refresh_interval = DEFAULT_REFRESH_INTERVAL
  @cache = nil
  @key = key
end

Instance Attribute Details

#error_handlerProc

called when exceptions or timeouts happen within the refresher

Returns:



21
22
23
# File 'lib/caddy/cache.rb', line 21

def error_handler
  @error_handler
end

#refresh_intervalNumeric

Returns number of seconds between calls to #refresher; timeout is set to {#refresher} - 0.1.

Returns:

  • (Numeric)

    number of seconds between calls to #refresher; timeout is set to {#refresher} - 0.1



16
17
18
# File 'lib/caddy/cache.rb', line 16

def refresh_interval
  @refresh_interval
end

#refresherProc

Returns called on interval #refresh_interval with the returned object used as the cache.

Returns:

  • (Proc)

    called on interval #refresh_interval with the returned object used as the cache



12
13
14
# File 'lib/caddy/cache.rb', line 12

def refresher
  @refresher
end

Instance Method Details

#[](k) ⇒ Object

Convenience method for getting the value of the refresher-returned object at path k, assuming the refresher-returned value responds to [].

If not, #cache can be used instead to access the refresher-returned object.

Parameters:

  • k

    key to access from the refresher-returned cache.



37
38
39
# File 'lib/caddy/cache.rb', line 37

def [](k)
  cache[k]
end

#cacheObject

Returns the refresher-produced value that is used as the cache.



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/caddy/cache.rb', line 42

def cache
  raise "Please run `Caddy.start` before attempting to access the cache" unless @task && @task.running?

  unless @cache
    logger.warn "Caddy cache access of :#{@key} before initial load; doing synchronous load."\
                "Please allow some more time for your app to start up."
    refresh
  end

  @cache
end

#refreshObject

Updates the internal cache object.

Freezes the result to avoid mutation errors.



101
102
103
# File 'lib/caddy/cache.rb', line 101

def refresh
  @cache = refresher.call.freeze
end

#startObject

Starts the period refresh cycle.

Every refresh_interval seconds – smoothed by a jitter amount (a random amount +/- REFRESH_INTERVAL_JITTER_PCT) – the refresher lambda is called and the results stored in cache.

Note that the result of the refresh is frozen to avoid multithreading mutations.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/caddy/cache.rb', line 60

def start
  unless refresher && refresher.respond_to?(:call)
    raise "Please set your cache refresher via `Caddy[:#{@key}].refresher = -> { <code that returns a value> }`"
  end

  raise "`Caddy[:#{@key}].refresh_interval` must be > 0" unless refresh_interval > 0

  jitter_amount = [0.1, refresh_interval * REFRESH_INTERVAL_JITTER_PCT].max
  interval = refresh_interval + rand(-jitter_amount...jitter_amount)
  timeout_interval = [interval - 1, 0.1].max

  stop # stop any existing task from running

  @task = Concurrent::TimerTask.new(
    run_now: true,
    execution_interval: interval,
    timeout_interval: timeout_interval
  ) do
    refresh
    nil # no need for the {#Concurrent::TimerTask} to keep a reference to the value
  end

  @task.add_observer(Caddy::TaskObserver.new(error_handler, @key))

  logger.debug "Starting Caddy refresher for :#{@key}, updating every #{interval.round(1)}s."

  @task.execute

  @task.running?
end

#stopObject

Stops the current executing refresher.

The current cache value is persisted even if the task is stopped.



94
95
96
# File 'lib/caddy/cache.rb', line 94

def stop
  @task.shutdown if @task && @task.running?
end