Class: Parse::Cache::Pool

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

Overview

Moneta-compatible facade over a ConnectionPool of Moneta stores. The Faraday caching middleware only calls four methods on its store (‘[]`, `key?`, `delete`, `store`); this class checks out a backend for each of them via `@pool.with`.

Why a pool: a single Moneta-Redis store wraps one Redis connection. Under a multi-threaded Puma worker (or any concurrent caller), threads serialize on that connection’s mutex. A pool of N stores lets up to N cache calls run in parallel.

Note that a cache hit costs two checkouts (‘key?` then `[]`). That is accepted to keep behavior identical to a plain Moneta store; callers should size the pool with that in mind (default 5, which matches the Puma default thread count).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(size: 5, timeout: 5) { ... } ⇒ Pool

Returns a new instance of Pool.

Parameters:

  • size (Integer) (defaults to: 5)

    number of pooled backend stores.

  • timeout (Numeric) (defaults to: 5)

    seconds to wait for a checkout before raising ‘ConnectionPool::TimeoutError`.

Yields:

  • Block invoked to build a single backend store. Must return a Moneta store responding to ‘[]`, `key?`, `delete`, `store`.

Raises:

  • (ArgumentError)


32
33
34
35
36
# File 'lib/parse/cache/pool.rb', line 32

def initialize(size: 5, timeout: 5, &block)
  raise ArgumentError, "Parse::Cache::Pool requires a block that builds a Moneta store" unless block_given?
  @pool = ConnectionPool.new(size: size, timeout: timeout, &block)
  @closed = false
end

Instance Attribute Details

#poolObject (readonly)

The wrapped ConnectionPool instance.



25
26
27
# File 'lib/parse/cache/pool.rb', line 25

def pool
  @pool
end

Instance Method Details

#[](key) ⇒ Object



38
39
40
# File 'lib/parse/cache/pool.rb', line 38

def [](key)
  @pool.with { |store| store[key] }
end

#clearObject

Clear the underlying backend. Pooled Moneta stores all point at the same Redis DB, so a single checkout suffices — issuing ‘clear` on one connection flushes the DB for every connection.



72
73
74
75
# File 'lib/parse/cache/pool.rb', line 72

def clear
  @pool.with { |store| store.clear if store.respond_to?(:clear) }
  self
end

#closeObject

Close all pooled backends. Safe to call multiple times — repeat calls are no-ops. ‘ConnectionPool#shutdown` raises `ConnectionPool::PoolShuttingDownError` on a second invocation, so we gate it with a `@closed` flag.



81
82
83
84
85
# File 'lib/parse/cache/pool.rb', line 81

def close
  return if @closed
  @closed = true
  @pool.shutdown { |store| store.close if store.respond_to?(:close) }
end

#create(key, value, options = {}) ⇒ Object

Atomic SETNX-style write. Required by ‘Parse::CreateLock` to acquire cross-process locks against Redis-backed stores. Forwards to the underlying Moneta store’s ‘#create`, which returns `true` only if the key was absent and is now set.



58
59
60
# File 'lib/parse/cache/pool.rb', line 58

def create(key, value, options = {})
  @pool.with { |store| store.create(key, value, options) }
end

#delete(key) ⇒ Object



46
47
48
# File 'lib/parse/cache/pool.rb', line 46

def delete(key)
  @pool.with { |store| store.delete(key) }
end

#increment(key, amount = 1, options = {}) ⇒ Object

Atomic counter increment. Forwarded for parity with Moneta so callers expecting the full Moneta surface (counters, rate limits) work transparently through the pool.



65
66
67
# File 'lib/parse/cache/pool.rb', line 65

def increment(key, amount = 1, options = {})
  @pool.with { |store| store.increment(key, amount, options) }
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/parse/cache/pool.rb', line 42

def key?(key)
  @pool.with { |store| store.key?(key) }
end

#store(key, value, options = {}) ⇒ Object



50
51
52
# File 'lib/parse/cache/pool.rb', line 50

def store(key, value, options = {})
  @pool.with { |store| store.store(key, value, options) }
end