Iostream-Li's picture
Add files using upload-large-folder tool
ff78003 verified
/**
* 简单 TTL + LRU 缓存。literature.adapter 用它去重 5 分钟内同 query。
*
* - 不依赖外部包(避免再引一个 lib)。
* - 容量到上限时淘汰**最久未被读**的 entry(经典 LRU)。
* - get() 不返回过期 entry,而且过期就立刻 delete 释放。
*/
export class TtlLruCache<V> {
private readonly maxEntries: number;
private readonly ttlMs: number;
private readonly map = new Map<string, { value: V; expiresAt: number }>();
constructor(opts: { maxEntries: number; ttlMs: number }) {
if (opts.maxEntries <= 0) {
throw new Error("TtlLruCache: maxEntries must be > 0");
}
if (opts.ttlMs <= 0) {
throw new Error("TtlLruCache: ttlMs must be > 0");
}
this.maxEntries = opts.maxEntries;
this.ttlMs = opts.ttlMs;
}
get(key: string): V | undefined {
const entry = this.map.get(key);
if (!entry) return undefined;
if (entry.expiresAt < Date.now()) {
this.map.delete(key);
return undefined;
}
// LRU 触摸:删后再插,Map 保留插入顺序,新插的就是最近访问。
this.map.delete(key);
this.map.set(key, entry);
return entry.value;
}
set(key: string, value: V): void {
if (this.map.has(key)) this.map.delete(key);
this.map.set(key, { value, expiresAt: Date.now() + this.ttlMs });
while (this.map.size > this.maxEntries) {
const firstKey = this.map.keys().next().value;
if (firstKey === undefined) break;
this.map.delete(firstKey);
}
}
size(): number {
return this.map.size;
}
clear(): void {
this.map.clear();
}
}