Class: Restless::RecoveryCache
- Inherits:
-
Object
- Object
- Restless::RecoveryCache
- Defined in:
- lib/restless/caches.rb
Overview
CACHE-010..015. Agent Recovery messages keyed by error fingerprint.
Performance is the whole point. The lookup sits on the hot path of every 4xx/5xx, so it is synchronous, in-process and never does I/O. A cold miss injects nothing and returns immediately; the server piggybacks the message onto the next upload response, so the SECOND occurrence hits.
"No message for this fingerprint" is itself a cacheable answer, stored as nil, so a cold miss does not stay cold on every request. The negative TTL is shorter so a freshly-attached dashboard message starts working within minutes.
Constant Summary collapse
- DEFAULT_TTL_MS =
CACHE-014: 1 hour, positive
3_600_000- DEFAULT_NEGATIVE_TTL_MS =
CACHE-014: 5 minutes, negative
300_000- MISS =
Distinguishes "cached as absent" (nil) from "never seen" (MISS).
Object.new.freeze
Instance Method Summary collapse
- #clear ⇒ Object
-
#get(key) ⇒ Object
Returns a String (inject it), nil (the server confirmed there is none), or MISS (never seen, or expired).
-
#initialize(ttl_ms = DEFAULT_TTL_MS, negative_ttl_ms = DEFAULT_NEGATIVE_TTL_MS) ⇒ RecoveryCache
constructor
A new instance of RecoveryCache.
- #invalidate(key) ⇒ Object
-
#lookup(key) ⇒ Object
Convenience for the hot path: the message to inject, or nil.
- #set(key, message) ⇒ Object
-
#set_negative_unless_present(key) ⇒ Object
CACHE-013.
- #size ⇒ Object
Constructor Details
#initialize(ttl_ms = DEFAULT_TTL_MS, negative_ttl_ms = DEFAULT_NEGATIVE_TTL_MS) ⇒ RecoveryCache
Returns a new instance of RecoveryCache.
83 84 85 86 87 88 |
# File 'lib/restless/caches.rb', line 83 def initialize(ttl_ms = DEFAULT_TTL_MS, negative_ttl_ms = DEFAULT_NEGATIVE_TTL_MS) @ttl_ms = ttl_ms @negative_ttl_ms = negative_ttl_ms @entries = {} @mutex = Mutex.new end |
Instance Method Details
#clear ⇒ Object
133 134 135 |
# File 'lib/restless/caches.rb', line 133 def clear @mutex.synchronize { @entries.clear } end |
#get(key) ⇒ Object
Returns a String (inject it), nil (the server confirmed there is none), or MISS (never seen, or expired).
92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/restless/caches.rb', line 92 def get(key) @mutex.synchronize do entry = @entries[key] next MISS if entry.nil? ttl = entry[:message].nil? ? @negative_ttl_ms : @ttl_ms if now_ms - entry[:ts] > ttl # CACHE-015 @entries.delete(key) next MISS end entry[:message] end end |
#invalidate(key) ⇒ Object
129 130 131 |
# File 'lib/restless/caches.rb', line 129 def invalidate(key) @mutex.synchronize { @entries.delete(key) } end |
#lookup(key) ⇒ Object
Convenience for the hot path: the message to inject, or nil.
107 108 109 110 |
# File 'lib/restless/caches.rb', line 107 def lookup(key) value = get(key) value.is_a?(String) ? value : nil end |
#set(key, message) ⇒ Object
112 113 114 |
# File 'lib/restless/caches.rb', line 112 def set(key, ) @mutex.synchronize { @entries[key] = { message: , ts: now_ms } } end |
#set_negative_unless_present(key) ⇒ Object
CACHE-013. A negative entry must never overwrite a positive one.
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/restless/caches.rb', line 117 def set_negative_unless_present(key) @mutex.synchronize do entry = @entries[key] if entry ttl = entry[:message].nil? ? @negative_ttl_ms : @ttl_ms fresh = now_ms - entry[:ts] <= ttl next if fresh && !entry[:message].nil? end @entries[key] = { message: nil, ts: now_ms } end end |
#size ⇒ Object
137 138 139 |
# File 'lib/restless/caches.rb', line 137 def size @mutex.synchronize { @entries.size } end |