Class: MaquinaStream::ComponentCache

Inherits:
Object
  • Object
show all
Defined in:
lib/maquina_stream/component_cache.rb

Overview

Caches pure render results: component partials, and whole sealed documents.

A component render is a pure function of its locals, and during a stream the same locals recur on every single frame: a code block that closed twenty frames ago is re-rendered, identically, twenty more times. Profiling a 20KB message put 56ms of a 68ms frame inside ActionView for exactly this reason — 36 fences at roughly 1.5ms each. Markdown parsing was 0.7ms of it.

Two tiers, because they solve different problems:

Tier What it buys
process-local nanoseconds, and hits nearly always during one stream
store survives a restart, a reload, and other workers. Optional

The store tier is deliberately opt-in. Solid Cache is backed by the database, so a read is a query — worth it for a cold page reload, wasteful on a hot loop that the local tier already answers. Set it when a message is likely to be rendered by more than one process; leave it nil otherwise.

MaquinaStream::ComponentCache.store = Rails.cache

Caching does nothing for bandwidth. It is a CPU fix only.

Constant Summary collapse

MAX_ENTRIES =

How many entries the process-local tier holds before evicting the least recently used. A cap, not a target: the working set of one stream is far smaller.

1_024
NAMESPACE =

Key prefix for the store tier. Versioned, so a change to what is cached invalidates the old entries rather than deserializing them.

"ms:component:v1"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.store ⇒ Object

The optional second-tier cache store — anything answering read(key) and write(key, value), Rails.cache in practice. Nil by default, and deliberately so: see the note above.



45
46
47
# File 'lib/maquina_stream/component_cache.rb', line 45

def store
  @store
end

Class Method Details

.clear! ⇒ Object

Empties the process-local tier. Does not touch #store.



61
62
63
# File 'lib/maquina_stream/component_cache.rb', line 61

def clear!
  mutex.synchronize { entries.clear }
end

.fetch(*key_parts) ⇒ Object

Returns the cached value for key_parts, calling the block to compute it on a miss. The key is the digest of the parts joined, so they must cover everything the block's result depends on.



50
51
52
53
54
55
56
57
58
# File 'lib/maquina_stream/component_cache.rb', line 50

def fetch(*key_parts)
  key = digest(key_parts)

  local(key) || from_store(key) || begin
    value = yield
    write(key, value)
    value
  end
end

.reset_stats! ⇒ Object

Zeroes the hit and miss counters.



76
77
78
# File 'lib/maquina_stream/component_cache.rb', line 76

def reset_stats!
  @stats = {hits: 0, misses: 0}
end

.size ⇒ Object

How many entries the process-local tier holds, capped at MAX_ENTRIES.



66
# File 'lib/maquina_stream/component_cache.rb', line 66

def size = entries.size

.stats ⇒ Object

Hit rate is worth being able to see: a cache that never hits is worse than no cache, and a cache that always hits on a changing document is a key that is not capturing what it should.



71
72
73
# File 'lib/maquina_stream/component_cache.rb', line 71

def stats
  @stats ||= {hits: 0, misses: 0}
end