allow admins to enable configurable disk I/O profiling

pull/1/head
ansuz 3 years ago
parent 755228e43c
commit b65730b853

@ -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,

@ -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');

@ -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');

@ -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);
}));
});
});

@ -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);
}
};

@ -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);
});
};

@ -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);

@ -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);

Loading…
Cancel
Save