Files
gitops/ts/cache/classes.cache.cleaner.ts

69 lines
1.9 KiB
TypeScript

import { logger } from '../logging.ts';
import type { CacheDb } from './classes.cachedb.ts';
// deno-lint-ignore no-explicit-any
type DocumentClass = { getInstances: (filter: any) => Promise<{ delete: () => Promise<void> }[]> };
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
/**
* Periodically cleans up expired cached documents.
*/
export class CacheCleaner {
private intervalId: number | null = null;
private intervalMs: number;
private documentClasses: DocumentClass[] = [];
private cacheDb: CacheDb;
constructor(cacheDb: CacheDb, intervalMs = DEFAULT_INTERVAL_MS) {
this.cacheDb = cacheDb;
this.intervalMs = intervalMs;
}
/** Register a document class for cleanup */
registerClass(cls: DocumentClass): void {
this.documentClasses.push(cls);
}
start(): void {
if (this.intervalId !== null) return;
this.intervalId = setInterval(() => {
this.clean().catch((err) => {
logger.error(`CacheCleaner error: ${err}`);
});
}, this.intervalMs);
// Unref so the interval doesn't prevent process exit
Deno.unrefTimer(this.intervalId);
logger.debug(`CacheCleaner started (interval: ${this.intervalMs}ms)`);
}
stop(): void {
if (this.intervalId !== null) {
clearInterval(this.intervalId);
this.intervalId = null;
logger.debug('CacheCleaner stopped');
}
}
/** Run a single cleanup pass */
async clean(): Promise<number> {
const now = Date.now();
let totalDeleted = 0;
for (const cls of this.documentClasses) {
try {
const expired = await cls.getInstances({ expiresAt: { $lt: now } });
for (const doc of expired) {
await doc.delete();
totalDeleted++;
}
} catch (err) {
logger.error(`CacheCleaner: failed to clean class: ${err}`);
}
}
if (totalDeleted > 0) {
logger.debug(`CacheCleaner: deleted ${totalDeleted} expired document(s)`);
}
return totalDeleted;
}
}