diff --git a/performance/run.js b/performance/run.js index 9e8d4f4d265871c782135fb57b81bf16661606b0..03d3786095885a7a0ffb967136952a0056823c3d 100755 --- a/performance/run.js +++ b/performance/run.js @@ -48,9 +48,7 @@ var argv = require('optimist') .describe('h', 'display this usage message') .alias('m', 'max') .describe('m', 'maximum active users to simulate (0 == infinite)') -.default('m', 1000) -.describe('o', 'maximum *outstanding* activities to allow') -.default('o', 100) +.default('m', 10000) .alias('s', 'server') .describe('s', 'base URL to browserid server') .demand('s') @@ -80,10 +78,16 @@ var averages = [ 0.0 ]; +// outstanding incomplete activites +var outstanding = 0; + // activities complete since the last poll var completed = { }; +// how many activies does an active user undertake per second +const activitiesPerUserPerSecond = (40.0 / ( 24 * 60 * 60 )); + // activities var activity = { "signup": { @@ -144,6 +148,9 @@ Object.keys(activity).forEach(function(k) { } })(); +// a global count of how many poll iterations have been completed +var iterations = 0; + function poll() { function startNewActivity() { // what type of activity is this? @@ -156,22 +163,95 @@ function poll() { } } // start the activity! + outstanding++; activity[act].startFunc(configuration, function() { - console.log(act, "complete"); + outstanding--; + if (undefined === completed[act]) completed[act] = 0; + completed[act]++; }); } - // XXX: next work to be done is here. upon each call to poll we must: - // 1. update running averages based on activites completed while we - // were sleeping. - // 2. - // 3. determine how many activities to start based on throttling, - // current outstanding, and current active users being simulated - // 4. start those activities - // 5. schedule another poll 1s from the time the last was started - - // XXX: test... - for (var i = 0; i < 100; i++) startNewActivity(); + function updateAverages(elapsed) { + if (!iterations) return; + + var numActCompleted = 0; + Object.keys(completed).forEach(function(k) { numActCompleted += completed[k]; }); + completed = { }; + var avgUsersThisPeriod = (numActCompleted / activitiesPerUserPerSecond) * (elapsed / 1000); + + // the 1s average is a goldfish. + averages[0] = avgUsersThisPeriod; + + // for 5s and 60s averages, a little special logic to handle cases + // where we don't have enough history to dampen based on past performance + var i = 5 > iterations ? iterations * 1.0 : 5.0; + averages[1] = ((i-1) * averages[1] + avgUsersThisPeriod) / i; + var i = 60 > iterations ? iterations * 1.0 : 60.0; + averages[2] = ((i-1) * averages[2] + avgUsersThisPeriod) / i; + } + + function outputAverages() { + console.log("\t", averages[0].toFixed(2), + "\t", averages[1].toFixed(2), + "\t", averages[2].toFixed(2)); + } + + // ** how much time has elapsed since the last poll? + var elapsed; + { + var now = new Date(); + elapsed = now - lastPoll; + lastPoll = now; + } + + // ** update running averages ** + updateAverages(elapsed); + + // ** determine how many activities to start ** + + // how many active users would we like to simulate + var targetActive = args.m; + + // if we're not throttled, then we'll trying 150% as many as + // we're simulating right now. If we're not simulating at least + // 10000 active users, that shall be our lower bound + if (!targetActive) { + if (averages[0] > 10000) targetActive = averages[0] * 1.5; + else targetActive = 10000; + } + + // now how many new activities do we want to start? + var newAct = activitiesPerUserPerSecond * targetActive; + + // scale based on how much time has elapsed since the last poll + // on every iteration except the first + if (iterations) newAct *= (elapsed / 1000); + + // probabilistic rounding + { + var add = (newAct % 1.0) < Math.random() ? 0 : 1; + newAct = Math.floor(newAct) + add; + } + + // ** start activities ** + + // start the new activites until they're all started, or until we've + // got twice as many outstanding as would be required by the target we + // want to hit (which means the server can't keep up). + while (newAct >= 1.0 && outstanding < (activitiesPerUserPerSecond * targetActive * 2)) { + startNewActivity(); + newAct--; + } + + // ** schedule another wake up + var wakeUpIn = 1000 - (new Date() - lastPoll); + setTimeout(poll, wakeUpIn); + + // display averages + outputAverages(); + + iterations++; } -poll(); +console.log("Average active users simulated over the last 1s/5s/60s:"); +setTimeout(poll, 1);