meldestelle/infrastructure/cache/README-INFRA-CACHE.md
2025-09-03 15:19:11 +02:00

6.7 KiB
Raw Blame History

Infrastructure/Cache Modulbeschreibung und Implementierungsleitfaden

Letzte Aktualisierung: 03. September 2025

Zweck und Aufgaben des Moduls

Das Infrastructure/Cache-Modul stellt eine einheitliche, technologieneutrale CacheSchnittstelle für alle Services bereit und liefert mit einer Redisbasierten AdapterImplementierung die produktionsreife Ausführung. Ziele:

  • Antwortzeiten reduzieren und Primärdatenbanken entlasten.
  • Einheitliche API für Lesen/Schreiben, BatchOperationen und TTLs.
  • Resilienz bei RedisAusfällen durch lokalen Fallback.
  • Operative Transparenz durch einfache Metriken, HealthInformationen und periodische Wartungsaufgaben.

Architektur (PortAdapter)

  • cacheapi: enthält die öffentlichen Verträge und Basistypen
    • DistributedCache: zentrale PortSchnittstelle für CacheOperationen
    • CacheEntry, CacheConfiguration, CacheSerializer
    • ConnectionStatusTracker/ConnectionStateListener zur Verbindungsüberwachung
  • rediscache: Adapter, der die PortSchnittstelle mit Spring Data Redis umsetzt
    • RedisDistributedCache: konkrete Implementierung inkl. OfflineFallback, DirtySync, Batchs, KeyPrefixing, TTLHandling und einfachen Metriken
    • JacksonCacheSerializer: serialisiert Werte und CacheEntry per Jackson

Öffentliche API (Auszug)

DistributedCache

  • get(key, clazz)/set(key, value, ttl?)
  • delete(key), exists(key)
  • multiGet(keys, clazz), multiSet(map, ttl?)
  • multiDelete(keys)
  • synchronize(keys?), markDirty(key), getDirtyKeys(), clear()

Idiomatic Kotlin Extensions

  • cache.get(key)
  • cache.multiGet(keys)

CacheConfiguration (DefaultCacheConfiguration vorhanden)

  • defaultTtl?, localCacheMaxSize?, offlineModeEnabled, synchronizationInterval, offlineEntryMaxAge?, keyPrefix, compressionEnabled, compressionThreshold

Hinweis: Die Kompression wird aktuell durch den Serializer bereitgestellt; Schwellwerte/Flags sind für zukünftiges Tuning vorgesehen.

Implementierungsdetails (RedisDistributedCache)

  • Lokaler Fallback: ConcurrentHashMap als lokaler Cache speichert CacheEntry inkl. expiresAt. Bei RedisAusfall werden Schreibvorgänge lokal gehalten und als „dirty“ markiert.
  • DirtySynchronisation: Sobald die Verbindung wieder ONLINE ist, werden geänderte Schlüssel zu Redis synchronisiert (synchronize()).
  • KeyPrefixing: Alle externen Keys werden mittels keyPrefix gekapselt, um Mandanten/Services zu isolieren.
  • TTL/Expiration: TTL wird einheitlich über kotlin.time.Duration angegeben und für Redis in java.time.Duration konvertiert. Lokale Einträge enthalten expiresAt und werden periodisch bereinigt.
  • BatchOperationen: multiGet/multiSet/multiDelete nutzen RedisBatching/Pipelining, lokal wird konsistent gespiegelt.
  • Größenbegrenzung Local Cache (neu): Wenn localCacheMaxSize gesetzt ist, werden bei Überschreitung die am längsten nicht mehr modifizierten Einträge aus dem lokalen Cache entfernt (LRM least recently modified). Dadurch bleibt der lokale Fallback speichereffizient.
  • Periodische Aufgaben (@Scheduled):
    • Verbindungsprüfung: fixedDelayString = "${redis.connection-check-interval:10000}"
    • Lokale Bereinigung: fixedDelayString = "${redis.local-cache-cleanup-interval:60000}"
    • DirtySync: fixedDelayString = "${redis.sync-interval:300000}"
    • MetrikenLog: fixedDelayString = "${redis.metrics-log-interval:300000}"

