Rework cpuShares into cpuQuota

cpuShares is the relative weight wrt other apps. This is used when
there is contention for CPU. If we want this, maybe we implement
a UI where we show all the apps and let the user re-order them.
As it stands, it is confusing.

cpuQuota is a more straightforward "hard limit" of the CPU% that you
want the app to consume.

Can be tested with : stress -c 8 -t 20s
This commit is contained in:
Girish Ramakrishnan
2024-04-10 17:38:49 +02:00
parent 2afaf1f36d
commit b4e4f26361
15 changed files with 265 additions and 91 deletions

View File

@@ -32,7 +32,7 @@ exports = module.exports = {
setIcon,
setTags,
setMemoryLimit,
setCpuShares,
setCpuQuota,
setMounts,
setAutomaticBackup,
setAutomaticUpdate,
@@ -163,7 +163,6 @@ const appstore = require('./appstore.js'),
manifestFormat = require('cloudron-manifestformat'),
notifications = require('./notifications.js'),
once = require('./once.js'),
os = require('os'),
path = require('path'),
paths = require('./paths.js'),
PassThrough = require('stream').PassThrough,
@@ -173,7 +172,6 @@ const appstore = require('./appstore.js'),
services = require('./services.js'),
shell = require('./shell.js'),
storage = require('./storage.js'),
system = require('./system.js'),
tasks = require('./tasks.js'),
tgz = require('./backupformat/tgz.js'),
TransformStream = require('stream').Transform,
@@ -185,7 +183,7 @@ const appstore = require('./appstore.js'),
_ = require('underscore');
const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.errorJson', 'apps.runState',
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuShares',
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuQuota',
'apps.label', 'apps.tagsJson', 'apps.taskId', 'apps.reverseProxyConfigJson', 'apps.servicesConfigJson', 'apps.operatorsJson',
'apps.sso', 'apps.debugModeJson', 'apps.enableBackup', 'apps.proxyAuth', 'apps.containerIp', 'apps.crontab',
'apps.creationTime', 'apps.updateTime', 'apps.enableAutomaticUpdate', 'apps.upstreamUri',
@@ -408,10 +406,10 @@ function validateMemoryLimit(manifest, memoryLimit) {
return null;
}
function validateCpuShares(cpuShares) {
assert.strictEqual(typeof cpuShares, 'number');
function validateCpuQuota(cpuQuota) {
assert.strictEqual(typeof cpuQuota, 'number');
if (cpuShares < 2 || cpuShares > 1024) return new BoxError(BoxError.BAD_FIELD, 'cpuShares has to be between 2 and 1024');
if (cpuQuota < 1 || cpuQuota > 100) return new BoxError(BoxError.BAD_FIELD, 'cpuQuota has to be between 1 and 100');
return null;
}
@@ -574,7 +572,7 @@ function removeInternalFields(app) {
const result = _.pick(app,
'id', 'appStoreId', 'installationState', 'error', 'runState', 'health', 'taskId',
'subdomain', 'domain', 'fqdn', 'certificate', 'crontab', 'upstreamUri',
'accessRestriction', 'manifest', 'portBindings', 'iconUrl', 'memoryLimit', 'cpuShares', 'operators',
'accessRestriction', 'manifest', 'portBindings', 'iconUrl', 'memoryLimit', 'cpuQuota', 'operators',
'sso', 'debugMode', 'reverseProxyConfig', 'enableBackup', 'creationTime', 'updateTime', 'ts', 'tags',
'label', 'secondaryDomains', 'redirectDomains', 'aliasDomains', 'env', 'enableAutomaticUpdate',
'storageVolumeId', 'storageVolumePrefix', 'mounts', 'enableTurn', 'enableRedis',
@@ -834,7 +832,7 @@ async function add(id, appStoreId, manifest, subdomain, domain, portBindings, da
accessRestriction = data.accessRestriction || null,
accessRestrictionJson = JSON.stringify(accessRestriction),
memoryLimit = data.memoryLimit || 0,
cpuShares = data.cpuShares || 512,
cpuQuota = data.cpuQuota || 100,
installationState = data.installationState,
runState = data.runState,
sso = 'sso' in data ? data.sso : null,
@@ -858,11 +856,11 @@ async function add(id, appStoreId, manifest, subdomain, domain, portBindings, da
const queries = [];
queries.push({
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, '
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuQuota, '
+ 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, '
+ 'enableMailbox, mailboxDisplayName, upstreamUri, enableTurn, enableRedis) '
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares,
args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuQuota,
sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon,
enableMailbox, mailboxDisplayName, upstreamUri, enableTurn, enableRedis ]
});
@@ -1568,25 +1566,25 @@ async function setMemoryLimit(app, memoryLimit, auditSource) {
return { taskId };
}
async function setCpuShares(app, cpuShares, auditSource) {
async function setCpuQuota(app, cpuQuota, auditSource) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof cpuShares, 'number');
assert.strictEqual(typeof cpuQuota, 'number');
assert.strictEqual(typeof auditSource, 'object');
const appId = app.id;
let error = checkAppState(app, exports.ISTATE_PENDING_RESIZE);
if (error) throw error;
error = validateCpuShares(cpuShares);
error = validateCpuQuota(cpuQuota);
if (error) throw error;
const task = {
args: {},
values: { cpuShares }
values: { cpuQuota }
};
const taskId = await safe(addTask(appId, exports.ISTATE_PENDING_RESIZE, task, auditSource));
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, cpuShares, taskId });
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, cpuQuota, taskId });
return { taskId };
}
@@ -2338,7 +2336,7 @@ async function clone(app, data, user, auditSource) {
const icons = await getIcons(app.id);
const dolly = _.pick(app, 'memoryLimit', 'cpuShares', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', 'tags',
const dolly = _.pick(app, 'memoryLimit', 'cpuQuota', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', 'tags',
'enableMailbox', 'mailboxDisplayName', 'mailboxName', 'mailboxDomain', 'enableInbox', 'inboxName', 'inboxDomain',
'enableTurn', 'enableRedis', 'mounts', 'enableBackup', 'enableAutomaticUpdate', 'accessRestriction', 'operators', 'sso');
@@ -2902,7 +2900,7 @@ async function loadConfig(app) {
const appConfig = safe.JSON.parse(safe.fs.readFileSync(path.join(paths.APPS_DATA_DIR, app.id + '/config.json')));
let data = {};
if (appConfig) {
data = _.pick(appConfig, 'memoryLimit', 'cpuShares', 'enableBackup', 'reverseProxyConfig', 'env', 'servicesConfig', 'label', 'tags', 'enableAutomaticUpdate');
data = _.pick(appConfig, 'memoryLimit', 'cpuQuota', 'enableBackup', 'reverseProxyConfig', 'env', 'servicesConfig', 'label', 'tags', 'enableAutomaticUpdate');
}
const icon = safe.fs.readFileSync(path.join(paths.APPS_DATA_DIR, app.id + '/icon.png'));