diff --git a/benchmarks/commonUtilities.js b/benchmarks/commonUtilities.js deleted file mode 100755 index 04d315f..0000000 --- a/benchmarks/commonUtilities.js +++ /dev/null @@ -1,287 +0,0 @@ -/** - * Functions that are used in several benchmark tests - */ -const fs = require('fs') -const Datastore = require('../lib/datastore') -const Persistence = require('../lib/persistence') -const { callbackify } = require('util') -let executeAsap - -try { - executeAsap = setImmediate -} catch (e) { - executeAsap = process.nextTick -} - -/** - * Configure the benchmark - */ -module.exports.getConfiguration = function (benchDb) { - const program = require('commander') - - program - .option('-n --number [number]', 'Size of the collection to test on', parseInt) - .option('-i --with-index', 'Use an index') - .option('-m --in-memory', 'Test with an in-memory only store') - .parse(process.argv) - - const n = program.number || 10000 - - console.log('----------------------------') - console.log('Test with ' + n + ' documents') - console.log(program.withIndex ? 'Use an index' : 'Don\'t use an index') - console.log(program.inMemory ? 'Use an in-memory datastore' : 'Use a persistent datastore') - console.log('----------------------------') - - const d = new Datastore({ - filename: benchDb, - inMemoryOnly: program.inMemory - }) - - return { n, d, program } -} - -/** - * Ensure the workspace stat and the db datafile is empty - */ -module.exports.prepareDb = function (filename, cb) { - callbackify((filename) => Persistence.ensureParentDirectoryExistsAsync(filename))(filename, function () { - fs.access(filename, fs.constants.FS_OK, function (err) { - if (!err) { - fs.unlink(filename, cb) - } else { return cb() } - }) - }) -} - -/** - * Return an array with the numbers from 0 to n-1, in a random order - * Uses Fisher Yates algorithm - * Useful to get fair tests - */ -function getRandomArray (n) { - const res = [] - let i - let j - let temp - - for (i = 0; i < n; i += 1) { res[i] = i } - - for (i = n - 1; i >= 1; i -= 1) { - j = Math.floor((i + 1) * Math.random()) - temp = res[i] - res[i] = res[j] - res[j] = temp - } - - return res -} -module.exports.getRandomArray = getRandomArray - -/** - * Insert a certain number of documents for testing - */ -module.exports.insertDocs = function (d, n, profiler, cb) { - const order = getRandomArray(n) - - profiler.step('Begin inserting ' + n + ' docs') - - function runFrom (i) { - if (i === n) { // Finished - const opsPerSecond = Math.floor(1000 * n / profiler.elapsedSinceLastStep()) - console.log('===== RESULT (insert) ===== ' + opsPerSecond + ' ops/s') - profiler.step('Finished inserting ' + n + ' docs') - profiler.insertOpsPerSecond = opsPerSecond - return cb() - } - - // eslint-disable-next-line n/handle-callback-err - d.insert({ docNumber: order[i] }, function (err) { - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} - -/** - * Find documents with find - */ -module.exports.findDocs = function (d, n, profiler, cb) { - const order = getRandomArray(n) - - profiler.step('Finding ' + n + ' documents') - - function runFrom (i) { - if (i === n) { // Finished - console.log('===== RESULT (find) ===== ' + Math.floor(1000 * n / profiler.elapsedSinceLastStep()) + ' ops/s') - profiler.step('Finished finding ' + n + ' docs') - return cb() - } - - // eslint-disable-next-line n/handle-callback-err - d.find({ docNumber: order[i] }, function (err, docs) { - if (docs.length !== 1 || docs[0].docNumber !== order[i]) { return cb(new Error('One find didnt work')) } - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} - -/** - * Find documents with find and the $in operator - */ -module.exports.findDocsWithIn = function (d, n, profiler, cb) { - const ins = [] - const arraySize = Math.min(10, n) - - // Preparing all the $in arrays, will take some time - for (let i = 0; i < n; i += 1) { - ins[i] = [] - - for (let j = 0; j < arraySize; j += 1) { - ins[i].push((i + j) % n) - } - } - - profiler.step('Finding ' + n + ' documents WITH $IN OPERATOR') - - function runFrom (i) { - if (i === n) { // Finished - console.log('===== RESULT (find with in selector) ===== ' + Math.floor(1000 * n / profiler.elapsedSinceLastStep()) + ' ops/s') - profiler.step('Finished finding ' + n + ' docs') - return cb() - } - - // eslint-disable-next-line n/handle-callback-err - d.find({ docNumber: { $in: ins[i] } }, function (err, docs) { - if (docs.length !== arraySize) { return cb(new Error('One find didnt work')) } - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} - -/** - * Find documents with findOne - */ -module.exports.findOneDocs = function (d, n, profiler, cb) { - const order = getRandomArray(n) - - profiler.step('FindingOne ' + n + ' documents') - - function runFrom (i) { - if (i === n) { // Finished - console.log('===== RESULT (findOne) ===== ' + Math.floor(1000 * n / profiler.elapsedSinceLastStep()) + ' ops/s') - profiler.step('Finished finding ' + n + ' docs') - return cb() - } - - // eslint-disable-next-line n/handle-callback-err - d.findOne({ docNumber: order[i] }, function (err, doc) { - if (!doc || doc.docNumber !== order[i]) { return cb(new Error('One find didnt work')) } - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} - -/** - * Update documents - * options is the same as the options object for update - */ -module.exports.updateDocs = function (options, d, n, profiler, cb) { - const order = getRandomArray(n) - - profiler.step('Updating ' + n + ' documents') - - function runFrom (i) { - if (i === n) { // Finished - console.log('===== RESULT (update) ===== ' + Math.floor(1000 * n / profiler.elapsedSinceLastStep()) + ' ops/s') - profiler.step('Finished updating ' + n + ' docs') - return cb() - } - - // Will not actually modify the document but will take the same time - d.update({ docNumber: order[i] }, { docNumber: order[i] }, options, function (err, nr) { - if (err) { return cb(err) } - if (nr !== 1) { return cb(new Error('One update didnt work')) } - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} - -/** - * Remove documents - * options is the same as the options object for update - */ -module.exports.removeDocs = function (options, d, n, profiler, cb) { - const order = getRandomArray(n) - - profiler.step('Removing ' + n + ' documents') - - function runFrom (i) { - if (i === n) { // Finished - // opsPerSecond corresponds to 1 insert + 1 remove, needed to keep collection size at 10,000 - // We need to subtract the time taken by one insert to get the time actually taken by one remove - const opsPerSecond = Math.floor(1000 * n / profiler.elapsedSinceLastStep()) - const removeOpsPerSecond = Math.floor(1 / ((1 / opsPerSecond) - (1 / profiler.insertOpsPerSecond))) - console.log('===== RESULT (remove) ===== ' + removeOpsPerSecond + ' ops/s') - profiler.step('Finished removing ' + n + ' docs') - return cb() - } - - d.remove({ docNumber: order[i] }, options, function (err, nr) { - if (err) { return cb(err) } - if (nr !== 1) { return cb(new Error('One remove didnt work')) } - // eslint-disable-next-line n/handle-callback-err - d.insert({ docNumber: order[i] }, function (err) { // We need to reinsert the doc so that we keep the collection's size at n - // So actually we're calculating the average time taken by one insert + one remove - executeAsap(function () { - runFrom(i + 1) - }) - }) - }) - } - - runFrom(0) -} - -/** - * Load database - */ -module.exports.loadDatabase = function (d, n, profiler, cb) { - profiler.step('Loading the database ' + n + ' times') - - function runFrom (i) { - if (i === n) { // Finished - console.log('===== RESULT ===== ' + Math.floor(1000 * n / profiler.elapsedSinceLastStep()) + ' ops/s') - profiler.step('Finished loading a database' + n + ' times') - return cb() - } - - // eslint-disable-next-line n/handle-callback-err - d.loadDatabase(function (err) { - executeAsap(function () { - runFrom(i + 1) - }) - }) - } - - runFrom(0) -} diff --git a/benchmarks/ensureIndex.js b/benchmarks/ensureIndex.js deleted file mode 100755 index cf33c16..0000000 --- a/benchmarks/ensureIndex.js +++ /dev/null @@ -1,50 +0,0 @@ -const program = require('commander') -const { apply, waterfall } = require('../test/utils.test.js') -const Datastore = require('../lib/datastore') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const profiler = new Profiler('INSERT BENCH') -const benchDb = 'workspace/insert.bench.db' -const d = new Datastore(benchDb) - -program - .option('-n --number [number]', 'Size of the collection to test on', parseInt) - .option('-i --with-index', 'Test with an index') - .parse(process.argv) - -const n = program.number || 10000 - -console.log('----------------------------') -console.log('Test with ' + n + ' documents') -console.log('----------------------------') - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - function (cb) { - let i - - profiler.step('Begin calling ensureIndex ' + n + ' times') - - for (i = 0; i < n; i += 1) { - d.ensureIndex({ fieldName: 'docNumber' }) - delete d.indexes.docNumber - } - - console.log('Average time for one ensureIndex: ' + (profiler.elapsedSinceLastStep() / n) + 'ms') - profiler.step('Finished calling ensureIndex ' + n + ' times') - cb() - } -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/find.js b/benchmarks/find.js deleted file mode 100755 index d3c9a83..0000000 --- a/benchmarks/find.js +++ /dev/null @@ -1,27 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const profiler = new Profiler('FIND BENCH') -const benchDb = 'workspace/find.bench.db' -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -const n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { d.ensureIndex({ fieldName: 'docNumber' }) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - apply(commonUtilities.findDocs, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/findOne.js b/benchmarks/findOne.js deleted file mode 100755 index b965709..0000000 --- a/benchmarks/findOne.js +++ /dev/null @@ -1,28 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/findOne.bench.db' -const profiler = new Profiler('FINDONE BENCH') -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -const n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { d.ensureIndex({ fieldName: 'docNumber' }) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - function (cb) { setTimeout(function () { cb() }, 500) }, - apply(commonUtilities.findOneDocs, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/findWithIn.js b/benchmarks/findWithIn.js deleted file mode 100755 index 8df0c41..0000000 --- a/benchmarks/findWithIn.js +++ /dev/null @@ -1,27 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/find.bench.db' -const profiler = new Profiler('FIND BENCH') -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -const n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { d.ensureIndex({ fieldName: 'docNumber' }) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - apply(commonUtilities.findDocsWithIn, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/insert.js b/benchmarks/insert.js deleted file mode 100755 index 4938212..0000000 --- a/benchmarks/insert.js +++ /dev/null @@ -1,32 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/insert.bench.db' -const profiler = new Profiler('INSERT BENCH') -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -let n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { - d.ensureIndex({ fieldName: 'docNumber' }) - n = 2 * n // We will actually insert twice as many documents - // because the index is slower when the collection is already - // big. So the result given by the algorithm will be a bit worse than - // actual performance - } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/loadDatabase.js b/benchmarks/loadDatabase.js deleted file mode 100755 index b007dfb..0000000 --- a/benchmarks/loadDatabase.js +++ /dev/null @@ -1,35 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const program = require('commander') -const Datastore = require('../lib/datastore') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/loaddb.bench.db' -const profiler = new Profiler('LOADDB BENCH') -const d = new Datastore(benchDb) - -program - .option('-n --number [number]', 'Size of the collection to test on', parseInt) - .option('-i --with-index', 'Test with an index') - .parse(process.argv) - -const n = program.number || 10000 - -console.log('----------------------------') -console.log('Test with ' + n + ' documents') -console.log(program.withIndex ? 'Use an index' : "Don't use an index") -console.log('----------------------------') - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(cb) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - apply(commonUtilities.loadDatabase, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/profiler.js b/benchmarks/profiler.js deleted file mode 100644 index a5d73af..0000000 --- a/benchmarks/profiler.js +++ /dev/null @@ -1,92 +0,0 @@ -const util = require('util') - -function formatTime (time, precision) { - // If we're dealing with ms, round up to seconds when time is at least 1 second - if (time > 1000 && precision === 'ms') { - return (Math.floor(time / 100) / 10) + ' s' - } else { - return time.toFixed(3) + ' ' + precision - } -} - -// get time in ns -function getTime () { - const t = process.hrtime() - return (t[0] * 1e9 + t[1]) -} - -/** - * Create a profiler with name testName to monitor the execution time of a route - * The profiler has two arguments: a step msg and an optional reset for the internal timer - * It will display the execution time per step and total from latest rest - * - * Optional logToConsole flag, which defaults to true, causes steps to be printed to console. - * otherwise, they can be accessed from Profiler.steps array. - */ -function Profiler (name, logToConsole, precision) { - this.name = name - this.steps = [] - this.sinceBeginning = null - this.lastStep = null - this.logToConsole = typeof (logToConsole) === 'undefined' ? true : logToConsole - this.precision = typeof (precision) === 'undefined' ? 'ms' : precision - this.divisor = 1 - - if (this.precision === 'ms') this.divisor = 1e6 -} - -Profiler.prototype.beginProfiling = function () { - if (this.logToConsole) { console.log(this.name + ' - Begin profiling') } - this.resetTimers() -} - -Profiler.prototype.resetTimers = function () { - this.sinceBeginning = getTime() - this.lastStep = getTime() - this.steps.push(['BEGIN_TIMER', this.lastStep]) -} - -Profiler.prototype.elapsedSinceBeginning = function () { - return (getTime() - this.sinceBeginning) / this.divisor -} - -Profiler.prototype.elapsedSinceLastStep = function () { - return (getTime() - this.lastStep) / this.divisor -} - -// Return the deltas between steps, in nanoseconds - -Profiler.prototype.getSteps = function () { - const divisor = this.divisor - - return this.steps.map(function (curr, index, arr) { - if (index === 0) return undefined - const delta = (curr[1] - arr[index - 1][1]) - return [curr[0], (delta / divisor)] - }).slice(1) -} - -Profiler.prototype.step = function (msg) { - if (!this.sinceBeginning || !this.lastStep) { - console.log(util.format( - '%s - %s - You must call beginProfiling before registering steps', - this.name, - msg - )) - return - } - - if (this.logToConsole) { - console.log(util.format('%s - %s - %s (total: %s)', - this.name, - msg, - formatTime(this.elapsedSinceLastStep(), this.precision), - formatTime(this.elapsedSinceBeginning(), this.precision) - )) - } - - this.lastStep = getTime() - this.steps.push([msg, this.lastStep]) -} - -module.exports = Profiler diff --git a/benchmarks/remove.js b/benchmarks/remove.js deleted file mode 100755 index e3607f2..0000000 --- a/benchmarks/remove.js +++ /dev/null @@ -1,35 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/remove.bench.db' -const profiler = new Profiler('REMOVE BENCH') -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -const n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { d.ensureIndex({ fieldName: 'docNumber' }) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - - // Test with remove only one document - function (cb) { profiler.step('MULTI: FALSE'); return cb() }, - apply(commonUtilities.removeDocs, { multi: false }, d, n, profiler), - // Test with multiple documents - function (cb) { d.remove({}, { multi: true }, function () { return cb() }) }, - apply(commonUtilities.insertDocs, d, n, profiler), - function (cb) { profiler.step('MULTI: TRUE'); return cb() }, - apply(commonUtilities.removeDocs, { multi: true }, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/benchmarks/update.js b/benchmarks/update.js deleted file mode 100755 index 3fce526..0000000 --- a/benchmarks/update.js +++ /dev/null @@ -1,37 +0,0 @@ -const { apply, waterfall } = require('../test/utils.test.js') -const commonUtilities = require('./commonUtilities') -const Profiler = require('./profiler') - -const benchDb = 'workspace/update.bench.db' -const profiler = new Profiler('UPDATE BENCH') -const config = commonUtilities.getConfiguration(benchDb) -const d = config.d -const n = config.n - -waterfall([ - apply(commonUtilities.prepareDb, benchDb), - function (cb) { - d.loadDatabase(function (err) { - if (err) { return cb(err) } - if (config.program.withIndex) { d.ensureIndex({ fieldName: 'docNumber' }) } - cb() - }) - }, - function (cb) { profiler.beginProfiling(); return cb() }, - apply(commonUtilities.insertDocs, d, n, profiler), - - // Test with update only one document - function (cb) { profiler.step('MULTI: FALSE'); return cb() }, - apply(commonUtilities.updateDocs, { multi: false }, d, n, profiler), - - // Test with multiple documents - // eslint-disable-next-line n/handle-callback-err - function (cb) { d.remove({}, { multi: true }, function (err) { return cb() }) }, - apply(commonUtilities.insertDocs, d, n, profiler), - function (cb) { profiler.step('MULTI: TRUE'); return cb() }, - apply(commonUtilities.updateDocs, { multi: true }, d, n, profiler) -], function (err) { - profiler.step('Benchmark finished') - - if (err) { return console.log('An error was encountered: ', err) } -}) diff --git a/test/benchmark.test.js b/test/benchmark.test.js new file mode 100644 index 0000000..14fd963 --- /dev/null +++ b/test/benchmark.test.js @@ -0,0 +1,292 @@ +import { promisify } from 'util' +import { performance } from 'node:perf_hooks' +import Persistence from '../src/persistence.js' +import { access, unlink, constants } from 'fs/promises' +import Datastore from '../src/datastore.js' + +const getRandomArray = n => { + const res = [] + let i + let j + let temp + + for (i = 0; i < n; i += 1) { res[i] = i } + + for (i = n - 1; i >= 1; i -= 1) { + j = Math.floor((i + 1) * Math.random()) + temp = res[i] + res[i] = res[j] + res[j] = temp + } + + return res +} + +const insertDocs = async (d, n) => { + const order = getRandomArray(n) + + for (let i = 0; i <= n; i++) { + await d.insertAsync({ docNumber: order[i] }) + await promisify(process.nextTick)() + } +} + +const measure = async (name, n, cb) => { + performance.mark(`${name} start`) + + for (let i = 0; i < n; i += 1) { + await cb(i) + } + + performance.mark(`${name} end`) + + const { duration } = performance.measure(`${name} average duration for ${n} operations`, `${name} start`, `${name} end`) + const opsPerSecond = Math.floor(1000 * n / duration) + + console.log(`===== RESULT (${name}) ===== ${opsPerSecond} ops/s ===== ${Math.floor(duration / n * 1000) / 1000}ms/execution`) +} + +describe('Performance tests without index', function () { + const n = 1000 + const benchDb = 'workspace/bench.db' + let d + + before(function () { + if (!process.argv.includes('--benchmark')) this.skip() + else this.timeout(60000) + }) + + beforeEach('Prepare database', async () => { + try { + await access(benchDb, constants.F_OK) + await unlink(benchDb) + } catch {} + await Persistence.ensureParentDirectoryExistsAsync(benchDb) + d = new Datastore({ filename: benchDb }) + await d.loadDatabaseAsync() + }) + + afterEach('Clean database', async () => { + try { + await d.this.executor.queue.guardian + d = null + await access(benchDb, constants.F_OK) + await unlink(benchDb) + } catch {} + }) + + it('ensureIndex', async () => { + await insertDocs(d, n) + await measure('ensureIndex', n, async (i) => { + d.ensureIndexAsync({ fieldName: 'docNumber' }) + delete d.indexes.docNumber + }) + }) + + it('find', async () => { + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('find', n, async (i) => { + const docs = await d.findAsync({ docNumber: order[i] }) + if (docs.length !== 1 || docs[0].docNumber !== order[i]) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('findOne', async () => { + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('findOne', n, async (i) => { + const doc = await d.findOneAsync({ docNumber: order[i] }) + if (doc == null || doc.docNumber !== order[i]) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('find with $in', async () => { + await insertDocs(d, n) + + const ins = [] + const arraySize = Math.min(10, n) + + // Preparing all the $in arrays, will take some time + for (let i = 0; i < n; i += 1) { + ins[i] = [] + + for (let j = 0; j < arraySize; j += 1) { + ins[i].push((i + j) % n) + } + } + + await measure('find with $in', n, async (i) => { + const docs = await d.findAsync({ docNumber: { $in: ins[i] } }) + if (docs.length !== arraySize) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('insert docs', async () => { + const order = getRandomArray(n) + await measure('insert docs', n, async (i) => { + await d.insertAsync({ docNumber: order[i] }) + await promisify(process.nextTick)() + }) + }) + + it('load database', async () => { + await insertDocs(d, n) + await measure('load database', n, async (i) => { + await d.loadDatabaseAsync() + }) + }) + + it('Insert and remove one', async () => { + // TODO measure only remove + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one', n, async (i) => { + const nr = await d.removeAsync({ docNumber: order[i] }, { multi: false }) + if (nr !== 1) throw new Error('One remove didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Insert and remove one with multi option', async () => { + // TODO measure only remove + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option', n, async (i) => { + const nr = await d.removeAsync({ docNumber: order[i] }, { multi: true }) + if (nr !== 1) throw new Error('One remove didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Update', async () => { + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option', n, async (i) => { + const { numAffected } = await d.updateAsync({ docNumber: order[i] }, { docNumber: order[i] }, { multi: false }) + if (numAffected !== 1) throw new Error('One update didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Update with multi option', async () => { + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option', n, async (i) => { + const { numAffected } = await d.updateAsync({ docNumber: order[i] }, { docNumber: order[i] }, { multi: true }) + if (numAffected !== 1) throw new Error('One update didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('find with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('find with index', n, async (i) => { + const docs = await d.findAsync({ docNumber: order[i] }) + if (docs.length !== 1 || docs[0].docNumber !== order[i]) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('findOne with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('findOne with index', n, async (i) => { + const doc = await d.findOneAsync({ docNumber: order[i] }) + if (doc == null || doc.docNumber !== order[i]) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('find with $in with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + + const ins = [] + const arraySize = Math.min(10, n) + + // Preparing all the $in arrays, will take some time + for (let i = 0; i < n; i += 1) { + ins[i] = [] + + for (let j = 0; j < arraySize; j += 1) { + ins[i].push((i + j) % n) + } + } + + await measure('find with $in with index', n, async (i) => { + const docs = await d.findAsync({ docNumber: { $in: ins[i] } }) + if (docs.length !== arraySize) throw new Error('One find didnt work') + await promisify(process.nextTick)() + }) + }) + + it('insert docs with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + const order = getRandomArray(n) + await measure('insert docs with index', n, async (i) => { + await d.insertAsync({ docNumber: order[i] }) + await promisify(process.nextTick)() + }) + }) + + it('load database with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + await measure('load database with index', n, async (i) => { + await d.loadDatabaseAsync() + }) + }) + + it('Insert and remove one with index', async () => { + // TODO measure only remove + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with index', n, async (i) => { + const nr = await d.removeAsync({ docNumber: order[i] }, { multi: false }) + if (nr !== 1) throw new Error('One remove didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Insert and remove one with multi option with index', async () => { + // TODO measure only remove + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option with index', n, async (i) => { + const nr = await d.removeAsync({ docNumber: order[i] }, { multi: true }) + if (nr !== 1) throw new Error('One remove didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Update with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option with index', n, async (i) => { + const { numAffected } = await d.updateAsync({ docNumber: order[i] }, { docNumber: order[i] }, { multi: false }) + if (numAffected !== 1) throw new Error('One update didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) + + it('Update with multi option with index', async () => { + await d.ensureIndexAsync({ fieldName: 'docNumber' }) + await insertDocs(d, n) + const order = getRandomArray(n) + await measure('insert and remove one with multi option with index', n, async (i) => { + const { numAffected } = await d.updateAsync({ docNumber: order[i] }, { docNumber: order[i] }, { multi: true }) + if (numAffected !== 1) throw new Error('One update didnt work') + await d.insertAsync({ docNumber: order[i] }) + }) + }) +})