854 lines
25 KiB
JavaScript
854 lines
25 KiB
JavaScript
/*
|
|
Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/*global JSCRUND */
|
|
global.JSCRUND = {};
|
|
|
|
// Modules:
|
|
JSCRUND.mynode = require("database-jones");
|
|
JSCRUND.unified_debug = require("unified_debug");
|
|
JSCRUND.udebug = JSCRUND.unified_debug.getLogger("jscrund.js");
|
|
JSCRUND.stats = require(JSCRUND.mynode.api.stats);
|
|
|
|
// Backends:
|
|
JSCRUND.mysqljs = require('./jscrund_mysqljs');
|
|
|
|
JSCRUND.errors = [];
|
|
|
|
var DEBUG, DETAIL;
|
|
var path = require("path"),
|
|
fs = require("fs");
|
|
|
|
|
|
// webkit-devtools-agent allows you to profile the process from a Chrome browser
|
|
try {
|
|
require('webkit-devtools-agent');
|
|
} catch(e) { };
|
|
|
|
function usage() {
|
|
var msg = "" +
|
|
"Usage: node jscrund [options]\n" +
|
|
" --help:\n" +
|
|
" -h : Print help (this message)\n" +
|
|
" -A : Run \"A\" tests (default)\n" +
|
|
" -B : Run \"B\" tests\n" +
|
|
" --varchar=size\n" +
|
|
" : Specify varchar size in B tests (default 10)\n" +
|
|
" --adapter=ndb\n" +
|
|
" -n : Use ndb adapter (default)\n" +
|
|
" --adapter=mysql\n" +
|
|
" -m : Use mysql adapter\n" +
|
|
" --spi : Run tests using DBServiceProvider SPI \n" +
|
|
" --adapter=sql\n" +
|
|
" -f : Use felix sql driver (not mysql-js api)\n" +
|
|
" --adapter=null\n" +
|
|
" : Use null driver\n" +
|
|
" --log : Write log file\n" +
|
|
" --detail: Enable detail debug output\n" +
|
|
" --debug :\n" +
|
|
" -d : Enable debug output\n" +
|
|
" -df=file: Enable debug output only from source file <file>\n" +
|
|
" -i <n> : Specify number of iterations per test (default 4000)\n" +
|
|
" --delay=<m>,<n>\n" +
|
|
" : Delay <m> seconds after first iteration and <n> seconds before exiting\n" +
|
|
" --modes :\n" +
|
|
" --mode : Specify modes to run (default indy,each,bulk)\n" +
|
|
" --tests :\n" +
|
|
" --test : Specify tests to run (default persist,find,remove)\n" +
|
|
" -r <n> : Repeat tests #n times (default 1, n<0: forever)\n" +
|
|
" --trace :\n" +
|
|
" -t : Enable trace output\n" +
|
|
" --set prop=value: set connection property prop to value\n" +
|
|
" -E <name> \n" +
|
|
" --deployment=<name>\n: use deplyment <name> from jones_deployments.js\n"
|
|
;
|
|
console.log(msg);
|
|
process.exit(1);
|
|
}
|
|
|
|
//handle command line arguments
|
|
function parse_command_line(options) {
|
|
var i, val, values, pair, m, n;
|
|
for(i = 2; i < process.argv.length ; i++) {
|
|
val = process.argv[i];
|
|
switch (val) {
|
|
case '--help':
|
|
case '-h':
|
|
options.exit = true;
|
|
break;
|
|
case '-A':
|
|
options.doClass = "A";
|
|
break;
|
|
case '-B':
|
|
options.doClass = "B";
|
|
break;
|
|
case '-n':
|
|
options.adapter = "ndb";
|
|
break;
|
|
case '-m':
|
|
options.adapter = "mysql";
|
|
break;
|
|
case '-f':
|
|
options.adapter = "sql";
|
|
break;
|
|
case '-i':
|
|
var iterList = process.argv[++i];
|
|
options.iterations = iterList.split('\,');
|
|
for(m = 0 ; m < options.iterations.length; ++m) {
|
|
n = options.iterations[m]
|
|
if (isNaN(n)) {
|
|
console.log('iterations value ',n,' is not allowed');
|
|
options.exit = true;
|
|
}
|
|
}
|
|
break;
|
|
case '-r':
|
|
options.nRuns = parseInt(process.argv[++i]);
|
|
if (isNaN(options.nRuns)) {
|
|
console.log('runs value is not allowed:', process.argv[i]);
|
|
options.exit = true;
|
|
}
|
|
break;
|
|
case '-E':
|
|
options.deployment = process.argv[++i];
|
|
break;
|
|
case '--stats':
|
|
options.stats = true;
|
|
break;
|
|
case '--debug':
|
|
case '-d':
|
|
JSCRUND.unified_debug.on();
|
|
JSCRUND.unified_debug.level_debug();
|
|
options.printStackTraces = true;
|
|
break;
|
|
case '--detail':
|
|
JSCRUND.unified_debug.on();
|
|
JSCRUND.unified_debug.level_detail();
|
|
options.printStackTraces = true;
|
|
break;
|
|
case '--trace':
|
|
case '-t':
|
|
options.printStackTraces = true;
|
|
break;
|
|
case '--log':
|
|
options.log = true;
|
|
break;
|
|
case '--spi':
|
|
options.spi = true;
|
|
break;
|
|
case '--set':
|
|
i++; // next argument
|
|
pair = process.argv[i].split('=');
|
|
if(pair.length === 2) {
|
|
if(isFinite(parseInt(pair[1]))) {
|
|
pair[1] = parseInt(pair[1])
|
|
}
|
|
if(DEBUG) JSCRUND.udebug.log("Setting global:", pair[0], "=", pair[1]);
|
|
options.setProp[pair[0]] = pair[1];
|
|
}
|
|
else {
|
|
console.log("Invalid --set option " + process.argv[i]);
|
|
options.exit = true;
|
|
}
|
|
break;
|
|
default:
|
|
values = val.split('=');
|
|
if (values.length === 2) {
|
|
switch (values[0]) {
|
|
case '--count':
|
|
options.count = values[1];
|
|
break;
|
|
case '--adapter':
|
|
options.adapter = values[1];
|
|
break;
|
|
case '--mode':
|
|
case '--modes':
|
|
options.modes = values[1];
|
|
options.modeNames = options.modes.split('\,');
|
|
// validate mode names
|
|
for (m = 0; m < options.modeNames.length; ++m) {
|
|
switch (options.modeNames[m]) {
|
|
case 'indy':
|
|
case 'each':
|
|
case 'bulk':
|
|
break;
|
|
default:
|
|
console.log('Invalid mode ' + options.modeNames[m]);
|
|
options.exit = true;
|
|
}
|
|
}
|
|
break;
|
|
case '--test':
|
|
case '--tests':
|
|
options.tests = values[1];
|
|
options.testNames = options.tests.split('\,');
|
|
// validate test names
|
|
for (var t = 0; t < options.testNames.length; ++t) {
|
|
switch (options.testNames[t]) {
|
|
case 'persist':
|
|
case 'find':
|
|
case 'remove':
|
|
case 'setVarchar':
|
|
case 'clearVarchar':
|
|
break;
|
|
default:
|
|
console.log('Invalid test ' + options.testNames[t]);
|
|
options.exit = true;
|
|
}
|
|
}
|
|
break;
|
|
case '-df':
|
|
unified_debug.on();
|
|
unified_debug.set_file_level(values[1], 5);
|
|
break;
|
|
case '--delay':
|
|
var delays = values[1].split(',');
|
|
options.delay_pre = delays[0];
|
|
options.delay_post = delays[1];
|
|
break;
|
|
case '--varchar':
|
|
options.B_varchar_size = values[1];
|
|
break;
|
|
case '--deployment':
|
|
options.deployment = values[1];
|
|
break;
|
|
default:
|
|
console.log('Invalid option ' + val);
|
|
options.exit = true;
|
|
}
|
|
} else {
|
|
console.log('Invalid option ' + val);
|
|
options.exit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Timer functions for crund. Multiple timers can be used simultaneously,
|
|
* as long as each timer is created via new Timer().
|
|
* start() starts the timer.
|
|
* stop() stops the timer and writes results.
|
|
* mode is the mode of operation (indy, each, or bulk)
|
|
* operation is the operation (e.g. persist, find, delete)
|
|
* numberOfIterations is the number of iterations of each operation
|
|
*/
|
|
function Timer() {
|
|
}
|
|
|
|
Timer.prototype.start = function(mode, operation, numberOfIterations) {
|
|
//console.log('lib.Timer.start', mode, operation, 'iterations:', numberOfIterations);
|
|
this.mode = mode;
|
|
this.operation = operation;
|
|
this.numberOfIterations = numberOfIterations;
|
|
this.current = Date.now();
|
|
};
|
|
|
|
Timer.prototype.stop = function() {
|
|
function pad(str, count, onRight) {
|
|
while (str.length < count)
|
|
str = (onRight ? str + ' ' : ' ' + str);
|
|
return str;
|
|
}
|
|
function rpad(num, str) {
|
|
return pad(str, num, true);
|
|
}
|
|
function lpad(num, str) {
|
|
return pad(str, num, false);
|
|
}
|
|
this.interval = Date.now() - this.current;
|
|
this.average = this.interval / this.numberOfIterations;
|
|
var ops = Math.round(this.numberOfIterations * 1000 / this.interval);
|
|
console.log(rpad(18, this.mode + ' ' + this.operation),
|
|
' time: ' + lpad(4, this.interval.toString()) + 'ms',
|
|
' avg latency: ' + lpad(4, this.average.toFixed(3)) + 'ms',
|
|
' ops/s: ' + lpad(4, ops.toString()));
|
|
};
|
|
|
|
/** Error reporter
|
|
*/
|
|
function appendError(error) {
|
|
JSCRUND.errors.push(error);
|
|
if ((options.printStackTraces) && typeof(error.stack) !== 'undefined') {
|
|
JSCRUND.errors.push(error.stack);
|
|
}
|
|
};
|
|
|
|
function verifyObject(that) {
|
|
for (var prop in this) {
|
|
// only verify immediate properties, not inherited ones
|
|
if (this.hasOwnProperty(prop)) {
|
|
// only compare value, not type, since long numbers mapped to string
|
|
//if (this[prop] !== that[prop])
|
|
if (this[prop] != that[prop])
|
|
appendError('Error: data mismatch for property '
|
|
+ this.constructor.name + '.' + prop
|
|
+ ' expected: ' + JSON.stringify(this[prop])
|
|
+ ' actual: ' + JSON.stringify(that[prop]));
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Constructor for domain object for A mapped to table a.
|
|
*/
|
|
function A() {
|
|
}
|
|
|
|
A.prototype.init = function(i) {
|
|
this.id = i;
|
|
this.cint = -i;
|
|
this.clong = -i;
|
|
this.cfloat = -i;
|
|
this.cdouble = -i;
|
|
};
|
|
|
|
A.prototype.verify = verifyObject;
|
|
|
|
/** Constructor for domain object for B mapped to table b.
|
|
*/
|
|
function B() {
|
|
}
|
|
|
|
B.prototype.init = function(i) {
|
|
this.id = i;
|
|
this.cint = -i;
|
|
this.clong = -i;
|
|
this.cfloat = -i;
|
|
this.cdouble = -i;
|
|
};
|
|
|
|
B.prototype.setVarchar = function(len) {
|
|
this.cvarchar_def = ___;
|
|
};
|
|
|
|
B.prototype.verify = verifyObject;
|
|
|
|
|
|
/** Result Logging
|
|
*/
|
|
function currentDateString() {
|
|
function zpad(s) {
|
|
return (s.length == 1) ? "0" + s : s;
|
|
}
|
|
var d = new Date();
|
|
var yy = d.getFullYear();
|
|
var mm = zpad("" + (d.getMonth() + 1));
|
|
var dd = zpad("" + d.getDate());
|
|
var hh = zpad("" + d.getHours());
|
|
var mn = zpad("" + d.getMinutes());
|
|
var sc = zpad("" + d.getSeconds());
|
|
return ("" + yy + mm + dd + "_" + hh + mn + sc);
|
|
};
|
|
|
|
function ResultLog(enabled) {
|
|
this.enabled = enabled;
|
|
if(enabled) {
|
|
this.name = "log_" + currentDateString() + ".txt";
|
|
this.fd = fs.openSync(this.name, 'a');
|
|
} else {
|
|
this.name = "[none]";
|
|
this.message = "";
|
|
}
|
|
}
|
|
|
|
ResultLog.prototype.write = function(message) {
|
|
if(this.enabled) {
|
|
var buffer = new Buffer(message);
|
|
fs.writeSync(this.fd, buffer, 0, buffer.length);
|
|
} else {
|
|
this.message += message;
|
|
}
|
|
};
|
|
|
|
ResultLog.prototype.close = function() {
|
|
if(this.enabled) {
|
|
fs.closeSync(this.fd);
|
|
} else {
|
|
console.log(this.message);
|
|
}
|
|
};
|
|
|
|
/** Options are set up based on command line.
|
|
* Properties are set up based on options.
|
|
*/
|
|
function main() {
|
|
var config_file_exists = false;
|
|
|
|
/* Default options: */
|
|
var options = {
|
|
'doClass' : 'A',
|
|
'adapter' : 'ndb',
|
|
'database': 'jscrund',
|
|
'modes': 'indy,each,bulk',
|
|
'tests': null,
|
|
'iterations': [4000],
|
|
'stats': false,
|
|
'spi': false,
|
|
'log': false,
|
|
'nRuns': 1,
|
|
'setProp' : {},
|
|
'delay_pre' : 0,
|
|
'delay_post' : 0,
|
|
'B_varchar_size' : 10,
|
|
'deployment' : 'test'
|
|
};
|
|
|
|
/* Options from config file */
|
|
try {
|
|
var config_file = require("./jscrund.config");
|
|
config_file_exists = true;
|
|
for(var i in config_file.options) {
|
|
if(config_file.options.hasOwnProperty(i)) {
|
|
options[i] = config_file.options[i];
|
|
}
|
|
}
|
|
}
|
|
catch(e) {
|
|
if (e.message.indexOf('Cannot find module') === -1) {
|
|
console.log(e);
|
|
console.log(e.name, 'reading jscrund.config:', e.message, '\nPlease correct this error and try again.\n');
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
/* Options from command line */
|
|
parse_command_line(options);
|
|
|
|
if (options.exit) {
|
|
usage();
|
|
process.exit(0);
|
|
}
|
|
|
|
/* Global udebug level; may have been set by options */
|
|
DEBUG = JSCRUND.udebug.is_debug();
|
|
DETAIL = JSCRUND.udebug.is_detail();
|
|
|
|
/* Create the string value for varchar tests */
|
|
if(options.B_varchar_size > 0) {
|
|
options.B_varchar_value = "";
|
|
for(var i = 0 ; i < options.B_varchar_size ; i++) {
|
|
options.B_varchar_value += String.fromCharCode(48 + (i % 64));
|
|
}
|
|
}
|
|
|
|
/* Fetch the backend implementation */
|
|
if(options.spi) {
|
|
JSCRUND.spiAdapter = require('./jscrund_dbspi');
|
|
JSCRUND.implementation = new JSCRUND.spiAdapter.implementation();
|
|
} else if(options.adapter == 'sql') {
|
|
JSCRUND.sqlAdapter = require('./jscrund_sql');
|
|
JSCRUND.implementation = new JSCRUND.sqlAdapter.implementation();
|
|
} else if(options.adapter == 'null') {
|
|
JSCRUND.nullAdapter = require('./jscrund_null');
|
|
JSCRUND.implementation = new JSCRUND.nullAdapter.implementation();
|
|
} else {
|
|
JSCRUND.implementation = new JSCRUND.mysqljs.implementation();
|
|
}
|
|
|
|
/* Get connection properties */
|
|
var properties;
|
|
if(typeof JSCRUND.implementation.getConnectionProperties === 'function') {
|
|
properties = JSCRUND.implementation.getConnectionProperties();
|
|
} else {
|
|
properties = new JSCRUND.mynode.ConnectionProperties(options.adapter, options.deployment);
|
|
}
|
|
|
|
/* Then mix in properties from the command line */
|
|
properties.database = options.database;
|
|
for(i in options.setProp) {
|
|
if(options.setProp.hasOwnProperty(i)) {
|
|
properties[i] = options.setProp[i];
|
|
}
|
|
}
|
|
|
|
/* Finally store the complete properties object in the options */
|
|
options.properties = properties;
|
|
|
|
/* Force GC is available if node is run with the --expose-gc option
|
|
*/
|
|
options.use_gc = ( typeof global.gc === 'function' );
|
|
|
|
var logFile = new ResultLog(options.log);
|
|
new JSCRUND.mynode.TableMapping("a").applyToClass(A);
|
|
new JSCRUND.mynode.TableMapping("b").applyToClass(B);
|
|
options.annotations = [ A, B ];
|
|
|
|
var generateAllParameters = function(numberOfParameters) {
|
|
var result = [];
|
|
for (var i = 0; i < numberOfParameters; ++i) {
|
|
result[i] = generateParameters(i);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
var generateParameters = function(i) {
|
|
return {'key': generateKey(i), 'object': generateObject(i)};
|
|
};
|
|
|
|
var generateKey = function(i) {
|
|
return i;
|
|
};
|
|
|
|
var generateObject = function(i) {
|
|
var result;
|
|
if(options.doClass == "A") {
|
|
result = new A();
|
|
} else if(options.doClass == "B") {
|
|
result = new B();
|
|
} else {
|
|
assert(false);
|
|
}
|
|
result.init(i);
|
|
return result;
|
|
};
|
|
|
|
|
|
// mainTestLoop (-r 10)
|
|
// batchSizeLoop (-i 1,10,100)
|
|
// ModeLoop (--modes=indy,each)
|
|
// TestsLoop (--tests=persist,remove)
|
|
|
|
var runTests = function(options) {
|
|
|
|
var timer = new Timer();
|
|
|
|
var modeNames = options.modes.split('\,');
|
|
var modeNumber = 0;
|
|
var mode;
|
|
var modeName;
|
|
|
|
var testNames;
|
|
var testNumber = 0;
|
|
var operation;
|
|
var testName;
|
|
|
|
var key;
|
|
var object;
|
|
|
|
var numberOfBatchLoops = options.iterations.length;
|
|
var batchLoopNumber = 0;
|
|
var numberOfIterations;
|
|
var iteration;
|
|
|
|
var nRun = 0;
|
|
var nRuns = (options.nRuns < 0 ? Infinity : options.nRuns);
|
|
var nReport = 0;
|
|
|
|
var operationsDoneCallback;
|
|
var testsDoneCallback;
|
|
var resultStats = [];
|
|
|
|
var parameters;
|
|
|
|
/* Which tests to run */
|
|
if(options.tests) { // Explicit test names
|
|
testNames = options.tests.split('\,');
|
|
} else {
|
|
switch(options.doClass) {
|
|
case 'B':
|
|
testNames = [ 'persist', 'setVarchar', 'find', 'clearVarchar', 'remove' ];
|
|
break;
|
|
case 'A':
|
|
default:
|
|
testNames = [ 'persist', 'find', 'remove' ];
|
|
break;
|
|
};
|
|
}
|
|
|
|
/** Recursively call the operation numberOfIterations times in autocommit mode
|
|
* and then call the operationsDoneCallback
|
|
*/
|
|
var indyOperationsLoop = function(err) {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.indyOperationsLoop iteration:', iteration, 'err:', err);
|
|
// check result
|
|
if (err) {
|
|
appendError(err);
|
|
}
|
|
// call implementation operation
|
|
if (iteration < numberOfIterations) {
|
|
operation.apply(JSCRUND.implementation, [parameters[iteration], indyOperationsLoop]);
|
|
iteration++;
|
|
} else {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.indyOperationsLoop iteration:', iteration, 'complete.');
|
|
timer.stop();
|
|
resultStats.push({
|
|
name: testName + "," + modeName,
|
|
time: timer.interval
|
|
});
|
|
setImmediate(operationsDoneCallback);
|
|
}
|
|
};
|
|
|
|
/** Call indyOperationsLoop for all of the tests in testNames
|
|
*/
|
|
var indyTestsLoop = function() {
|
|
testName = testNames[testNumber];
|
|
operation = JSCRUND.implementation[testName];
|
|
operationsDoneCallback = indyTestsLoop;
|
|
if (testNumber < testNames.length) {
|
|
if(options.use_gc) global.gc(); // Full GC between tests
|
|
testNumber++;
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.indyTestsLoop', testNumber, 'of', testNames.length, ':', testName);
|
|
iteration = 0;
|
|
timer.start(modeName, testName, numberOfIterations);
|
|
setImmediate(indyOperationsLoop, null);
|
|
} else {
|
|
// done with all indy tests
|
|
// stop timer and report
|
|
setImmediate(testsDoneCallback);
|
|
}
|
|
};
|
|
|
|
/** Finish the each operations loop after commit.
|
|
*/
|
|
var eachCommitDoneCallback = function(err) {
|
|
// check result
|
|
if (err) {
|
|
appendError(err);
|
|
}
|
|
timer.stop();
|
|
resultStats.push({
|
|
name: testName + "," + modeName,
|
|
time: timer.interval
|
|
});
|
|
setImmediate(operationsDoneCallback);
|
|
};
|
|
|
|
/** Call the operation numberOfIterations times within a transaction
|
|
* and then call the operationsDoneCallback
|
|
*/
|
|
var eachOperationsLoop = function(err) {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.eachOperationsLoop iteration:', iteration, 'err:', err);
|
|
// check result
|
|
if (err) {
|
|
appendError(err);
|
|
}
|
|
// call implementation operation
|
|
if (iteration < numberOfIterations) {
|
|
operation.apply(JSCRUND.implementation, [parameters[iteration], eachOperationsLoop]);
|
|
iteration++;
|
|
} else {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.eachOperationLoop iteration:', iteration, 'complete.');
|
|
JSCRUND.implementation.commit(eachCommitDoneCallback);
|
|
}
|
|
};
|
|
|
|
/** Call eachOperationsLoop for all of the tests in testNames
|
|
*/
|
|
var eachTestsLoop = function() {
|
|
testName = testNames[testNumber];
|
|
operation = JSCRUND.implementation[testName];
|
|
operationsDoneCallback = eachTestsLoop;
|
|
if (testNumber < testNames.length) {
|
|
if(options.use_gc) global.gc(); // Full GC between tests
|
|
testNumber++;
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.eachTestsLoop', testNumber, 'of', testNames.length, ':', testName);
|
|
iteration = 0;
|
|
timer.start(modeName, testName, numberOfIterations);
|
|
JSCRUND.implementation.begin(function(err) {
|
|
setImmediate(eachOperationsLoop, null);
|
|
});
|
|
} else {
|
|
// done with all each tests
|
|
// stop timer and report
|
|
testsDoneCallback();
|
|
}
|
|
};
|
|
|
|
/** Check the results of a bulk execute.
|
|
*/
|
|
var bulkCheckBatchCallback = function(err) {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.bulkCheckBatchCallback', err);
|
|
// check result
|
|
if (err) {
|
|
appendError(err);
|
|
}
|
|
timer.stop();
|
|
resultStats.push({
|
|
name: testName + "," + modeName,
|
|
time: timer.interval
|
|
});
|
|
setImmediate(bulkTestsLoop);
|
|
};
|
|
|
|
/** Check the results of a bulk operation. It will be executed
|
|
* numberOfIterations times per test.
|
|
*/
|
|
var bulkCheckOperationCallback = function(err) {
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.bulkCheckOperationCallback', err);
|
|
if (err) {
|
|
appendError(err);
|
|
}
|
|
};
|
|
|
|
/** Construct one batch and execute it for all of the tests in testNames
|
|
*/
|
|
var bulkTestsLoop = function() {
|
|
testName = testNames[testNumber];
|
|
operation = JSCRUND.implementation[testName];
|
|
operationsDoneCallback = bulkTestsLoop;
|
|
if (testNumber < testNames.length) {
|
|
if(options.use_gc) global.gc(); // Full GC between tests
|
|
testNumber++;
|
|
if(DETAIL) JSCRUND.udebug.log_detail('jscrund.bulkTestsLoop', testNumber, 'of', testNames.length, ':', testName);
|
|
timer.start(modeName, testName, numberOfIterations);
|
|
JSCRUND.implementation.createBatch(function(err) {
|
|
for (iteration = 0; iteration < numberOfIterations; ++iteration) {
|
|
operation.apply(JSCRUND.implementation, [parameters[iteration], bulkCheckOperationCallback]);
|
|
}
|
|
JSCRUND.implementation.executeBatch(bulkCheckBatchCallback);
|
|
});
|
|
} else {
|
|
// done with all bulk tests
|
|
testsDoneCallback();
|
|
}
|
|
};
|
|
|
|
/** Run all modes specified in --modes: default is indy, each, bulk.
|
|
*
|
|
*/
|
|
var modeLoop = function() {
|
|
modeName = modeNames[modeNumber];
|
|
mode = modes[modeNumber];
|
|
testNumber = 0;
|
|
if (modeNumber < modes.length) {
|
|
modeNumber++;
|
|
console.log('\njscrund.modeLoop ', modeName, "[ size",numberOfIterations,"]");
|
|
mode.apply(runTests);
|
|
} else {
|
|
if (JSCRUND.errors.length !== 0) {
|
|
console.log(JSCRUND.errors);
|
|
}
|
|
report();
|
|
batchSizeLoop();
|
|
}
|
|
};
|
|
|
|
function report() {
|
|
var opNames, opTimes, r, adapter;
|
|
|
|
adapter = options.adapter + (options.spi ? "(spi)" : "");
|
|
opNames = "rtime[ms]," + adapter + '\t';
|
|
opTimes = "" + numberOfIterations + '\t';
|
|
|
|
while (r = resultStats.shift()) {
|
|
opNames += r.name + '\t';
|
|
opTimes += r.time + '\t';
|
|
}
|
|
|
|
if(! nReport++) {
|
|
logFile.write(opNames + '\n');
|
|
}
|
|
|
|
logFile.write(opTimes + '\n');
|
|
}
|
|
|
|
/** Iterate over batch sizes
|
|
*/
|
|
function batchSizeLoop() {
|
|
if(batchLoopNumber < numberOfBatchLoops) {
|
|
numberOfIterations = options.iterations[batchLoopNumber];
|
|
batchLoopNumber++;
|
|
parameters = generateAllParameters(numberOfIterations);
|
|
modeNumber = 0;
|
|
setImmediate(modeLoop);
|
|
} else {
|
|
mainTestLoop();
|
|
}
|
|
}
|
|
|
|
/** Run test with all modes.
|
|
*/
|
|
var mainTestLoop = function() {
|
|
if (nRun++ >= nRuns) {
|
|
console.log('\ndone: ' + nRuns + ' runs.');
|
|
console.log("\nappending results to file: " + logFile.name);
|
|
logFile.close();
|
|
if (options.stats) {
|
|
JSCRUND.stats.peek();
|
|
}
|
|
JSCRUND.implementation.close(function(err) {
|
|
if(options.delay_post > 0) {
|
|
console.log("Delaying", options.delay_post, "seconds");
|
|
setTimeout(process.exit, 1000 * options.delay_post);
|
|
} else {
|
|
process.exit(err ? 1 : 0);
|
|
}
|
|
});
|
|
} else {
|
|
console.log('\nRun #' + nRun + ' of ' + nRuns);
|
|
batchLoopNumber = 0;
|
|
if(nRun === 2 && (options.delay_pre > 0)) {
|
|
console.log("Waiting", options.delay_pre, "seconds...");
|
|
setTimeout(batchSizeLoop, 1000 * options.delay_pre);
|
|
options.delay_pre = 0;
|
|
} else {
|
|
batchSizeLoop();
|
|
}
|
|
}
|
|
};
|
|
|
|
// runTests starts here
|
|
var modeTable = {
|
|
'indy': indyTestsLoop,
|
|
'each': eachTestsLoop,
|
|
'bulk': bulkTestsLoop
|
|
};
|
|
var modes = [];
|
|
for (var m = 0; m < modeNames.length; ++m) {
|
|
modes.push(modeTable[modeNames[m]]);
|
|
}
|
|
|
|
console.log('running tests with options:\n', options);
|
|
JSCRUND.implementation.initialize(options, function(err) {
|
|
// initialization complete
|
|
if (err) {
|
|
console.log('Error initializing JSCRUND.implementation:', err);
|
|
process.exit(1);
|
|
} else {
|
|
testsDoneCallback = modeLoop;
|
|
mainTestLoop();
|
|
}
|
|
});
|
|
};
|
|
|
|
// create database
|
|
JSCRUND.metadataManager = require("jones-ndb").getDBMetadataManager(properties);
|
|
|
|
JSCRUND.metadataManager.runSQL(path.join(__dirname, "./create.sql"), function(err) {
|
|
if (err) {
|
|
console.log('Error creating tables.', err);
|
|
process.exit(1);
|
|
}
|
|
|
|
// if database create successful, run tests
|
|
runTests(options);
|
|
});
|
|
|
|
}
|
|
|
|
main();
|
|
|