feat(system): retention cleanup jobs, storage panel API and health storage level (CACHE-2)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:16:50 +08:00
1 parent d13c18b4b1
commit 05b163ce32
9 files changed
+1254 -9

No files matched your search

@@ -0,0 +1,79 @@
import type { Kysely } from "kysely";
import { execDdl } from "../src/migration-helpers.ts";
// CACHE-2 存储保留(docs/runbook/storage.md):
// - 保留清理按时间列分批删除,给还没有时间索引的高流水表补上(已有:task_created_idx、agent_run_event_at_idx、
// process_heartbeat_sample_at_idx);
// - 按表设置 autovacuum:批量删除 / 追加为主的大表降低触发比例,频繁原地更新的小表更激进,避免死元组堆积。
// 引擎 schema(openworkflow)的表由引擎自己建,这里不动。
const INDEXES: [name: string, ddl: string][] = [
["observation_created_idx", "observation (created_at)"],
["sse_event_at_idx", "sse_event (at)"],
["audit_event_at_idx", "audit_event (at)"],
["capability_invocation_created_idx", "capability_invocation (created_at)"],
["agent_run_message_created_idx", "agent_run_message (created_at)"],
["tasks_fact_created_idx", "tasks_fact (created_at)"],
["tasks_run_meta_created_idx", "tasks_run_meta (created_at)"],
["tasks_op_ledger_started_idx", "tasks_op_ledger (started_at) where status = 'succeeded'"],
];
/** 追加 + 批量删除为主的大表 */
const APPEND_TABLES = [
"observation",
"sse_event",
"audit_event",
"agent_run_event",
"process_heartbeat_sample",
"capability_invocation",
"task",
"tasks_fact",
"tasks_run_meta",
];
const APPEND_OPTS = {
autovacuum_vacuum_scale_factor: "0.05",
autovacuum_analyze_scale_factor: "0.02",
autovacuum_vacuum_insert_scale_factor: "0.1",
};
/** 频繁原地更新的小表(心跳 15 秒一次、令牌桶、调度状态、缓存命中刷新 accessed_at) */
const HOT_TABLES = ["process_heartbeat", "rate_limit_bucket", "scheduled_job", "kv_cache", "outbox_event"];
const HOT_OPTS = {
autovacuum_vacuum_scale_factor: "0.02",
autovacuum_vacuum_threshold: "100",
autovacuum_analyze_scale_factor: "0.05",
};
const setOpts = (tables: string[], o: Record<string, string>) =>
tables
.map(
(t) =>
`alter table ${t} set (${Object.entries(o)
.map(([k, v]) => `${k} = ${v}`)
.join(", ")});`,
)
.join("\n");
const resetOpts = (tables: string[], o: Record<string, string>) =>
tables.map((t) => `alter table ${t} reset (${Object.keys(o).join(", ")});`).join("\n");
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`${INDEXES.map(([n, d]) => `create index if not exists ${n} on ${d};`).join("\n")}
${setOpts(APPEND_TABLES, APPEND_OPTS)}
${setOpts(HOT_TABLES, HOT_OPTS)}
`,
);
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`${resetOpts(HOT_TABLES, HOT_OPTS)}
${resetOpts(APPEND_TABLES, APPEND_OPTS)}
${INDEXES.map(([n]) => `drop index if exists ${n};`).join("\n")}
`,
);
}