Skip to content

Commit

Permalink
Merge pull request #76 from fed135/next
Browse files Browse the repository at this point in the history
[Release] v1.12.2
  • Loading branch information
fed135 authored Mar 22, 2019
2 parents 177d6e9 + b914527 commit 8b7a6b7
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 65 deletions.
29 changes: 29 additions & 0 deletions tests/profiling/dao.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function simulateNetwork() {
let tick = 0;
while(true) {
if (++tick > 0xffff) break;
}
return true;
}

function calculateResponseTime(numItems) {
return Math.round(30 + numItems * 0.5);
}

function getAssets(ids, { language }) {
return new Promise((resolve, reject) => {
simulateNetwork();
setTimeout(() => resolve(ids.map(id => ({ id, language }))), calculateResponseTime(ids.length));
});
}

function getErroredRequest(ids, { language }) {
return new Promise((resolve, reject) => {
throw new Error('Something went wrong');
});
}

module.exports = {
getAssets,
getErroredRequest,
};
86 changes: 30 additions & 56 deletions tests/profiling/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,77 +6,51 @@
/* Requires ------------------------------------------------------------------*/

const crypto = require('crypto');
const {getAssets, getErroredRequest} = require('../integration/utils/dao.js');
const settings = require('./settings');
const HA = require('../../src/index.js');
const {fork} = require('child_process');
const path = require('path');

/* Init ----------------------------------------------------------------------*/

// Setup
const store = HA(settings.setup);
const app = fork(path.resolve(__dirname, './testApp.js'));
const startTime = Date.now();

// Suite
const suite = {
sampleRange: 2,
completed: 0,
cacheHits: 0,
sum: 0,
timeouts: 0,
batches: 0,
startHeap: process.memoryUsage().rss,
};

store.on('query', () => { suite.batches++; });
store.on('cacheHit', () => { suite.cacheHits++; });

async function hitStore() {
if (Date.now() - startTime < settings.test.testDuration) {
setTimeout(hitStore, settings.test.requestDelay);
let finished = false;
setTimeout(() => {
if (finished === false) suite.timeouts++;
}, 500);
process.nextTick(hitStore);

// Simulate normal z-distribution
suite.sampleRange = (Math.round(Math.random()*3) === 0) ? 1:2;
const randomError = (Math.round(Math.random()*10) === 0);
const id = crypto.randomBytes(suite.sampleRange).toString('hex');
let sampleRange = (Math.round(Math.random()*3) === 0) ? 1:2;
const id = crypto.randomBytes(sampleRange).toString('hex');
const language = settings.test.languages[Math.floor(Math.random()*settings.test.languages.length)];
const before = Date.now();
if (randomError) store.config.resolver = getErroredRequest;
else store.config.resolver = getAssets;
store.get(id, { language }, crypto.randomBytes(8).toString('hex'))
.then((result) => {
if (!result || result.id !== id || result.language !== language) {
console.log(result, 'does not match', id, language);
throw new Error('integrity test failed');
}
suite.sum += (Date.now() - before);
finished = true;
suite.completed++;
}, () => {})
.catch((err) => { console.log(err); process.exit(1)} );
app.send({ id, language });
}
else {
console.log(`
${suite.completed} completed requests
${suite.cacheHits} cache hits
${JSON.stringify(await store.size())}
${suite.timeouts} timed out
avg response time ${(suite.sum / suite.completed).toFixed(3)}
${suite.batches} queries sent
${((process.memoryUsage().rss - suite.startHeap) / 1024).toFixed(2)} Kbytes allocated
`);
for (const expectation in settings.assert) {
if (suite[expectation] < settings.assert[expectation][0] || suite[expectation] > settings.assert[expectation][1]) {
console.log(expectation, 'did not match expectation', settings.assert[expectation]);
throw new Error('performance test failed');
}
}

process.exit(0);
app.send('finish');
}
}