Wichtige Robustheitsdetails

  • Alle RedisOperationen fangen RedisConnectionFailureException ab und schalten den ConnectionState auf DISCONNECTED. Beim nächsten erfolgreichen Zugriff wird CONNECTED gesetzt und eine Synchronisation der dirty keys ausgelöst.
  • multiSet setzt TTLs bei Bedarf per Pipeline nach (pExpire); einzelne setOperationen nutzen expire via Duration.

Verwendung (Beispiele)

Einbinden: Projekte hängen gegen :infrastructure:cache:redis-cache und injizieren DistributedCache.

Lesen/Schreiben mit TTL

val user = cache.get<User>("user:42")
if (user == null) {
    val loaded = userRepository.findById("42") ?: return null
    cache.set("user:42", loaded, ttl = 1.hours)
}

BatchLesezugriff

val ids = listOf("user:1", "user:2", "user:3")
val map = cache.multiGet<User>(ids)

BulkSchreiben

cache.multiSet(mapOf(
    "cfg:app" to appConfig,
    "cfg:features" to features
), ttl = 30.minutes)

Verbindungsstatus überwachen

cache.registerConnectionListener(object : ConnectionStateListener {
    override fun onConnectionStateChanged(newState: ConnectionState, timestamp: Instant) {
        logger.info("Cache connection state: $newState at $timestamp")
    }
})

Konfiguration

DefaultCacheConfiguration bietet sinnvolle Defaults. Relevante Properties (optional via Spring @Scheduled Platzhalter):

  • redis.connection-check-interval: ms für Verbindungsprüfung (Default 10000)
  • redis.local-cache-cleanup-interval: ms für lokale Bereinigung (Default 60000)
  • redis.sync-interval: ms für Synchronisationsläufe (Default 300000)
  • redis.metrics-log-interval: ms für periodisches MetrikenLogging (Default 300000)

Hinweise

  • keyPrefix sollte pro Service gesetzt werden (z. B. "masterdata"), um Kollisionen zu vermeiden.
  • localCacheMaxSize begrenzt die Größe des lokalen FallbackCaches. Bei null ist die Größe unbegrenzt.

Betrieb & Monitoring

  • HealthInfos: getHealthStatus() liefert eine einfache Einschätzung basierend auf ConnectionState und Erfolgsrate der Operationen.
  • Metriken: getPerformanceMetrics() liefert einfache Kennzahlen (Operations, SuccessRate, Größe lokaler Cache, Anzahl dirty Keys). Periodisches Logging per @Scheduled möglich.
  • Cache Warming: warmCache(keys, loader) und warmCacheBulk(map) helfen, HotKeys/gefragte Konfigurationen beim Start vorzuwärmen.

Grenzen & bekannte Punkte

  • Kompression ist im Serializer implementiert; die konfigurierbaren Flags/Schwellenwerte sind derzeit nicht dynamisch an/ausgeschaltet.
  • OfflineModus: Die Konfiguration offlineModeEnabled ist vorhanden; die Implementierung betreibt den lokalen Fallback standardmäßig bei Verbindungsproblemen. Eine harte Deaktivierung dieses Verhaltens ist aktuell nicht verdrahtet.

Changelog (Kurz)

  • 20250903: Fehlerbehebungen für @ScheduledPlatzhalter, korrektes Logging im CacheWarming, lokale CacheGrößenbegrenzung (LRMEviction) hinzugefügt. Dokumentation aktualisiert (diese Datei).

Fazit

Das CacheModul bietet eine klare, wiederverwendbare CacheSchnittstelle mit einer robusten RedisImplementierung. Es unterstützt TTLs, BatchOperationen, lokalen Fallback bei Ausfällen und liefert einfache, praxistaugliche Betriebsinformationen. Mit keyPrefix und lokalen Limits ist der Einsatz in MultiServiceUmgebungen unkompliziert und stabil.