mirror of
https://github.com/kremalicious/umami.git
synced 2024-12-22 17:23:54 +01:00
Load testing added via npm run loadtest
- npm scripts added - loadtesting "--weight" option for quick configs - Use chalk in loadtest for logging TODO: Need better way to pass in websiteId
This commit is contained in:
parent
0acda0a468
commit
bbc7e25ee8
@ -31,7 +31,10 @@
|
|||||||
"merge-lang": "node scripts/merge-lang.js",
|
"merge-lang": "node scripts/merge-lang.js",
|
||||||
"format-lang": "node scripts/format-lang.js",
|
"format-lang": "node scripts/format-lang.js",
|
||||||
"compile-lang": "formatjs compile-folder --ast build lang-compiled",
|
"compile-lang": "formatjs compile-folder --ast build lang-compiled",
|
||||||
"check-lang": "node scripts/check-lang.js"
|
"check-lang": "node scripts/check-lang.js",
|
||||||
|
"loadtest": "node scripts/loadtest.js",
|
||||||
|
"loadtest:medium": "node scripts/loadtest.js --weight=medium",
|
||||||
|
"loadtest:heavy": "node scripts/loadtest.js --weight=heavy --verbose"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"**/*.js": [
|
"**/*.js": [
|
||||||
@ -107,6 +110,7 @@
|
|||||||
"extract-react-intl-messages": "^4.1.1",
|
"extract-react-intl-messages": "^4.1.1",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"lint-staged": "^10.4.0",
|
"lint-staged": "^10.4.0",
|
||||||
|
"loadtest": "5.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss-flexbugs-fixes": "^4.2.1",
|
"postcss-flexbugs-fixes": "^4.2.1",
|
||||||
"postcss-import": "^12.0.1",
|
"postcss-import": "^12.0.1",
|
||||||
|
140
scripts/loadtest.js
Normal file
140
scripts/loadtest.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
const loadtest = require('loadtest');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const trunc = num => +num.toFixed(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example invocations:
|
||||||
|
*
|
||||||
|
* npm run loadtest -- --weight=heavy
|
||||||
|
* npm run loadtest -- --weight=heavy --verbose
|
||||||
|
* npm run loadtest -- --weight=single --verbose
|
||||||
|
* npm run loadtest -- --weight=medium
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command line arguments like --weight=heavy and --verbose use this object
|
||||||
|
* If you are providing _alternative_ configs, use --weight
|
||||||
|
* e.g. add --weight=ultra then add commandlineOptions.ultra={}
|
||||||
|
* --verbose can be combied with any weight.
|
||||||
|
*/
|
||||||
|
const commandlineOptions = {
|
||||||
|
single: {
|
||||||
|
concurrency: 1,
|
||||||
|
requestsPerSecond: 1,
|
||||||
|
maxSeconds: 5,
|
||||||
|
maxRequests: 1,
|
||||||
|
},
|
||||||
|
// Heavy can saturate CPU which leads to requests stalling depending on machine
|
||||||
|
// Keep an eye if --verbose logs pause, or if node CPU in top is > 100.
|
||||||
|
// https://github.com/alexfernandez/loadtest#usage-donts
|
||||||
|
heavy: {
|
||||||
|
concurrency: 10,
|
||||||
|
requestsPerSecond: 200,
|
||||||
|
maxSeconds: 60,
|
||||||
|
},
|
||||||
|
// Throttled requests should not max out CPU,
|
||||||
|
medium: {
|
||||||
|
concurrency: 3,
|
||||||
|
requestsPerSecond: 5,
|
||||||
|
maxSeconds: 60,
|
||||||
|
},
|
||||||
|
verbose: { statusCallback },
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
url: 'http://localhost:3000',
|
||||||
|
method: 'POST',
|
||||||
|
concurrency: 5,
|
||||||
|
requestsPerSecond: 5,
|
||||||
|
maxSeconds: 5,
|
||||||
|
requestGenerator: (params, options, client, callback) => {
|
||||||
|
const message = JSON.stringify(mockPageView());
|
||||||
|
options.headers['Content-Length'] = message.length;
|
||||||
|
options.headers['Content-Type'] = 'application/json';
|
||||||
|
options.headers['user-agent'] = 'User-Agent: Mozilla/5.0 LoadTest';
|
||||||
|
options.body = message;
|
||||||
|
options.path = '/api/collect';
|
||||||
|
const request = client(options, callback);
|
||||||
|
request.write(message);
|
||||||
|
return request;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function getArgument() {
|
||||||
|
const weight = process.argv[2] && process.argv[2].replace('--weight=', '');
|
||||||
|
const verbose = process.argv.includes('--verbose') && 'verbose';
|
||||||
|
return [weight, verbose];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch in all command line arguments over options object
|
||||||
|
// Must do this prior to calling `loadTest()`
|
||||||
|
getArgument().map(arg => Object.assign(options, commandlineOptions[arg]));
|
||||||
|
|
||||||
|
loadtest.loadTest(options, (error, results) => {
|
||||||
|
if (error) {
|
||||||
|
return console.error(chalk.redBright('Got an error: %s', error));
|
||||||
|
}
|
||||||
|
console.log(chalk.bold(chalk.yellow('\n--------\n')));
|
||||||
|
console.log(chalk.yellowBright('Loadtests complete:'), chalk.greenBright('success'), '\n');
|
||||||
|
prettyLogItem('Total Requests:', results.totalRequests);
|
||||||
|
prettyLogItem('Total Errors:', results.totalErrors);
|
||||||
|
|
||||||
|
prettyLogItem(
|
||||||
|
'Latency(mean/min/max)',
|
||||||
|
trunc(results.meanLatencyMs),
|
||||||
|
'/',
|
||||||
|
trunc(results.maxLatencyMs),
|
||||||
|
'/',
|
||||||
|
trunc(results.minLatencyMs),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.totalErrors) {
|
||||||
|
console.log(chalk.redBright('*'), chalk.red('Total Errors:'), results.totalErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.errorCodes && Object.keys(results.errorCodes).length) {
|
||||||
|
console.log(chalk.redBright('*'), chalk.red('Error Codes:'), results.errorCodes);
|
||||||
|
}
|
||||||
|
// console.log(results);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new object for each request. Note, we could randomize values here if desired.
|
||||||
|
*
|
||||||
|
* TODO: Need a better way of passing in websiteId, hostname, URL.
|
||||||
|
*
|
||||||
|
* @param {object} payload pageview payload same as sent via tracker
|
||||||
|
*/
|
||||||
|
function mockPageView(
|
||||||
|
payload = {
|
||||||
|
website: 'fcd4c7e3-ed76-439c-9121-3a0f102df126',
|
||||||
|
hostname: 'localhost',
|
||||||
|
screen: '1680x1050',
|
||||||
|
url: '/LOADTESTING',
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
type: 'pageview',
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you pass in --verbose, this function is called
|
||||||
|
function statusCallback(error, result, latency) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellowBright(`\n## req #${result.requestIndex + 1} of ${latency.totalRequests}`),
|
||||||
|
);
|
||||||
|
prettyLogItem('Request elapsed milliseconds:', trunc(result.requestElapsed));
|
||||||
|
prettyLogItem(
|
||||||
|
'Latency(mean/max/min):',
|
||||||
|
trunc(latency.meanLatencyMs),
|
||||||
|
'/',
|
||||||
|
trunc(latency.maxLatencyMs),
|
||||||
|
'/',
|
||||||
|
trunc(latency.minLatencyMs),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prettyLogItem(label, ...args) {
|
||||||
|
console.log(chalk.redBright('*'), chalk.green(label), ...args);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user