allow admins to enable configurable disk I/O profiling

This commit is contained in:
ansuz 2022-03-07 18:42:00 +05:30
parent 755228e43c
commit b65730b853
8 changed files with 148 additions and 1 deletions

View File

@ -315,6 +315,9 @@ var instanceStatus = function (Env, Server, cb) {
disableIntegratedEviction: Env.disableIntegratedEviction,
disableIntegratedTasks: Env.disableIntegratedTasks,
enableProfiling: Env.enableProfiling,
profilingWindow: Env.profilingWindow,
maxUploadSize: Env.maxUploadSize,
premiumUploadSize: Env.premiumUploadSize,

View File

@ -24,6 +24,8 @@ SET_PREMIUM_UPLOAD_SIZE
// BACKGROUND PROCESSES
DISABLE_INTEGRATED_TASKS
DISABLE_INTEGRATED_EVICTION
ENABLE_PROFILING
SET_PROFILING_WINDOW
// BROADCAST
SET_LAST_BROADCAST_HASH
@ -143,6 +145,16 @@ var makeIntegerSetter = function (attr) {
return makeGenericSetter(attr, args_isInteger);
};
var arg_isPositiveInteger = function (args) {
return Array.isArray(args) && isInteger(args[0]) && args[0] > 0;
};
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['ENABLE_PROFILING', [true]]], console.log)
commands.ENABLE_PROFILING = makeBooleanSetter('enableProfiling');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_PROFILING_WINDOW', [10000]]], console.log)
commands.SET_PROFILING_WINDOW = makeGenericSetter('profilingWindow', arg_isPositiveInteger);
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_MAX_UPLOAD_SIZE', [50 * 1024 * 1024]]], console.log)
commands.SET_MAX_UPLOAD_SIZE = makeIntegerSetter('maxUploadSize');

View File

@ -54,6 +54,10 @@ module.exports.create = function (config) {
launchTime: +new Date(),
enableProfiling: false,
profilingWindow: 10000,
bytesWritten: 0,
inactiveTime: config.inactiveTime,
archiveRetentionTime: config.archiveRetentionTime,
accountRetentionTime: config.accountRetentionTime,
@ -222,6 +226,15 @@ module.exports.create = function (config) {
return typeof(config[key]) === 'string'? config[key]: def;
};
Env.incrementBytesWritten = function (n) {
if (!Env.enableProfiling) { return; }
if (!n || typeof(n) !== 'number' || n < 0) { return; }
Env.bytesWritten += n;
setTimeout(function () {
Env.bytesWritten -= n;
}, Env.profilingWindow);
};
paths.pin = keyOrDefaultString('pinPath', './pins');
paths.block = keyOrDefaultString('blockPath', './block');
paths.data = keyOrDefaultString('filePath', './datastore');

View File

@ -380,10 +380,14 @@ const storeMessage = function (Env, channel, msg, isCp, optionalMessageHash, cb)
// Message stored, call back
cb();
index.size += msgBin.length;
var msgLength = msgBin.length;
index.size += msgLength;
// handle the next element in the queue
next();
// keep track of how many bytes are written
Env.incrementBytesWritten(msgLength);
}));
});
});

View File

@ -131,11 +131,13 @@ var upload = function (Env, safeKey, content, cb) {
blobstage.write(dec);
session.currentUploadSize += len;
cb(void 0, dec.length);
Env.incrementBytesWritten(len);
});
} else {
session.blobstage.write(dec);
session.currentUploadSize += len;
cb(void 0, dec.length);
Env.incrementBytesWritten(len);
}
};

View File

@ -49,6 +49,7 @@ Block.archive = function (Env, publicKey, _cb) {
return void cb('E_INVALID_BLOCK_ARCHIVAL_PATH');
}
// TODO Env.incrementBytesWritten
Fse.move(currentPath, archivePath, {
overwrite: true,
}, cb);
@ -83,6 +84,7 @@ Block.write = function (Env, publicKey, buffer, _cb) {
}));
}).nThen(function () {
Fs.writeFile(path, buffer, { encoding: 'binary' }, cb);
Env.incrementBytesWritten(buffer && buffer.length);
});
};

View File

