diff --git a/.travis.yml b/.travis.yml index 39dbb1f..2a8cd7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,8 @@ env: matrix: include: - - env: TO_TEST=NodeJS + - env: TO_TEST=leveldb + - env: TO_TEST=lmdb - env: TO_TEST=ESLint # - os: osx # env: TO_TEST=Karma/Travis_CI/Safari @@ -92,6 +93,7 @@ script: - if [[ "$TO_TEST" == Karma/Travis_CI/Firefox_* ]] && [ "$KARMA_BROWSERS" = "" ]; then export KARMA_BROWSERS=Firefox TO_TEST=Karma USE_ISTANBUL=1; fi - if [[ "$TO_TEST" == Karma/Travis_CI/* ]] && [ "$KARMA_BROWSERS" = "" ]; then export KARMA_BROWSERS=Safari TO_TEST=Karma; fi - if [ "$TO_TEST" = "Karma" ]; then node_modules/.bin/karma start; fi -- if [ "$TO_TEST" = "NodeJS" ]; then node_modules/.bin/jasmine; fi +- if [ "$TO_TEST" = "leveldb" ]; then node_modules/.bin/jasmine; fi +- if [ "$TO_TEST" = "lmdb" ]; then node_modules/.bin/jasmine --config=spec/support/lmdb-jasmine.json; fi - if [ "$TO_TEST" = "ESLint" ]; then node_modules/.bin/eslint src/main src/test *.js && node_modules/.bin/esdoc; fi - if [ "$USE_ISTANBUL" = "1" ]; then node_modules/.bin/codecov; fi diff --git a/benchmark/browser/BenchmarkUi.js b/benchmark/indexeddb/BenchmarkUi.js similarity index 100% rename from benchmark/browser/BenchmarkUi.js rename to benchmark/indexeddb/BenchmarkUi.js diff --git a/benchmark/browser/BenchmarkView.js b/benchmark/indexeddb/BenchmarkView.js similarity index 100% rename from benchmark/browser/BenchmarkView.js rename to benchmark/indexeddb/BenchmarkView.js diff --git a/benchmark/browser/SelectorBar.js b/benchmark/indexeddb/SelectorBar.js similarity index 100% rename from benchmark/browser/SelectorBar.js rename to benchmark/indexeddb/SelectorBar.js diff --git a/benchmark/browser/UiComponent.js b/benchmark/indexeddb/UiComponent.js similarity index 100% rename from benchmark/browser/UiComponent.js rename to benchmark/indexeddb/UiComponent.js diff --git a/benchmark/browser/index.html b/benchmark/indexeddb/index.html similarity index 96% rename from benchmark/browser/index.html rename to benchmark/indexeddb/index.html index ba5b1c4..f4c54ef 100644 --- a/benchmark/browser/index.html +++ b/benchmark/indexeddb/index.html @@ -3,7 +3,7 @@ JungleDB Browser Benchmark - + diff --git a/benchmark/nodejs/index.js b/benchmark/leveldb/index.js similarity index 54% rename from benchmark/nodejs/index.js rename to benchmark/leveldb/index.js index f58d137..bbcb4b8 100644 --- a/benchmark/nodejs/index.js +++ b/benchmark/leveldb/index.js @@ -3,6 +3,7 @@ process.on('uncaughtException', function(err){ process.exit(); }); +const JDB = require('../../dist/leveldb.js'); const BenchmarkRunner = require('../shared/BenchmarkRunner.js'); -BenchmarkRunner.runBenchmarks().then(() => console.log('\nBenchmarks finished.')); +BenchmarkRunner.runBenchmarks(undefined, JDB).then(() => console.log('\nBenchmarks finished.')); diff --git a/benchmark/lmdb/index.js b/benchmark/lmdb/index.js new file mode 100644 index 0000000..7d50142 --- /dev/null +++ b/benchmark/lmdb/index.js @@ -0,0 +1,9 @@ +process.on('uncaughtException', function(err){ + console.error(err.stack); + process.exit(); +}); + +const JDB = require('../../dist/lmdb.js'); +const BenchmarkRunner = require('../shared/BenchmarkRunner.js'); + +BenchmarkRunner.runBenchmarks(undefined, JDB, { maxDbSize: 1024*1024*1024 }).then(() => console.log('\nBenchmarks finished.')); diff --git a/benchmark/shared/Benchmark.js b/benchmark/shared/Benchmark.js index a38e2e6..568f1b6 100644 --- a/benchmark/shared/Benchmark.js +++ b/benchmark/shared/Benchmark.js @@ -1,5 +1,4 @@ if (typeof(require) !== 'undefined') { - JDB = require('../../dist/node.js'); Stats = require('./Stats.js'); BenchmarkUtils = require('./BenchmarkUtils.js'); } @@ -10,19 +9,37 @@ class Benchmark { * @param {number} benchmarkVersion * @param {Array.} tableNames */ - constructor(name, benchmarkVersion = 1, tableNames = ['default']) { + constructor(name, benchmarkVersion = 1, tableNames = ['default'], indices = {}) { this.name = name; this._databaseName = `db-${name}`; this._tableNames = tableNames; this._objectStores = []; + this._indices = indices; this._benchmarkVersion = benchmarkVersion; this._db = null; + + this._jdbOptions = {}; + this._jdb = null; + } + + setJDB(customJDB, options) { + this._jdb = customJDB; + this._jdbOptions = options || {}; + } + + get jdb() { + return this._jdb ? this._jdb : JDB; } async _init() { - this._db = new JDB.JungleDB(this._databaseName, this._benchmarkVersion); + this._db = new this.jdb.JungleDB(this._databaseName, this._benchmarkVersion, undefined, this._jdbOptions); for (const tableName of this._tableNames) { - this._objectStores.push(this._db.createObjectStore(tableName)); + const store = this._db.createObjectStore(tableName); + this._objectStores.push(store); + if (this._indices[tableName]) { + const { name, keyPath } = this._indices[tableName]; + store.createIndex(name, keyPath); + } } await this._db.connect(); } @@ -56,7 +73,6 @@ class Benchmark { * @returns {Promise.} */ async run(count = 2) { - BenchmarkUtils.logColored(`\n\nRun benchmark ${this.description || this.name}:`); await this._init(); const stats = []; for (let i=0; idatabaseEntryCount || batchSize>databaseEntryCount + || (batchSize===1 && !sync)) { // we ignore this setting as it is the same as batchSize 1 with sync + throw('Illegal parameter combination.'); + } + const indices = {}; + indices[BenchmarkReadIndex.TABLE_NAME] = { name: 'index', keyPath: 'index' }; + super(BenchmarkReadIndex.NAME, BenchmarkReadIndex.VERSION, [BenchmarkReadIndex.TABLE_NAME], indices); + this._databaseEntryCount = databaseEntryCount; + this._readCount = readCount; + this._entrySize = entrySize; + this._batchSize = batchSize; + this._sync = sync; + this._entryKeys = BenchmarkUtils.createRandomKeys(databaseEntryCount); + this._indexKeys = BenchmarkUtils.createRandomKeys(databaseEntryCount); + this.description = `${BenchmarkReadIndex.NAME},${databaseEntryCount} entries,` + + `read ${readCount} entries,${entrySize} B/entry,${batchSize} ops/tx,` + + `${this._sync? '' : 'a'}sync`; + } + + + async _init() { + await super._init(); + // fill up the database + const objectStore = this._db.getObjectStore(BenchmarkReadIndex.TABLE_NAME); + await BenchmarkUtils.fillObjectStoreWithIndex(objectStore, this._databaseEntryCount, this._entrySize, 1000, false, + this._entryKeys, this._indexKeys); + } + + + async _benchmark(stats) { + const readKeys = []; + for (let i=0; i { + const key = readKeys[index]; + const i = transaction.index('index'); + return i.values(this.jdb.KeyRange.only(key)); + }; + const objectStore = this._db.getObjectStore(BenchmarkReadIndex.TABLE_NAME); + const result = await BenchmarkUtils.performBatchOperation(objectStore, this._readCount, this._batchSize, + this._sync, readOperation); + if (!result.results.every(entry => !!entry)) { + console.error('Error reading data from database.'); + } + stats.addReads(this._readCount, this._readCount * this._entrySize, result.totalTime); + } +} +BenchmarkReadIndex.NAME = 'benchmark-read-index'; +BenchmarkReadIndex.TABLE_NAME = 'default'; +BenchmarkReadIndex.VERSION = 1; + +if (typeof(module) !== 'undefined') { + module.exports = BenchmarkReadIndex; +} diff --git a/benchmark/shared/BenchmarkRunner.js b/benchmark/shared/BenchmarkRunner.js index 8f1db10..c22643e 100644 --- a/benchmark/shared/BenchmarkRunner.js +++ b/benchmark/shared/BenchmarkRunner.js @@ -4,10 +4,11 @@ if (typeof(require) !== 'undefined') { BenchmarkRead = require('./BenchmarkRead.js'); BenchmarkDelete = require('./BenchmarkDelete.js'); BenchmarkReadFromMultiple = require('./BenchmarkReadFromMultiple.js'); + BenchmarkReadIndex = require('./BenchmarkReadIndex.js'); } class BenchmarkRunner { - static async runBenchmarks(benchmarkUi = null) { + static async runBenchmarks(benchmarkUi = null, customJDB = null, options = {}) { if (benchmarkUi) { for (const benchmarkDescription of BenchmarkRunner.BenchmarkDescriptions) { const type = benchmarkDescription.benchmark.NAME; @@ -25,9 +26,10 @@ class BenchmarkRunner { let result; try { const benchmark = new benchmarkDescription.benchmark(params); + benchmark.setJDB(customJDB, options); result = await benchmark.run(); // eslint-disable-line no-await-in-loop - } catch(e) { - console.log('\nSkipped invalid configuration', params, 'because of error', e); + } catch (e) { + // console.log('\nSkipped invalid configuration', params, 'because of error', e); result = null; } if (benchmarkUi) { @@ -103,7 +105,7 @@ BenchmarkRunner.BenchmarkDescriptions = [ entryCount: [100, 500, 1000, 10000], entrySize: [100, 1000, 100000], batchSize: [1, 100, 500], - sync: [true, false] + sync: [true] }, { benchmark: BenchmarkOverwrite, @@ -111,7 +113,7 @@ BenchmarkRunner.BenchmarkDescriptions = [ overwriteCount: [1000, 10000], entrySize: [10, 100, 1000], batchSize: [1, 10, 100, 1000], - sync: [true, false] + sync: [true] }, { benchmark: BenchmarkRead, @@ -119,7 +121,7 @@ BenchmarkRunner.BenchmarkDescriptions = [ readCount: [10, 100, 1000], entrySize: [100, 1000, 1000000], batchSize: [1, 10, 100], - sync: [true, false] + sync: [true] }, { benchmark: BenchmarkDelete, @@ -127,7 +129,7 @@ BenchmarkRunner.BenchmarkDescriptions = [ deleteCount: [10, 100, 1000], entrySize: [100, 1000], batchSize: [1, 10, 100], - sync: [true, false] + sync: [true] }, { benchmark: BenchmarkReadFromMultiple, @@ -136,7 +138,23 @@ BenchmarkRunner.BenchmarkDescriptions = [ totalEntrySize: [100, 1000], numberTables: [1, 2, 3], batchSize: [1, 10, 100], - sync: [true, false] + sync: [true] + }, + { + benchmark: BenchmarkReadIndex, + databaseEntryCount: [100, 500, 1000], + readCount: [10, 100, 1000], + entrySize: [100, 1000, 1000000], + batchSize: [1, 10, 100], + sync: [true] + }, + { + benchmark: BenchmarkReadIndex, + databaseEntryCount: [100000], + readCount: [10, 100, 1000], + entrySize: [100, 1000], + batchSize: [1, 10, 100], + sync: [true] } ]; diff --git a/benchmark/shared/BenchmarkUtils.js b/benchmark/shared/BenchmarkUtils.js index 70668b2..f67394a 100644 --- a/benchmark/shared/BenchmarkUtils.js +++ b/benchmark/shared/BenchmarkUtils.js @@ -32,6 +32,37 @@ class BenchmarkUtils { } + /** + * @param {ObjectStore} objectStore + * @param {number} entryCount + * @param {number} entrySize + * @param {number} [batchSize] + * @param {boolean} [sync] + * @param {Array.} [keys] + * @param {Stats} [stats] + * @returns {Promise.} + */ + static async fillObjectStoreWithIndex(objectStore, entryCount, entrySize, batchSize=1, sync=false, keys=null, indexKeys=null, stats=null) { + const randomData = []; + for (let i=0; i { + const key = keys ? keys[index] : String(index); + const data = randomData[index]; + await transaction.put(key, data); + }; + const totalTime = + (await BenchmarkUtils.performBatchOperation(objectStore, entryCount, batchSize, sync, putEntry)).totalTime; + if (stats) { + stats.addWrites(entryCount, entryCount*entrySize, totalTime); + } + } + + /** * @param {ObjectStore} objectStore * @param {number} totalCount @@ -163,7 +194,8 @@ class BenchmarkUtils { console.log(`%c${str}`, 'color: teal; font-weight: bold;'); } else { // node - console.log('\x1b[36m%s\x1b[0m', str); + // console.log('\x1b[36m%s\x1b[0m', str); + console.log(str); } } } diff --git a/benchmark/shared/Stats.js b/benchmark/shared/Stats.js index cebcfa8..c9925f9 100644 --- a/benchmark/shared/Stats.js +++ b/benchmark/shared/Stats.js @@ -196,6 +196,10 @@ class Stats { } } + totalTime() { + return `${this.writeTimePerRun + this.readTimePerRun},${Stats._getTimeString(this.writeTimePerRun + this.readTimePerRun)}`; + } + toString() { let result = ''; if (this.writes > 0) { diff --git a/clients/browser/index.html b/clients/browser/index.html index dfcdbde..6cf65ac 100644 --- a/clients/browser/index.html +++ b/clients/browser/index.html @@ -5,7 +5,7 @@ JungleDB - +