In-Memory-Cache
InMemoryCache ist die Default-Cache-Implementierung. Sie ist
eine Map mit drei Dingen obendrauf: LRU-Eviction begrenzt durch
maxEntries, fauler TTL pro Eintrag und einem optionalen
Hintergrund-Sweep, der abgelaufene, nicht mehr angefasste Einträge
zurückgewinnt. In-Process, keine Abhängigkeiten, geht bei Neustart
verloren.
import { InMemoryCache, InMemoryCacheOptions } from 'actor-ts';
// Defaults: maxEntries 10 000, cleanupMs 60 000.const cache = new InMemoryCache();
// Or tune it with the builder:const cacheOptions = InMemoryCacheOptions.create() .withMaxEntries(50_000) .withCleanupMs(30_000);const tuned = new InMemoryCache(cacheOptions);Ein einfaches Objekt ist die Kurzform-Alternative —
new InMemoryCache({ maxEntries: 50_000, cleanupMs: 30_000 }). Eine
TTL wird weiterhin pro Aufruf übergeben (siehe
TTL-Behandlung), nicht beim Konstruieren.
Optionen
Abschnitt betitelt „Optionen“| Feld | Builder | Default | Bedeutung |
|---|---|---|---|
maxEntries | withMaxEntries(n) | 10000 | LRU-Schranke für gespeicherte Einträge. Infinity = unbegrenzt. |
cleanupMs | withCleanupMs(ms) | 60000 | Intervall des Hintergrund-Sweeps für abgelaufene Einträge (ms). 0 / Infinity schaltet den Sweep ab. |
Werte werden einmalig beim Konstruieren auf den gemergten Settings
validiert: maxEntries muss eine positive Ganzzahl (oder Infinity)
sein und cleanupMs eine nicht-negative Zahl (oder Infinity). Ein
ungültiger Wert wirft
OptionsError — der
Builder, ein einfaches Objekt und HOCON treffen alle dieselbe Prüfung.
Wann einsetzen
Abschnitt betitelt „Wann einsetzen“Drei Szenarien:
- Tests — schnell, kein I/O, sauberes Teardown via
close(). - Single-Process-Produktion — ein Prozess, kein Bedarf, Cache-State über Pods zu teilen.
- Dev / lokal — derselbe Code ohne Redis auf dem Laptop.
Für Multi-Process-Deployments nimm stattdessen Redis oder Memcached — sonst hielte jeder Prozess seine eigene, unabhängige Kopie.
Die Operationen
Abschnitt betitelt „Die Operationen“InMemoryCache implementiert die vollständige
Cache-Fläche:
await cache.set('user:1', user, 60_000); // value + optional TTL (ms)const hit = await cache.get<User>('user:1'); // Option<User> — None on miss/expiry
await cache.setIfAbsent('lock:job', '1', 30_000); // true iff it was storedawait cache.incr('ratelimit:1.2.3.4', 60_000); // atomic ++, returns the new count
const many = await cache.mget<User>(['user:1', 'user:2']); // Map<string, User>await cache.mset(new Map([['a', user1], ['b', user2]]), 60_000);
await cache.delete('user:1', 'user:2'); // one or many keysawait cache.close(); // stops the sweep + clears the MapsetIfAbsent ist hier aus einem strukturellen Grund atomar, nicht
aus einem protokollarischen: Lesen und Schreiben liegen im selben
synchronen Block, und die single-threaded Event-Loop kann keinen
anderen Aufrufer dazwischenschieben. Das gilt nur innerhalb
eines Prozesses — zwei Node-Prozesse haben je ihre eigene Map,
ein lock:job-Key sichert zwischen ihnen also nichts ab. Für
prozessübergreifendes Locking nimm
acquireLock über RedisCache.
get gibt ein Option<V> zurück — None bei einem Miss oder
nach Ablauf.
TTL-Behandlung
Abschnitt betitelt „TTL-Behandlung“await cache.set('key', value, 60_000); // expires at now + 60sawait cache.set('key', value); // no TTL — lives until evicted/deletedExpiry hat zwei Pfade. Er ist faul beim Zugriff — die Deadline
eines Eintrags wird bei jedem get, mget, incr und setIfAbsent
geprüft, und ein abgelaufener Eintrag wird an dieser Stelle verworfen.
Ein Hintergrund-Sweep läuft dann alle cleanupMs (Default 60 s)
und gewinnt abgelaufene Einträge zurück, die nie wieder angefasst
werden, damit sie nicht auf einen Read wartend in der Map liegen.
Setze cleanupMs auf 0 / Infinity, um den Sweep abzuschalten und
dich allein auf faule Expiry zu verlassen.
incr setzt die TTL nur, wenn es den Zähler erzeugt (der Wert
wird 1); spätere Inkremente erneuern sie nicht — die richtige
Semantik für einen Fixed-Window-Rate-Limiter.
Standardmäßig begrenzt (LRU)
Abschnitt betitelt „Standardmäßig begrenzt (LRU)“InMemoryCache ist LRU-begrenzt bei maxEntries (Default 10 000):
das Einfügen eines neuen Keys über die Schranke hinaus evictet den am
längsten nicht genutzten Eintrag. get/incr/setIfAbsent zählen
als Nutzung und schieben einen Key ans zuletzt-genutzte Ende, sodass
heiße Keys überleben und kalte zuerst evictet werden. Genau das
verhindert, dass eine Flut eindeutiger, nie erneut gelesener Keys —
etwa angreiferseitig gewählte Idempotency-Key- oder Rate-Limit-Keys —
die Map unbegrenzt wachsen lässt.
Setze maxEntries: Infinity, um die Eviction ganz abzuschalten. Tu
das nur, wenn du den Key-Raum kontrollierst — eine unbegrenzte Map
läuft dem Prozess irgendwann in einen OOM.
Über das System teilen
Abschnitt betitelt „Über das System teilen“import { CacheExtensionId, InMemoryCache } from 'actor-ts';
// The extension's `default` cache is an InMemoryCache out of the box:const cache = system.extension(CacheExtensionId).cache();
// Override the default, or register a separate named cache:system.extension(CacheExtensionId).setCache('default', new InMemoryCache());const sessions = system.extension(CacheExtensionId).cache('sessions');system.extension(CacheExtensionId).cache(name) löst einen Cache
über seinen Namen auf — der Name default ist ein InMemoryCache,
solange du ihn nicht ersetzt (via setCache, registerCache oder
den HOCON-Pfad actor-ts.cache.<name>.plugin). HTTP-Middleware,
Projection-Actors und dein eigener Code teilen sich dann eine
konfigurierte Instanz, statt dass jeder seine eigene baut.
Der von der Extension gebaute In-Memory-Cache liest seine Defaults aus HOCON:
actor-ts.cache.in-memory { maxEntries = 50000 # LRU cap cleanupMs = 30000 # background sweep interval, ms (0 disables)}Für einen Wegwerf-Cache konstruiere einfach direkt
new InMemoryCache() — keine Extension nötig.
Wann er für Produktion falsch ist
Abschnitt betitelt „Wann er für Produktion falsch ist“Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Cache-Übersicht — das große Bild.
- Redis-Cache — Multi-Process- Alternative.
- Memcached-Cache — Alternative.
- CachedSnapshotStore — ein Konsument der Cache-Abstraktion.
