0.5 缩减重构

This commit is contained in:
lix-2026
2026-04-13 19:21:42 +08:00
parent af92c4b149
commit 71fb1aee7e
2023 changed files with 21113 additions and 394493 deletions
+80 -3
View File
@@ -34,14 +34,78 @@ async function cleanupByCreationTime(ctx: any, table: string, cutoffMs: number,
return deleted;
}
async function cleanupOrphanConvexFiles(
ctx: any,
args: { cutoffMs: number; maxDeletes: number; dryRun: boolean },
): Promise<{ deleted: number; scanned: number; hitLimit: boolean }> {
const batchSize = 200;
let deleted = 0;
let scanned = 0;
let lastCreationTime: number | null = null;
let lastId: any = null;
while (deleted < args.maxDeletes) {
let q = ctx.db.system.query("_storage").order("asc");
if (lastCreationTime != null && lastId != null) {
// 说明:Convex 的 `paginate` 每个函数只能调用一次,这里用“游标过滤 + take”模拟分页。
// 使用 (_creationTime, _id) 作为游标,避免同一毫秒内多条记录导致遗漏/死循环。
q = q.filter((qb: any) =>
qb.or(
qb.gt(qb.field("_creationTime"), lastCreationTime),
qb.and(qb.eq(qb.field("_creationTime"), lastCreationTime), qb.gt(qb.field("_id"), lastId)),
),
);
}
const rows = await q.take(batchSize);
if (!rows.length) break;
let reachedNewer = false;
for (const row of rows) {
scanned += 1;
const created = typeof row._creationTime === "number" ? row._creationTime : 0;
lastCreationTime = created;
lastId = row._id;
if (created >= args.cutoffMs) {
reachedNewer = true;
break;
}
// 说明:当前仓库里,Convex Files 仅用于 media_assets.storage_id。
// 若未来新增其它引用表,需要把引用检查一并补齐,避免误删仍被使用的文件。
const ref = await ctx.db
.query("media_assets")
.withIndex("by_storage_id", (q: any) => q.eq("storage_id", row._id))
.first();
if (ref) continue;
if (!args.dryRun) {
try {
await ctx.storage.delete(row._id);
} catch {
// ignore
}
}
deleted += 1;
if (deleted >= args.maxDeletes) break;
}
if (reachedNewer) break;
}
return { deleted, scanned, hitLimit: deleted >= args.maxDeletes };
}
export const cleanupWeekly = internalMutation({
args: {
dryRun: v.optional(v.boolean()),
maxDeletes: v.optional(v.number()),
maxFileDeletes: v.optional(v.number()),
},
handler: async (ctx, args) => {
const dryRun = Boolean(args.dryRun);
const maxDeletes = Math.max(100, Math.min(50_000, Math.floor(args.maxDeletes ?? 20_000)));
const maxFileDeletes = Math.max(100, Math.min(50_000, Math.floor(args.maxFileDeletes ?? 10_000)));
const cutoffMs = Date.now() - WEEK_MS;
@@ -60,11 +124,21 @@ export const cleanupWeekly = internalMutation({
const deletedAuthSessions = await cleanupByCreationTime(ctx, "authSessions", cutoffMs, perTable, dryRun);
const deletedJobs = await cleanupByCreationTime(ctx, "jobs", cutoffMs, maxDeletes - perTable * 2, dryRun);
// 说明:清理 Convex Files 孤儿数据(_storage 里“7 天前且无引用”的文件)。
const files = await cleanupOrphanConvexFiles(ctx, { cutoffMs, maxDeletes: maxFileDeletes, dryRun });
// 若达到上限,兜底再跑一轮(避免一次删太多导致超时)
const hitLimit =
deletedAuthRefreshTokens >= perTable || deletedAuthSessions >= perTable || deletedJobs >= maxDeletes - perTable * 2;
deletedAuthRefreshTokens >= perTable ||
deletedAuthSessions >= perTable ||
deletedJobs >= maxDeletes - perTable * 2 ||
files.hitLimit;
if (!dryRun && hitLimit) {
await ctx.scheduler.runAfter(60_000, (internal as any).maintenance.cleanupWeekly, { dryRun: false, maxDeletes });
await ctx.scheduler.runAfter(60_000, (internal as any).maintenance.cleanupWeekly, {
dryRun: false,
maxDeletes,
maxFileDeletes,
});
}
return {
@@ -75,9 +149,12 @@ export const cleanupWeekly = internalMutation({
authRefreshTokens: deletedAuthRefreshTokens,
authSessions: deletedAuthSessions,
jobs: deletedJobs,
convexFiles: files.deleted,
},
scanned: {
convexFiles: files.scanned,
},
rescheduled: !dryRun && hitLimit,
};
},
});