Dokümanlar menüsü
Çalıştırmalar arası cache
Hesaplanmış bir sonucu ayrı çalıştırmalar arasında içerik hash'iyle yeniden kullanın — ve bilerek best-effort, yani bir cache kesintisi çalıştırmayı da yanında götürmez.
Ne işe yarar#
Bir çalıştırma içindeki yeniden kullanım zaten çözülmüş: replay tam olarak bunu yapıyor. Replay'in yapamadığı şey, farklı iki çalıştırma arasında işi paylaşmak — ve tekrarlanan maliyet genelde orada. Aynı RAG sorusunu soran iki çalıştırma aynı metni iki kez gömer; aynı belgeyi özetleyen iki çalıştırma bedelini iki kez öder.
Anahtar çalıştırmadan bağımsızdır (<namespace>:<hash>), yani bir runId altında hesaplanan sonuç başka birinin altında bulunur. Bütün fikir budur: daha hızlı bir journal değil, journal'ın hiç kapatmak için tasarlanmadığı boşluk.
Adım adım#
Uzanacağınız fonksiyon getOrCompute: varsa cache'lenmiş değeri döner, yoksa hesaplar, saklar ve döner. İki yarıyı ayrı istediğinizde get ve set vardır; ttlMs ise depolama arka ucu TTL yeteneğini destekliyorsa geçerlidir.
import { createCache } from '@gnldev/cache';
const cache = createCache(storage.cache, 'embeddings', {
onError: (op, err) => logger.warn({ op, err }, 'cache degraded'),
});
// run A computes it; run B finds it — the key does not carry a runId
const vector = await cache.getOrCompute(
{ text: question },
() => embed(question),
{ ttlMs: 86_400_000 },
);Best-effort — ve neden doğru varsayılan bu#
Cache bir hızlandırıcıdır, doğruluk kaynağı değil; o yüzden bir store hatası çalıştırmayı düşürmemelidir. Her işlem fırlatmak yerine geriler: başarısız bir get miss olur, başarısız bir set hiçbir şey yapmaz, getOrCompute yine hesaplar ve yine doğru cevabı döner. Hatalar sessizce yutulmaz, onError geri çağırmasıyla bildirilir.
Bu, çalıştırma journal'ı ile belleğin davranışının bilerek tersidir ve fark söylenmeye değer: onlar katıdır, çünkü bir journal yazımını kaybetmek garantiyi kaybetmektir. Bir cache yazımını kaybetmek yalnız zamana mal olur.
Gerçek bir hatayı önleyen küçük bir tasarım ayrıntısı: saklanan değer { v } ile sarılır. Sarmalayıcı olmasa cache'lenmiş bir undefined ile "burada hiçbir şey yok" ayırt edilemezdi ve meşru biçimde undefined olan her sonuç her seferinde yeniden hesaplanırdı.
Sayaçlar ve geçersizleştirme — küçük yazıyı okuyun#
stats() isabet ve ıskalama sayaçlarını, isabet oranını ve bir anahtar sayısını döner. Bunlar süreç içi sayaçlardır: süreç yeniden başlayınca sıfırlanırlar, çünkü store'un işi değer saklamaktır, saymak değil.
invalidate(key) o tek anahtarı siler. Argümansız invalidate() ise yalnız bu örneğin gördüğü anahtarları siler — CacheStore portu anahtar sıralaması sunmaz, yani buradan bir store'u toptan temizlemenin yolu yoktur. Bu bir flush değildir; sayfada yazıyor çünkü isim aksini düşündürüyor.
cache.stats();
// { hits: 41, misses: 9, hitRate: 0.82, size: 50 }
await cache.invalidate({ text: question }); // one key
await cache.invalidate(); // only what THIS instance has seenAPI#
createCachecreateCache(store, namespace = 'default', { onError? }) — tek bir namespace'e bağlı bir Cache.
CachecreateCache’in döndürdüğü nesne: get(key), set(key, value, { ttlMs? }), getOrCompute(key, compute, { ttlMs? }), stats(), invalidate(key?). Her okuma/yazma fırlatmak yerine geriler.
CacheSetOptions{ ttlMs? } — depolama arka ucu ttl yeteneğini desteklediğinde geçerlidir.
CacheOptions{ onError? } — store hatasında çağrılır; hata bildirilir, fırlatılmaz.
CacheStats{ hits, misses, hitRate, size } — yeniden başlatmada sıfırlanan süreç içi sayaçlar; size, bu örneğin bildiği anahtarlardır, store'un toplamı değil.
<namespace>:<hash> altında hash'lenmiş içeriktir. Farklı işleri farklı namespace'lere verin — gömmeler, özetler, dış aramalar — ki bir sonuç sınıfını geçersizleştirmek diğerine uzanamasın.