Class: AtomicCache::AtomicCacheClient

Inherits:
Object
  • Object
show all
Defined in:
lib/atomic_cache/atomic_cache_client.rb

Constant Summary collapse

DEFAULT_MAX_RETRIES =
5
DEFAULT_GENERATE_TIME_MS =

30 seconds

30000
BACKOFF_DURATION_MS =
50

Instance Method Summary collapse

Constructor Details

#initialize(storage: nil, timestamp_manager: nil, default_options: {}, logger: nil, metrics: nil) ⇒ AtomicCacheClient

Returns a new instance of AtomicCacheClient.

Parameters:

  • storage (Object) (defaults to: nil)

    Cache storage adapter

  • timestamp_manager (Object) (defaults to: nil)

    Timestamp manager

  • default_options (Hash) (defaults to: {})

    Default fetch options

  • logger (Object) (defaults to: nil)

    Logger

  • metrics (Object) (defaults to: nil)

    Metrics client

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
# File 'lib/atomic_cache/atomic_cache_client.rb', line 18

def initialize(storage: nil, timestamp_manager: nil, default_options: {}, logger: nil, metrics: nil)
  @default_options = (DefaultConfig.instance.default_options&.clone || {}).merge(default_options || {})
  @timestamp_manager = timestamp_manager
  @logger = logger || DefaultConfig.instance.logger
  @metrics = metrics || DefaultConfig.instance.metrics
  @storage = storage || DefaultConfig.instance.cache_storage

  raise ArgumentError.new("`timestamp_manager` required but none given") unless @timestamp_manager.present?
  raise ArgumentError.new("`storage` required but none given") unless @storage.present?
end

Instance Method Details

#fetch(keyspace, options = {}) { ... } ⇒ Object

Attempts to fetch the given keyspace, using an optional block to generate a new value when the cache is expired

Parameters:

  • keyspace (AtomicCache::Keyspace)

    the keyspace to fetch

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :generate_ttl_ms (Numeric) — default: 30000

    Max generate duration in ms

  • :max_retries (Numeric) — default: 5

    Max times to rety in waiting case

  • :backoff_duration_ms (Numeric) — default: 50

    Duration in ms to wait between retries

Yields:

  • Generates a new value when cache is expired



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
# File 'lib/atomic_cache/atomic_cache_client.rb', line 37

def fetch(keyspace, options={}, &blk)
  key = @timestamp_manager.current_key(keyspace)
  tags = ["cache_keyspace:#{keyspace.root}"]

  # happy path: see if the value is there in the key we expect
  value = @storage.read(key, options) if key.present?
  if !value.nil?
    metrics(:increment, 'read.present', tags: tags)
    log(:debug, "Read value from key: '#{key}'")
    return value
  end

  metrics(:increment, 'read.not-present', tags: tags)
  log(:debug, "Cache key `#{key}` not present.")

  # try to generate a new value if another process already isn't
  if block_given?
    new_value = generate_and_store(keyspace, options, tags, &blk)
    return new_value unless new_value.nil?
  end

  # attempt to fall back to the last known value
  value = last_known_value(keyspace, options, tags)
  return value if value.present?

  # wait for the other process if a last known value isn't there
  if key.present?
    return time('wait.run', tags: tags) do
      wait_for_new_value(keyspace, options, tags)
    end
  end

  # At this point, there's no key, value, last known key, or last known value.
  # A block wasn't given or couldn't create a non-nil value making it
  # impossible to do anything else, so bail
  if !key.present?
    metrics(:increment, 'no-key.give-up')
    log(:warn, "Giving up fetching cache keyspace for root `#{keyspace.root}`. No key could be generated.")
  end
  nil
end