| import { lt, sql } from "drizzle-orm"; |
| import type { Db } from "@paperclipai/db"; |
| import { pluginLogs } from "@paperclipai/db"; |
| import { logger } from "../middleware/logger.js"; |
|
|
| |
| const DEFAULT_RETENTION_DAYS = 7; |
|
|
| |
| const DELETE_BATCH_SIZE = 5_000; |
|
|
| |
| const MAX_ITERATIONS = 100; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export async function prunePluginLogs( |
| db: Db, |
| retentionDays: number = DEFAULT_RETENTION_DAYS, |
| ): Promise<number> { |
| const cutoff = new Date(); |
| cutoff.setDate(cutoff.getDate() - retentionDays); |
|
|
| let totalDeleted = 0; |
| let iterations = 0; |
|
|
| |
| while (iterations < MAX_ITERATIONS) { |
| const deleted = await db |
| .delete(pluginLogs) |
| .where(lt(pluginLogs.createdAt, cutoff)) |
| .returning({ id: pluginLogs.id }) |
| .then((rows) => rows.length); |
|
|
| totalDeleted += deleted; |
| iterations++; |
|
|
| if (deleted < DELETE_BATCH_SIZE) break; |
| } |
|
|
| if (iterations >= MAX_ITERATIONS) { |
| logger.warn( |
| { totalDeleted, iterations, cutoffDate: cutoff }, |
| "Plugin log retention hit iteration limit; some logs may remain", |
| ); |
| } |
|
|
| if (totalDeleted > 0) { |
| logger.info({ totalDeleted, retentionDays }, "Pruned expired plugin logs"); |
| } |
|
|
| return totalDeleted; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export function startPluginLogRetention( |
| db: Db, |
| intervalMs: number = 60 * 60 * 1_000, |
| retentionDays: number = DEFAULT_RETENTION_DAYS, |
| ): () => void { |
| const timer = setInterval(() => { |
| prunePluginLogs(db, retentionDays).catch((err) => { |
| logger.warn({ err }, "Plugin log retention sweep failed"); |
| }); |
| }, intervalMs); |
|
|
| |
| prunePluginLogs(db, retentionDays).catch((err) => { |
| logger.warn({ err }, "Initial plugin log retention sweep failed"); |
| }); |
|
|
| return () => clearInterval(timer); |
| } |
|
|