Files
cloudron-box/src/apptaskmanager.js

92 lines
2.9 KiB
JavaScript
Raw Normal View History

2019-08-28 15:00:55 -07:00
'use strict';
exports = module.exports = {
scheduleTask
2019-08-28 15:00:55 -07:00
};
const assert = require('assert'),
BoxError = require('./boxerror.js'),
2019-09-19 16:40:31 -07:00
debug = require('debug')('box:apptaskmanager'),
2019-08-28 15:00:55 -07:00
fs = require('fs'),
locker = require('./locker.js'),
safe = require('safetydance'),
path = require('path'),
paths = require('./paths.js'),
scheduler = require('./scheduler.js'),
2019-08-28 15:00:55 -07:00
tasks = require('./tasks.js');
let gActiveTasks = { }; // indexed by app id
let gPendingTasks = [ ];
2019-09-24 20:29:01 -07:00
let gInitialized = false;
2019-08-28 15:00:55 -07:00
const TASK_CONCURRENCY = 3;
function waitText(lockOperation) {
if (lockOperation === locker.OP_BOX_UPDATE) return 'Waiting for Cloudron to finish updating. See the Settings view';
if (lockOperation === locker.OP_PLATFORM_START) return 'Waiting for Cloudron to initialize';
2021-08-24 09:38:51 -07:00
if (lockOperation === locker.OP_FULL_BACKUP) return 'Waiting for Cloudron to finish backup. See the Backups view';
return ''; // cannot happen
}
2019-09-24 20:29:01 -07:00
function initializeSync() {
gInitialized = true;
locker.on('unlocked', startNextTask);
}
2019-08-28 15:00:55 -07:00
// callback is called when task is finished
2021-09-07 09:57:49 -07:00
function scheduleTask(appId, taskId, options, onFinished) {
2019-08-28 15:00:55 -07:00
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof taskId, 'string');
assert.strictEqual(typeof options, 'object');
2021-09-07 09:57:49 -07:00
assert.strictEqual(typeof onFinished, 'function');
2019-08-28 15:00:55 -07:00
2019-09-24 20:29:01 -07:00
if (!gInitialized) initializeSync();
2019-08-28 15:00:55 -07:00
if (appId in gActiveTasks) {
2021-09-07 09:57:49 -07:00
return onFinished(new BoxError(BoxError.CONFLICT, `Task for %s is already active: ${appId}`));
2019-08-28 15:00:55 -07:00
}
if (Object.keys(gActiveTasks).length >= TASK_CONCURRENCY) {
debug(`Reached concurrency limit, queueing task id ${taskId}`);
2021-07-12 23:35:30 -07:00
tasks.update(taskId, { percent: 1, message: 'Waiting for other app tasks to complete' });
2021-09-07 09:57:49 -07:00
gPendingTasks.push({ appId, taskId, options, onFinished });
2019-08-28 15:00:55 -07:00
return;
}
2021-07-12 23:35:30 -07:00
const lockError = locker.recursiveLock(locker.OP_APPTASK);
2019-08-28 15:00:55 -07:00
if (lockError) {
debug(`Could not get lock. ${lockError.message}, queueing task id ${taskId}`);
2021-07-12 23:35:30 -07:00
tasks.update(taskId, { percent: 1, message: waitText(lockError.operation) });
2021-09-07 09:57:49 -07:00
gPendingTasks.push({ appId, taskId, options, onFinished });
2019-08-28 15:00:55 -07:00
return;
}
gActiveTasks[appId] = {};
const logFile = path.join(paths.LOG_DIR, appId, 'apptask.log');
if (!fs.existsSync(path.dirname(logFile))) safe.fs.mkdirSync(path.dirname(logFile)); // ensure directory
scheduler.suspendJobs(appId);
tasks.startTask(taskId, Object.assign(options, { logFile }), function (error, result) {
2021-09-07 09:57:49 -07:00
onFinished(error, result);
2019-08-28 15:00:55 -07:00
delete gActiveTasks[appId];
locker.unlock(locker.OP_APPTASK); // unlock event will trigger next task
2020-08-19 18:23:44 +02:00
scheduler.resumeJobs(appId);
2019-08-28 15:00:55 -07:00
});
}
function startNextTask() {
if (gPendingTasks.length === 0) return;
2019-08-28 15:00:55 -07:00
assert(Object.keys(gActiveTasks).length < TASK_CONCURRENCY);
const t = gPendingTasks.shift();
2021-09-07 09:57:49 -07:00
scheduleTask(t.appId, t.taskId, t.options, t.onFinished);
2019-08-28 15:00:55 -07:00
}