fix: keep long auto-delete intervals from overflowing setTimeout (#672)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Emrik Östling <emrik.ostling@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Signed-off-by: YodHeVauHe <mugharold@gmail.com>
This commit is contained in:
ZaSourceCode
2026-10-26 22:21:31 +03:00
committed by GitHub
parent 5d4d6bac00
commit 710b842e2d
3 changed files with 200 additions and 4 deletions
+48
View File
@@ -0,0 +1,48 @@
/** SetTimeout delays above this overflow a signed 32-bit integer and fire immediately. */
const MAX_TIMEOUT_DELAY_MS = 2 ** 31 - 1;
type ArmTimer = (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
const armNativeTimer: ArmTimer = (callback, delayMs) => setTimeout(callback, delayMs);
export type ScheduledTask = {
/** Stops the task. Works across chunk boundaries, unlike a raw timer handle. */
cancel: () => void;
};
export function scheduleDelayed(
delayMs: number,
task: () => void,
armTimer: ArmTimer = armNativeTimer,
): ScheduledTask {
let handle: ReturnType<typeof setTimeout> | undefined;
let cancelled = false;
const arm = (remainingMs: number) => {
const chunkMs = Math.min(remainingMs, MAX_TIMEOUT_DELAY_MS);
handle = armTimer(() => {
if (cancelled) {
return;
}
const restMs = remainingMs - chunkMs;
if (restMs > 0) {
arm(restMs);
return;
}
task();
}, chunkMs);
};
arm(delayMs);
return {
cancel: () => {
cancelled = true;
if (handle !== undefined) {
clearTimeout(handle);
}
},
};
}
+8 -4
View File
@@ -8,6 +8,7 @@ import db from "./db/db";
import { Jobs } from "./db/types";
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
import "./helpers/printVersions";
import { scheduleDelayed, ScheduledTask } from "./helpers/scheduleDelayed";
import { chooseConverter } from "./pages/chooseConverter";
import { convert } from "./pages/convert";
import { deleteFile } from "./pages/deleteFile";
@@ -75,12 +76,15 @@ app.listen(process.env.PORT || 3000);
console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);
let clearJobsTimer: ReturnType<typeof setTimeout> | undefined;
const MILLISECONDS_PER_HOUR = 60 * 60 * 1000;
const autoDeleteIntervalMs = AUTO_DELETE_EVERY_N_HOURS * MILLISECONDS_PER_HOUR;
let clearJobsTimer: ScheduledTask;
const clearJobs = () => {
const jobs = db
.query("SELECT * FROM jobs WHERE date_created < ?")
.as(Jobs)
.all(new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString());
.all(new Date(Date.now() - autoDeleteIntervalMs).toISOString());
for (const job of jobs) {
// delete the directories
@@ -97,7 +101,7 @@ const clearJobs = () => {
db.query("DELETE FROM jobs WHERE id = ?").run(job.id);
}
clearJobsTimer = setTimeout(clearJobs, AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000);
clearJobsTimer = scheduleDelayed(autoDeleteIntervalMs, clearJobs);
};
if (AUTO_DELETE_EVERY_N_HOURS > 0) {
@@ -109,7 +113,7 @@ const handleShutdown = async () => {
if (isShuttingDown) return;
isShuttingDown = true;
if (clearJobsTimer) {
clearTimeout(clearJobsTimer);
clearJobsTimer.cancel();
}
try {
await app.stop(true);