smc-hub
Version:
CoCalc: Backend webserver component
66 lines (59 loc) • 2.38 kB
text/typescript
/*
The default export is an init function that watches for projects that
are idle too long and stops them.
If you set the environment variable COCALC_NO_IDLE_TIMEOUT, then
this is not used. It would be better to use the database and a server
setting for this, but an env variable is very fast to implement.
*/
import getLogger from "smc-hub/logger";
import { callback2 } from "smc-util/async-utils";
import { database } from "smc-hub/servers/database";
import { DEFAULT_QUOTAS } from "smc-util/upgrade-spec";
import { BaseProject as Project } from "./base";
const logger = getLogger("stop-idle-projects");
async function stopIdleProjects(stopProject: (string) => Promise<void>) {
logger.info("stopping all idle projects");
logger.debug("query database for all running projects");
const runningProjects = (
await callback2(database._query, {
query:
"SELECT project_id, EXTRACT(EPOCH FROM NOW() - last_edited) as idle_time, settings FROM projects WHERE state ->> 'state' = 'running'",
})
).rows;
logger.debug("got ", runningProjects);
for (const project of runningProjects) {
const { project_id, idle_time, settings } = project;
const mintime = settings?.mintime ?? DEFAULT_QUOTAS.mintime;
const always_running = settings?.always_running ?? false;
if (!always_running && idle_time > mintime) {
// stopProject is async, but we don't await it (and it doesn't raise),
// since we want to immediately stop all of them, rather than waiting
// and stopping based on outdated information.
stopProject(project_id);
}
}
}
export default function init(getProject: (string) => Project) {
if (process.env.COCALC_NO_IDLE_TIMEOUT) {
logger.info(
"NOT initializing idle project stop loop since COCALC_NO_IDLE_TIMEOUT to set"
);
return;
}
logger.info(
"initializing idle project stop loop (set environment variable COCALC_NO_IDLE_TIMEOUT to disable)"
);
const stopProject = async (project_id: string) => {
logger.info(`stopping ${project_id} due to idle timeout`);
try {
(await getProject(project_id)).stop();
logger.debug(`stopped ${project_id} successfully`);
} catch (err) {
logger.error(`error stopping ${project_id} -- ${err}`);
}
};
setInterval(() => {
stopIdleProjects(stopProject);
}, 60000);
stopIdleProjects(stopProject);
}