mcp-memory-libsql
by spences10
Verified
import { AttachmentIndexService } from './index-service.js';
/**
* Service for managing attachment cleanup with intelligent scheduling
*/
export class AttachmentCleanupService {
private cleanupInterval: NodeJS.Timeout | null = null;
private readonly baseIntervalMs = 300000; // 5 minutes
private readonly maxIntervalMs = 3600000; // 1 hour
private currentIntervalMs: number;
private lastCleanupTime: number = 0;
private lastIndexSize: number = 0;
constructor(private indexService: AttachmentIndexService) {
this.currentIntervalMs = this.baseIntervalMs;
}
/**
* Start the cleanup service with adaptive scheduling
*/
start(): void {
if (this.cleanupInterval) {
return; // Already running
}
// Initial state
this.lastIndexSize = this.indexService.size;
this.lastCleanupTime = Date.now();
// Run initial cleanup immediately
this.cleanup();
// Schedule next cleanup
this.scheduleNextCleanup();
}
/**
* Schedule next cleanup based on system activity
*/
private scheduleNextCleanup(): void {
if (this.cleanupInterval) {
clearTimeout(this.cleanupInterval);
}
// Calculate next interval based on activity
const nextInterval = this.currentIntervalMs;
this.cleanupInterval = setTimeout(() => {
this.cleanup();
this.scheduleNextCleanup();
}, nextInterval);
}
/**
* Get current cleanup interval (for testing)
*/
getCurrentInterval(): number {
return this.currentIntervalMs;
}
/**
* Notify the cleanup service of system activity
* Call this when attachments are added or accessed
*/
notifyActivity(): void {
const currentSize = this.indexService.size;
const sizeIncreased = currentSize > this.lastIndexSize;
// Decrease interval if we're seeing increased activity
if (sizeIncreased) {
this.currentIntervalMs = Math.max(
this.baseIntervalMs,
this.currentIntervalMs * 0.75
);
// Force immediate cleanup if we're near capacity
if (currentSize >= this.indexService.maxEntries * 0.9) {
this.cleanup();
this.scheduleNextCleanup();
}
} else {
// Gradually increase interval during low activity
this.currentIntervalMs = Math.min(
this.maxIntervalMs,
this.currentIntervalMs * 1.25
);
}
this.lastIndexSize = currentSize;
}
/**
* Stop the cleanup service
*/
stop(): void {
if (this.cleanupInterval) {
clearTimeout(this.cleanupInterval);
this.cleanupInterval = null;
}
}
/**
* For testing purposes only - clear all internal state
*/
_reset(): void {
this.stop();
this.currentIntervalMs = this.baseIntervalMs;
this.lastCleanupTime = 0;
this.lastIndexSize = 0;
}
/**
* Run cleanup with performance monitoring
*/
private cleanup(): void {
try {
const startTime = process.hrtime();
// Only run if enough time has passed since last cleanup
const timeSinceLastCleanup = Date.now() - this.lastCleanupTime;
if (timeSinceLastCleanup < this.baseIntervalMs / 2) {
return;
}
// Run cleanup
this.indexService.cleanExpiredEntries();
this.lastCleanupTime = Date.now();
// Monitor performance
const [seconds, nanoseconds] = process.hrtime(startTime);
const milliseconds = seconds * 1000 + nanoseconds / 1000000;
// Adjust interval based on cleanup duration
if (milliseconds > 100) { // If cleanup takes >100ms
this.currentIntervalMs = Math.min(
this.maxIntervalMs,
this.currentIntervalMs * 1.5
);
}
} catch (error) {
console.error('Error during attachment cleanup:', error);
}
}
}