app.on('message', async (suite) => {
console.log(`
${suite.completed} completed requests
${suite.cacheHits} cache hits
${JSON.stringify(suite.size)} in memory
${suite.timeouts} timed out
avg response time ${(suite.sum / suite.completed).toFixed(3)}
${suite.batches} queries sent
${suite.avgBatchSize} items per queries on average
${(suite.startHeap / 1024).toFixed(2)} Kbytes allocated
`);

for (const expectation in settings.assert) {
if (suite[expectation] < settings.assert[expectation][0] || suite[expectation] > settings.assert[expectation][1]) {
console.error(new Error(`Performance test failed: ${expectation} did not match expectation ${settings.assert[expectation]}`));
process.exit(1);
}
}
process.exit(0);
});

hitStore();

19 changes: 10 additions & 9 deletions tests/profiling/settings.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
const {getAssets} = require('../integration/utils/dao.js');
const {getAssets} = require('./dao.js');

module.exports = {
test: {
testDuration: 60000,
testDuration: 1000,
requestDelay: 0,
languages: ['fr', 'en', 'pr', 'it', 'ge']
languages: ['fr', 'en', 'ge', 'it', 'pr'],
},
setup: {
resolver: getAssets,
uniqueParams: ['language'],
cache: { limit: 60000, steps: 5, base: 5000 },
batch: { tick: 2, limit: 50 },
batch: { tick: 10, max: 50 },
retry: { base: 1, step: 2 },
},
assert: {
completed: [30000, 100000],
cacheHits: [1000, 4000],
timeouts: [500, 4000],
batches: [20000, 60000],
rss: [30000, 60000],
completed: [100000, 200000],
cacheHits: [25000, 70000],
timeouts: [500, 8000],
batches: [500, 4000],
rss: [90000, 200000],
avgBatchSize: [30, 50],
},
}
68 changes: 68 additions & 0 deletions tests/profiling/testApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Test app worker - allows us to saturate the request generator without impacting the app
*/

/* Requires ------------------------------------------------------------------*/

const settings = require('./settings');
const HA = require('../../src/index.js');
const {getAssets,getErroredRequest} = require('./dao');
const crypto = require('crypto');

/* Local variables -----------------------------------------------------------*/

let handbreak = false;

const suite = {
completed: 0,
cacheHits: 0,
sum: 0,
timeouts: 0,
batches: 0,
avgBatchSize: 0,
startHeap: process.memoryUsage().rss,
};

const store = HA(settings.setup);

/* Methods -------------------------------------------------------------------*/

function handleRequest(id, language) {
const randomError = (Math.round(Math.random()*10) === 0);
let finished = false;
const before = Date.now();
setTimeout(() => {
if (finished === false) suite.timeouts++;
}, 500);
if (randomError) store.config.resolver = getErroredRequest;
else store.config.resolver = getAssets;
store.get(id, { language }, crypto.randomBytes(8).toString('hex'))
.then((result) => {
finished = true;
if (handbreak) return;
if (!result || result.id !== id || result.language !== language) {
console.log(result, 'does not match', id, language);
throw new Error('integrity test failed');
}
suite.sum += (Date.now() - before);
suite.completed++;
}, () => {})
.catch((err) => { console.log(err); process.exit(1)} );
}

store.on('query', batch => { suite.batches++; suite.avgBatchSize += batch.ids.length; });
store.on('cacheHit', () => { suite.cacheHits++; });

//End
async function complete() {
handbreak = true;
suite.avgBatchSize = Math.round(suite.avgBatchSize / suite.batches);
suite.size = await store.size();
suite.startHeap = process.memoryUsage().rss - suite.startHeap;

process.send(suite);
}

/* Init ----------------------------------------------------------------------*/

process.on('message', (msg) => msg === 'finish' ? complete() : handleRequest(msg.id, msg.language));

0 comments on commit 8b7a6b7

Please sign in to comment.