Skip to content
This repository was archived by the owner on Aug 2, 2025. It is now read-only.

Commit 253b084

Browse files
committed
feat(scheduler): Reload schedules on config update
This commit introduces a mechanism to reload the schedules when the configuration is updated. This ensures that the scheduler is always running with the latest configuration settings. It also adds logging to the deletion of Themes The changes include: - Added a function to the file. This function clears all existing schedules and restarts them. - Modified the function in to call the function after updating the configuration in the database. - Added logging to theme deletion
1 parent fe9fd19 commit 253b084

File tree

5 files changed

+366
-335
lines changed

5 files changed

+366
-335
lines changed

src/core/database/themes.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
import type { Theme } from "~/typings/database";
22
import { db } from "./database";
33
import { executeDbOperation } from "./helper";
4+
import { logger } from "../utils/logger";
45

56
const stmt = {
6-
insert: db.prepare(`
7+
insert: db.prepare(`
78
INSERT INTO themes (name, creator, vars, tags) VALUES (?, ?, ?, ?)
89
`),
9-
remove: db.prepare("DELETE FROM themes WHERE name = ?"),
10-
read: db.prepare("SELECT * FROM themes WHERE name = ?"),
11-
readAll: db.prepare("SELECT * FROM themes"),
10+
remove: db.prepare("DELETE FROM themes WHERE name = ?"),
11+
read: db.prepare("SELECT * FROM themes WHERE name = ?"),
12+
readAll: db.prepare("SELECT * FROM themes"),
1213
};
1314

1415
export function getThemes() {
15-
return executeDbOperation("Get Themes", () => stmt.readAll.all()) as Theme[];
16+
return executeDbOperation("Get Themes", () => stmt.readAll.all()) as Theme[];
1617
}
1718

1819
export function addTheme({ name, creator, vars, tags }: Theme) {
19-
return executeDbOperation("Save Theme", () =>
20-
stmt.insert.run(name, creator, vars, tags.toString()),
21-
);
20+
return executeDbOperation("Save Theme", () =>
21+
stmt.insert.run(name, creator, vars, tags.toString()),
22+
);
2223
}
2324
export function getSpecificTheme(name: string): Theme {
24-
return executeDbOperation(
25-
"Getting specific Theme",
26-
() => stmt.read.get(name) as Theme,
27-
);
25+
return executeDbOperation(
26+
"Getting specific Theme",
27+
() => stmt.read.get(name) as Theme,
28+
);
2829
}
2930

3031
export function deleteTheme(name: string) {
31-
return executeDbOperation("Remove Theme", () => stmt.remove.run(name));
32+
logger.debug(`Removing ${name} from themes `);
33+
return executeDbOperation("Remove Theme", () => stmt.remove.run(name));
3234
}

src/core/docker/scheduler.ts

Lines changed: 132 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -5,120 +5,146 @@ import { logger } from "~/core/utils/logger";
55
import type { config } from "~/typings/database";
66

77
function convertFromMinToMs(minutes: number): number {
8-
return minutes * 60 * 1000;
8+
return minutes * 60 * 1000;
99
}
1010

1111
async function initialRun(
12-
scheduleName: string,
13-
scheduleFunction: Promise<void> | void,
14-
isAsync: boolean,
12+
scheduleName: string,
13+
scheduleFunction: Promise<void> | void,
14+
isAsync: boolean,
1515
) {
16-
try {
17-
if (isAsync) {
18-
await scheduleFunction;
19-
} else {
20-
scheduleFunction;
21-
}
22-
logger.info(`Startup run success for: ${scheduleName}`);
23-
} catch (error) {
24-
logger.error(`Startup run failed for ${scheduleName}, ${error as string}`);
25-
}
16+
try {
17+
if (isAsync) {
18+
await scheduleFunction;
19+
} else {
20+
scheduleFunction;
21+
}
22+
logger.info(`Startup run success for: ${scheduleName}`);
23+
} catch (error) {
24+
logger.error(`Startup run failed for ${scheduleName}, ${error as string}`);
25+
}
2626
}
2727

28-
async function scheduledJob(
29-
name: string,
30-
jobFn: () => Promise<void>,
31-
intervalMs: number,
32-
) {
33-
while (true) {
34-
const start = Date.now();
35-
logger.info(`Task Start: ${name}`);
36-
try {
37-
await jobFn();
38-
logger.info(`Task End: ${name} succeeded.`);
39-
} catch (e) {
40-
logger.error(`Task End: ${name} failed:`, e);
41-
}
42-
const elapsed = Date.now() - start;
43-
const delay = Math.max(0, intervalMs - elapsed);
44-
await new Promise((r) => setTimeout(r, delay));
45-
}
28+
type CancelFn = () => void;
29+
let cancelFunctions: CancelFn[] = [];
30+
31+
async function reloadSchedules() {
32+
logger.info("Reloading schedules...");
33+
34+
cancelFunctions.forEach((cancel) => cancel());
35+
cancelFunctions = [];
36+
37+
await setSchedules();
38+
}
39+
40+
function scheduledJob(
41+
name: string,
42+
jobFn: () => Promise<void>,
43+
intervalMs: number,
44+
): CancelFn {
45+
let stopped = false;
46+
47+
async function run() {
48+
if (stopped) return;
49+
const start = Date.now();
50+
logger.info(`Task Start: ${name}`);
51+
try {
52+
await jobFn();
53+
logger.info(`Task End: ${name} succeeded.`);
54+
} catch (e) {
55+
logger.error(`Task End: ${name} failed:`, e);
56+
}
57+
const elapsed = Date.now() - start;
58+
const delay = Math.max(0, intervalMs - elapsed);
59+
setTimeout(run, delay);
60+
}
61+
62+
run();
63+
64+
return () => {
65+
stopped = true;
66+
};
4667
}
4768

4869
async function setSchedules() {
49-
logger.info("Starting DockStatAPI");
50-
try {
51-
const rawConfigData: unknown[] = dbFunctions.getConfig();
52-
const configData = rawConfigData[0];
53-
54-
if (
55-
!configData ||
56-
typeof (configData as config).keep_data_for !== "number" ||
57-
typeof (configData as config).fetching_interval !== "number"
58-
) {
59-
logger.error("Invalid configuration data:", configData);
60-
throw new Error("Invalid configuration data");
61-
}
62-
63-
const { keep_data_for, fetching_interval } = configData as config;
64-
65-
if (keep_data_for === undefined) {
66-
const errMsg = "keep_data_for is undefined";
67-
logger.error(errMsg);
68-
throw new Error(errMsg);
69-
}
70-
71-
if (fetching_interval === undefined) {
72-
const errMsg = "fetching_interval is undefined";
73-
logger.error(errMsg);
74-
throw new Error(errMsg);
75-
}
76-
77-
logger.info(
78-
`Scheduling: Fetching container statistics every ${fetching_interval} minutes`,
79-
);
80-
81-
logger.info(
82-
`Scheduling: Updating host statistics every ${fetching_interval} minutes`,
83-
);
84-
85-
logger.info(
86-
`Scheduling: Cleaning up Database every hour and deleting data older then ${keep_data_for} days`,
87-
);
88-
89-
// Schedule container data fetching
90-
await initialRun("storeContainerData", storeContainerData(), true);
91-
scheduledJob(
92-
"storeContainerData",
93-
storeContainerData,
94-
convertFromMinToMs(fetching_interval),
95-
);
96-
97-
// Schedule Host statistics updates
98-
await initialRun("storeHostData", storeHostData(), true);
99-
scheduledJob(
100-
"storeHostData",
101-
storeHostData,
102-
convertFromMinToMs(fetching_interval),
103-
);
104-
105-
// Schedule database cleanup
106-
await initialRun(
107-
"dbFunctions.deleteOldData",
108-
dbFunctions.deleteOldData(keep_data_for),
109-
false,
110-
);
111-
scheduledJob(
112-
"cleanupOldData",
113-
() => Promise.resolve(dbFunctions.deleteOldData(keep_data_for)),
114-
convertFromMinToMs(60),
115-
);
116-
117-
logger.info("Schedules have been set successfully.");
118-
} catch (error) {
119-
logger.error("Error setting schedules:", error);
120-
throw new Error(error as string);
121-
}
70+
logger.info("Starting DockStatAPI");
71+
try {
72+
const rawConfigData: unknown[] = dbFunctions.getConfig();
73+
const configData = rawConfigData[0];
74+
75+
if (
76+
!configData ||
77+
typeof (configData as config).keep_data_for !== "number" ||
78+
typeof (configData as config).fetching_interval !== "number"
79+
) {
80+
logger.error("Invalid configuration data:", configData);
81+
throw new Error("Invalid configuration data");
82+
}
83+
84+
const { keep_data_for, fetching_interval } = configData as config;
85+
86+
if (keep_data_for === undefined) {
87+
const errMsg = "keep_data_for is undefined";
88+
logger.error(errMsg);
89+
throw new Error(errMsg);
90+
}
91+
92+
if (fetching_interval === undefined) {
93+
const errMsg = "fetching_interval is undefined";
94+
logger.error(errMsg);
95+
throw new Error(errMsg);
96+
}
97+
98+
logger.info(
99+
`Scheduling: Fetching container statistics every ${fetching_interval} minutes`,
100+
);
101+
102+
logger.info(
103+
`Scheduling: Updating host statistics every ${fetching_interval} minutes`,
104+
);
105+
106+
logger.info(
107+
`Scheduling: Cleaning up Database every hour and deleting data older then ${keep_data_for} days`,
108+
);
109+
// Schedule container data fetching
110+
await initialRun("storeContainerData", storeContainerData(), true);
111+
cancelFunctions.push(
112+
scheduledJob(
113+
"storeContainerData",
114+
storeContainerData,
115+
convertFromMinToMs(fetching_interval),
116+
),
117+
);
118+
119+
// Schedule Host statistics updates
120+
await initialRun("storeHostData", storeHostData(), true);
121+
cancelFunctions.push(
122+
scheduledJob(
123+
"storeHostData",
124+
storeHostData,
125+
convertFromMinToMs(fetching_interval),
126+
),
127+
);
128+
129+
// Schedule database cleanup
130+
await initialRun(
131+
"dbFunctions.deleteOldData",
132+
dbFunctions.deleteOldData(keep_data_for),
133+
false,
134+
);
135+
cancelFunctions.push(
136+
scheduledJob(
137+
"cleanupOldData",
138+
() => Promise.resolve(dbFunctions.deleteOldData(keep_data_for)),
139+
convertFromMinToMs(60),
140+
),
141+
);
142+
143+
logger.info("Schedules have been set successfully.");
144+
} catch (error) {
145+
logger.error("Error setting schedules:", error);
146+
throw new Error(error as string);
147+
}
122148
}
123149

124-
export { setSchedules };
150+
export { setSchedules, reloadSchedules };

0 commit comments

Comments
 (0)