@ -284,6 +284,13 @@ var send404 = function (res, path) {
send404(res);
});
};
app.get('/api/profiling', function (req, res, next) {
if (!Env.enableProfiling) { return void send404(res); }
res.setHeader('Content-Type', 'text/javascript');
res.send(JSON.stringify({
bytesWritten: Env.bytesWritten,
}));
});
app.use(function (req, res, next) {
res.status(404);

View File

@ -66,6 +66,7 @@ define([
],
'stats': [ // Msg.admin_cat_stats
'cp-admin-refresh-stats',
'cp-admin-uptime',
'cp-admin-active-sessions',
'cp-admin-active-pads',
'cp-admin-open-files',
@ -85,6 +86,8 @@ define([
'performance': [ // Msg.admin_cat_performance
'cp-admin-refresh-performance',
'cp-admin-performance-profiling',
'cp-admin-enable-disk-measurements',
'cp-admin-bytes-written',
],
'network': [ // Msg.admin_cat_network
'cp-admin-update-available',
@ -644,6 +647,29 @@ define([
return $div;
};
Messages.admin_uptimeTitle = 'Launch time';
Messages.admin_uptimeHint = 'Date and time at which the server was launched';
create['uptime'] = function () {
var key = 'uptime';
var $div = makeBlock(key); // Msg.admin_activeSessionsHint, .admin_activeSessionsTitle
var pre = h('pre');
var set = function () {
var uptime = APP.instanceStatus.launchTime;
if (typeof(uptime) !== 'number') { return; }
pre.innerText = new Date(uptime);
};
set();
$div.append(pre);
onRefreshStats.reg(function () {
set();
});
return $div;
};
create['active-sessions'] = function () {
var key = 'active-sessions';
var $div = makeBlock(key); // Msg.admin_activeSessionsHint, .admin_activeSessionsTitle
@ -1739,6 +1765,84 @@ define([
return $div;
};
Messages.admin_enableDiskMeasurementsTitle = "Measure disk performance"; // XXX
Messages.admin_enableDiskMeasurementsHint = "If enabled, a JSON endpoint will be exposed under /api/profiling which keeps a running measurement of disk I/O within a configurable window. This setting can impact server performance and may reveal data you'd rather keep hidden. It is recommended that you leave it disabled unless you know what you are doing."; // XXX
create['enable-disk-measurements'] = makeAdminCheckbox({
key: 'enable-disk-measurements',
getState: function () {
return APP.instanceStatus.enableProfiling;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['ENABLE_PROFILING', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.enableProfiling);
});
});
},
});
Messages.admin_bytesWrittenTitle = "Disk performance measurement window";
Messages.admin_bytesWrittenHint = "If you have enabled disk performance measurements then the duration of the window can be configured below."; // XXX
Messages.admin_bytesWrittenDuration = "Duration of the window in milliseconds: {0}"; // XXX
Messages.admin_defaultDuration = "admin_defaultDuration"; // XXX
Messages.admin_setDuration = "Set duration"; // XXX
var isPositiveInteger = function (n) {
return n && typeof(n) === 'number' && n % 1 === 0 && n > 0;
};
create['bytes-written'] = function () {
var key = 'bytes-written';
var $div = makeBlock(key);
var duration = APP.instanceStatus.profilingWindow;
if (!isPositiveInteger(duration)) { duration = 10000; }
var newDuration = h('input', {type: 'number', min: 0, value: duration});
var set = h('button.btn.btn-primary', Messages.admin_setDuration);
$div.append(h('div', [
h('span.cp-admin-bytes-written-duration', Messages._getKey('admin_bytesWrittenDuration', [duration])),
h('div.cp-admin-setlimit-form', [
newDuration,
h('nav', [set])
])
]));
UI.confirmButton(set, {
classes: 'btn-primary',
multiple: true,
validate: function () {
var l = parseInt($(newDuration).val());
if (isNaN(l)) { return false; }
return true;
}
}, function () {
var d = parseInt($(newDuration).val());
if (!isPositiveInteger(d)) { return void UI.warn(Messages.error); }
var data = [d];
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['SET_PROFILING_WINDOW', data]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
return void console.error(e, response);
}
$div.find('.cp-admin-bytes-written-duration').text(Messages._getKey('admin_limit', [d]));
});
});
return $div;
};
create['update-available'] = function () { // Messages.admin_updateAvailableTitle.admin_updateAvailableHint.admin_updateAvailableLabel.admin_updateAvailableButton
if (!APP.instanceStatus.updateAvailable) { return; }
var $div = makeBlock('update-available', true);