💨 Aeon::Refresher – 4 + 1 Architecture¶
Breathes new life into stale caches.
1️⃣ Logical View¶
Purpose:
Proactively refresh cached query results before they expire.
Acts as Aeon’s self-healing temporal daemon.
Responsibilities
- Detect near-expiry keys (TTL < threshold).
- Recompute occurrences via Timekeeper.
- Write fresh values back to Redis.
- Emit Argus heartbeat metrics.
2️⃣ Process View¶
- Heartbeat job runs every N minutes.
- Refresher scans
aeon:cache:*keys. - If TTL < 30 min → load window metadata.
- Calls
Aeon::Timekeeper.within(span: window)to rebuild. - Stores new payload with extended TTL.
- Logs
aeon.refresher.refreshed.
3️⃣ Development View¶
File: app/services/aeon/refresher.rb
class Aeon::Refresher
THRESHOLD = 30.minutes
def self.run
keys = Redis.current.keys("aeon:cache:*")
keys.each do |key|
ttl = Redis.current.ttl(key)
next unless ttl.between?(0, THRESHOLD.to_i)
period = decode_period_from_key(key)
refreshed = Aeon::Timekeeper.within(span: period)
Redis.current.set(key, JSON.dump(refreshed), ex: Aeon::CacheManager::TTL_DEFAULT)
Argus.emit("aeon.refresher.refreshed", meta: { key:, ttl: })
end
end
def self.decode_period_from_key(key)
# optional encoded metadata for reconstruction
end
end
4️⃣ Physical View¶
| Attribute | Description |
|---|---|
| Runtime | Sidekiq / Cron job (Aeon::Heartbeat) |
| Store | Redis and Postgres |
| Dependencies | Timekeeper, CacheManager |
➕ 1 Scenario View¶
Scenario A: Cache Prewarming¶
At 23:45, Refresher detects next day’s window TTL → rebuilds early.
Scenario B: Burst Invalidations¶
Several timestamps updated → Refresher regenerates windows in parallel.
Observability:
Metrics aeon_refresher_runs_total, aeon_refresher_duration_seconds.