HybridCache und FusionCache im Vergleich
- Matt
In Cache mit zwei Ebenen ging es um HybridCache: Speicher- und verteilten Cache hinter einer API, mit eingebautem Schutz gegen hundert gleichzeitige Anfragen auf einen abgelaufenen Eintrag. Was da fehlte: HybridCache ist eine Abstraktion mit austauschbarer Implementierung, und die Bibliothek, die als Vorbild diente, kann mehr als die eingebaute Variante.
Was HybridCache mitbringt
HybridCache steckt seit .NET 9 im Paket Microsoft.Extensions.Caching.Hybrid und läuft dank Kompatibilitätsschicht bis hinunter zu .NET Framework 4.7.2. GetOrCreateAsync ruft die Fabrikmethode höchstens einmal pro Schlüssel auf und verteilt das Ergebnis in beide Ebenen. Tags kommen als Liste zum Aufruf dazu, RemoveByTagAsync invalidiert sie logisch, nicht physisch: markierte Einträge gelten ab dann als Cache-Miss, bleiben aber bis zum eigentlichen Ablauf im Speicher liegen.
Für eine Anwendung mit einer Instanz und Redis als zweiter Ebene reicht das.
Was FusionCache zusätzlich bietet
ZiggyCreatures.FusionCache bringt drei Dinge mit, die HybridCache nicht kennt:
- Fail-Safe: Schlägt die Fabrikmethode fehl, liefert FusionCache den zuletzt bekannten, bereits abgelaufenen Wert zurück, statt die Exception durchzureichen.
- Soft- und Hard-Timeouts: Eine langsame Fabrikmethode bekommt ein kurzes Zeitfenster, danach liefert FusionCache den alten Wert aus und lässt die Fabrik im Hintergrund weiterlaufen. Erst der Hard-Timeout bricht wirklich ab.
- Backplane: Läuft die Anwendung auf mehreren Knoten, synchronisiert die Backplane Invalidierungen über alle lokalen Speicher-Caches. Ohne sie gilt bei
HybridCache:RemoveByTagAsyncauf Knoten A räumt A und den verteilten Cache auf, Knoten B und C liefern aus ihrem lokalen L1 weiter, bis dessenLocalCacheExpirationabläuft.
var produkt = await cache.GetOrSetAsync<Produkt>(
$"produkt:{id}",
async _ => await _repo.LadenAsync(id),
options => options
.SetDuration(TimeSpan.FromMinutes(1))
.SetFailSafe(true, TimeSpan.FromHours(2))
.SetFactoryTimeouts(TimeSpan.FromMilliseconds(200)));
FusionCache als HybridCache einsetzen
Wer schon gegen die HybridCache-Abstraktion programmiert, muss den Aufrufcode nicht anfassen. FusionCache liefert einen Adapter:
builder.Services.AddFusionCache()
.WithDefaultEntryOptions(o => o.SetFailSafe(true))
.AsHybridCache();
Ab da injiziert man weiter HybridCache, bekommt darunter aber Fail-Safe und, mit dem passenden Zusatzpaket, eine Backplane. Der Preis: Über die Abstraktion stehen nur HybridCacheEntryOptions zur Verfügung, nicht das volle FusionCacheEntryOptions mit Soft-Timeout. Wer die braucht, ruft GetOrSetAsync direkt auf der IFusionCache-Instanz auf.
Wann sich der Wechsel nicht lohnt
Bei einer einzelnen Instanz bringt die Backplane nichts, den Stampede-Schutz hat HybridCache bereits eingebaut. Und Fail-Safe ist kein Freifahrtschein: Ein abgestürzter Datenbankserver hinter einer Fabrikmethode mit Fail-Safe fällt in den Metriken nicht mehr auf, solange noch ein alter Wert im Cache liegt. Wer es aktiviert, sollte den Fehler trotzdem loggen, statt ihn nur zu schlucken.
Details in der HybridCache-Dokumentation und im FusionCache-Repository, inklusive der Adapter-Dokumentation für den HybridCache-Weg.