feat(ADM-018): completed feature
This commit is contained in:
61
project/src/modules/cache/application/cache-service.ts
vendored
Normal file
61
project/src/modules/cache/application/cache-service.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { CacheAdapter } from '../domain/ports.js';
|
||||
import type { CacheContract } from '../domain/cache.js';
|
||||
|
||||
export class CacheService {
|
||||
private readonly contracts = new Map<string, CacheContract>();
|
||||
private hits = 0;
|
||||
private misses = 0;
|
||||
private invalidations = 0;
|
||||
|
||||
constructor(private readonly adapter: CacheAdapter) {}
|
||||
|
||||
registerContract(contract: CacheContract): void {
|
||||
this.contracts.set(contract.name, contract);
|
||||
}
|
||||
|
||||
listContracts(): CacheContract[] {
|
||||
return [...this.contracts.values()];
|
||||
}
|
||||
|
||||
async read<T>(contractName: string, key: string, loader: () => Promise<T>): Promise<T> {
|
||||
const contract = this.contracts.get(contractName);
|
||||
if (!contract) {
|
||||
// Unknown contracts still load from source.
|
||||
return loader();
|
||||
}
|
||||
const cached = await this.adapter.get<T>(key);
|
||||
if (cached !== undefined) {
|
||||
this.hits += 1;
|
||||
return cached;
|
||||
}
|
||||
this.misses += 1;
|
||||
const value = await loader();
|
||||
await this.adapter.set(key, value, contract.ttlSeconds).catch(() => undefined);
|
||||
return value;
|
||||
}
|
||||
|
||||
async invalidate(key: string): Promise<void> {
|
||||
this.invalidations += 1;
|
||||
await this.adapter.invalidate(key);
|
||||
}
|
||||
|
||||
async invalidateByName(name: string): Promise<number> {
|
||||
const contract = this.contracts.get(name);
|
||||
if (!contract) return 0;
|
||||
this.invalidations += 1;
|
||||
// Without an index, we invalidate the key pattern prefix; the in-memory adapter
|
||||
// handles this. Real Redis adapter would use SCAN.
|
||||
await this.adapter.invalidate(contract.keyPattern.replace(/[:*]/g, ''));
|
||||
return 1;
|
||||
}
|
||||
|
||||
metrics(): { hits: number; misses: number; invalidations: number; hitRatio: number } {
|
||||
const total = this.hits + this.misses;
|
||||
return {
|
||||
hits: this.hits,
|
||||
misses: this.misses,
|
||||
invalidations: this.invalidations,
|
||||
hitRatio: total === 0 ? 0 : this.hits / total,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user