Class: ResilientReads::QueryCache

Inherits:
Object
  • Object
show all
Defined in:
lib/resilient_reads/query_cache.rb

Overview

Caches SQL pattern-matching results (read vs write classification) so the regex does not need to run on every identical query string.

Mirrors the caching concept from active_record_proxy_adapters but is simpler — we only cache the boolean result of write_query? and skip_replica_routing? keyed by the SQL string.

Thread-safe via a Mutex around the internal Hash. Uses an LRU eviction strategy when the cache exceeds max_size.

Constant Summary collapse

DEFAULT_MAX_SIZE =
10_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_size: DEFAULT_MAX_SIZE, key_prefix: "rr_") ⇒ QueryCache

Returns a new instance of QueryCache.



18
19
20
21
22
23
24
25
# File 'lib/resilient_reads/query_cache.rb', line 18

def initialize(max_size: DEFAULT_MAX_SIZE, key_prefix: "rr_")
  @max_size = max_size
  @key_prefix = key_prefix
  @store = {}
  @mutex = Mutex.new
  @hits = 0
  @misses = 0
end

Instance Attribute Details

#key_prefixObject (readonly)

Returns the value of attribute key_prefix.



16
17
18
# File 'lib/resilient_reads/query_cache.rb', line 16

def key_prefix
  @key_prefix
end

#max_sizeObject (readonly)

Returns the value of attribute max_size.



16
17
18
# File 'lib/resilient_reads/query_cache.rb', line 16

def max_size
  @max_size
end

Instance Method Details

#clear!Object



64
65
66
67
68
69
70
# File 'lib/resilient_reads/query_cache.rb', line 64

def clear!
  @mutex.synchronize do
    @store.clear
    @hits = 0
    @misses = 0
  end
end

#fetch(sql) ⇒ Object

Fetch a cached value or compute it from the block. The block receives the SQL and should return the value to cache.

cache.fetch(sql) { |s| ResilientReads.write_query?(s) }


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/resilient_reads/query_cache.rb', line 32

def fetch(sql)
  key = cache_key(sql)

  @mutex.synchronize do
    if @store.key?(key)
      @hits += 1
      # Move to end (most recently used)
      value = @store.delete(key)
      @store[key] = value
      return value
    end
  end

  value = yield(sql)

  @mutex.synchronize do
    @misses += 1
    @store[key] = value
    evict! if @store.size > @max_size
  end

  value
end

#sizeObject



56
57
58
# File 'lib/resilient_reads/query_cache.rb', line 56

def size
  @mutex.synchronize { @store.size }
end

#statsObject



60
61
62
# File 'lib/resilient_reads/query_cache.rb', line 60

def stats
  @mutex.synchronize { { hits: @hits, misses: @misses, size: @store.size } }
end