update: scheduler can now prevent selected tasks from running under high load

This commit is contained in:
alikia2x (寒寒) 2025-01-25 23:25:11 +08:00
parent 96b4cecaec
commit 2ce726d16c
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
4 changed files with 40 additions and 4 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -21,6 +21,7 @@
"@alikia/random-key": "npm:@jsr/alikia__random-key",
"@electron/remote": "^2.1.2",
"@hono/node-server": "^1.13.7",
"@types/node-os-utils": "^1.3.4",
"@unly/universal-language-detector": "^2.0.3",
"better-sqlite3": "^11.6.0",
"dayjs": "^1.11.13",
@ -41,6 +42,7 @@
"image-size": "^1.1.1",
"jotai": "^2.11.0",
"memory-cache": "^0.2.0",
"node-os-utils": "^1.3.7",
"pino": "^9.6.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@ -1,3 +1,5 @@
import osu from "node-os-utils";
type TaskId = string;
type TaskFunction = () => void;
@ -9,6 +11,7 @@ interface Task {
nextRun?: number;
isPaused: boolean;
delayUntil?: number;
requiredSystemState: SystemState;
}
export interface TaskStatus {
@ -17,15 +20,27 @@ export interface TaskStatus {
nextRun?: string;
}
type SystemState = "ANY" | "LOW_POWER" | "IDLE";
export class Scheduler {
private tasks: Map<TaskId, Task> = new Map();
private timer: NodeJS.Timeout | null = null;
private monitorTimer: NodeJS.Timeout | null = null;
private cpuUsage: number = 0;
constructor(private readonly minTickInterval: number = 500) {
this.start();
}
private start(): void {
this.scheduleNextTick();
this.monitorTimer = setInterval(() => this.monitor(), 1000);
}
private monitor(): void {
osu.cpu.usage().then((cpuPercentage) => {
this.cpuUsage = cpuPercentage / 100;
})
}
private scheduleNextTick(): void {
@ -66,7 +81,17 @@ export class Scheduler {
return;
}
const isTaskReadyForIntervalRun = task.interval && task.nextRun && now >= task.nextRun;
const taskRequiredLowPower = task.requiredSystemState === "LOW_POWER";
const cpuUsage = this.cpuUsage;
const isSystemLowPower = cpuUsage < 0.75;
const isTaskReadyForLowPowerRun = taskRequiredLowPower ? isSystemLowPower : true;
const reachedTaskNextRun = task.interval && task.nextRun && now >= task.nextRun;
const isTaskReadyForIntervalRun = reachedTaskNextRun && isTaskReadyForLowPowerRun;
if (!isTaskReadyForLowPowerRun) {
this.delayTask(task.id, 1000)
}
if (isTaskReadyForIntervalRun) {
task.func();
task.lastRun = now;
@ -100,15 +125,17 @@ export class Scheduler {
* @param id A unique string identifier for the task.
* @param func The function to be executed by the task.
* @param interval The interval (in milliseconds) between task executions.
* @param requiredSystemState The required system state for the task to run.
*/
addTask(id: TaskId, func: TaskFunction, interval?: number): void {
addTask(id: TaskId, func: TaskFunction, interval?: number, requiredSystemState: SystemState = "ANY"): void {
this.tasks.set(id, {
id,
func,
interval,
isPaused: false,
lastRun: undefined,
nextRun: interval ? Date.now() + interval : undefined
nextRun: interval ? Date.now() + interval : undefined,
requiredSystemState: requiredSystemState,
});
this.scheduleNextTick();
@ -172,6 +199,9 @@ export class Scheduler {
const task = this.tasks.get(id);
if (task) {
task.delayUntil = Date.now() + delayMs;
if (task.nextRun) {
task.nextRun += delayMs;
}
}
this.scheduleNextTick();
@ -215,5 +245,9 @@ export class Scheduler {
clearTimeout(this.timer);
this.timer = null;
}
if (this.monitorTimer) {
clearTimeout(this.monitorTimer);
this.monitorTimer = null;
}
}
}

View File

@ -112,7 +112,7 @@ app.on("ready", () => {
initDatabase().then((db) => {
scheduler.addTask("screenshot", takeScreenshot, 2000);
scheduler.addTask("check-encoding", checkFramesForEncoding, 5000);
scheduler.addTask("process-encoding", processEncodingTasks, 10000);
scheduler.addTask("process-encoding", processEncodingTasks, 10000, "LOW_POWER");
scheduler.addTask("delete-screenshots", deleteUnnecessaryScreenshots, 20000);
dbConnection = db;
cache.put("server:dbConnection", dbConnection);