fix: FOURTH patch on the cron task viewer

This commit is contained in:
zk
2026-06-20 13:23:59 +00:00
parent 75bf78ad38
commit 72c890a8ec
2 changed files with 74 additions and 12 deletions
@@ -0,0 +1,54 @@
import DB from "#services/pg/db";
import { afterEach, describe, expect, it } from "vitest";
import { GetCronTaskExecutions } from "./admin-queries";
const TEST_CHATTY_TASK = "test-cron-chatty-task";
const TEST_DAILY_TASK = "test-cron-daily-task";
async function cleanupTestExecutions() {
await DB.deleteFrom("cron_task_execution")
.where("task_id", "in", [TEST_CHATTY_TASK, TEST_DAILY_TASK])
.execute();
}
describe("GetCronTaskExecutions", () => {
afterEach(cleanupTestExecutions);
it("caps results per task so a high-frequency task cannot crowd out other tasks", async () => {
const now = Date.now();
const isoAt = (offsetMs: number) => new Date(now - offsetMs).toISOString();
// Insert 25 executions for a chatty minutely task — exceeds the 20-per-task cap.
const chattyValues = Array.from({ length: 25 }, (_, i) => ({
task_id: TEST_CHATTY_TASK,
scheduled_at: isoAt(i * 60_000),
status: "success" as const,
completed_at: isoAt(i * 60_000 - 1_000),
output: null,
error: null,
}));
// Insert 3 executions for a daily task. These must not be crowded out.
const dailyValues = Array.from({ length: 3 }, (_, i) => ({
task_id: TEST_DAILY_TASK,
scheduled_at: isoAt(i * 24 * 60 * 60_000),
status: "success" as const,
completed_at: isoAt(i * 24 * 60 * 60_000 - 1_000),
output: null,
error: null,
}));
await DB.insertInto("cron_task_execution")
.values([...chattyValues, ...dailyValues])
.execute();
const results = await GetCronTaskExecutions();
const chatty = results.filter((r) => r.task_id === TEST_CHATTY_TASK);
const daily = results.filter((r) => r.task_id === TEST_DAILY_TASK);
expect(chatty).toHaveLength(20);
expect(daily).toHaveLength(3);
});
});
@@ -1,12 +1,9 @@
import type { Action, CronTask, CronTaskExecution, JobQueue } from "tachi-db";
import { ONE_HOUR } from "#lib/constants/time";
import {
SELECT_CRON_TASK,
SELECT_CRON_TASK_EXECUTION,
SELECT_JOB_QUEUE,
} from "#lib/db-formats/admin-jobs";
import { SELECT_CRON_TASK, SELECT_JOB_QUEUE } from "#lib/db-formats/admin-jobs";
import DB from "#services/pg/db";
import { sql } from "kysely";
export const ADMIN_PAGE_SIZE = 50;
@@ -161,11 +158,22 @@ export function GetCronTasks(): Promise<Array<CronTask>> {
.execute();
}
export function GetCronTaskExecutions(): Promise<Array<CronTaskExecution>> {
return DB.selectFrom("cron_task_execution")
.select(SELECT_CRON_TASK_EXECUTION)
.where("cron_task_execution.scheduled_at", ">=", adminRecentSinceIso(24 * 7))
.orderBy("cron_task_execution.scheduled_at", "desc")
.limit(100)
.execute();
/** Most recent executions per task, capped at 20 per task_id to prevent high-frequency tasks from crowding out others. */
export async function GetCronTaskExecutions(): Promise<Array<CronTaskExecution>> {
const since = adminRecentSinceIso(24 * 7);
const rows = await sql<CronTaskExecution>`
SELECT id, task_id, scheduled_at, started_at, completed_at, status, output, error
FROM (
SELECT
id, task_id, scheduled_at, started_at, completed_at, status, output, error,
ROW_NUMBER() OVER (PARTITION BY task_id ORDER BY scheduled_at DESC) AS rn
FROM cron_task_execution
WHERE scheduled_at >= ${since}
) ranked
WHERE rn <= 20
ORDER BY scheduled_at DESC
`.execute(DB);
return rows.rows;
}