Skip to content

Commit 24b5031

Browse files
committed
Support ioredis and sqlite3
1 parent e08ad4d commit 24b5031

8 files changed

Lines changed: 161 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ Yields:
3030
- Derives plot labels from benchmark metadata (package, platform, ..)
3131
- Uses unique temporary directories for every db
3232
- Can optionally wrap the db in `encoding-down` and/or `levelup`
33-
- Also takes `level` or something else that's already a `levelup` interface.
33+
- Also takes `level` or something else that's already a `levelup` interface
34+
- Also takes `ioredis` and `sqlite3` (see [`third-party/`](./third-party)).
3435

3536
## Usage
3637

lib/run.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@ module.exports = function (benchmark, targetId, options) {
2222

2323
// Load target module from working directory
2424
const cwd = process.cwd()
25-
const target = from(cwd, targetId)
2625
const targetPkg = resolve(targetId + '/package.json', { basedir: cwd })
2726
const targetDir = path.dirname(targetPkg)
2827
const { name, version } = require(targetPkg)
2928

29+
let target = from(cwd, targetId)
30+
31+
if (targetId === 'sqlite3' || name === 'sqlite3') {
32+
target = require('../third-party/sqlite3')(target)
33+
} else if (targetId === 'ioredis' || name === 'ioredis') {
34+
target = require('../third-party/ioredis')(target)
35+
}
36+
3037
if (typeof target !== 'function') {
3138
throw new Error('Target must export a function')
3239
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"devDependencies": {
3131
"dependency-check": "^3.3.0",
3232
"hallmark": "^0.1.0",
33+
"haredis-tmp": "github:vweevers/haredis-tmp#feat/windows-support",
3334
"level-community": "^3.0.0",
3435
"nyc": "^14.1.1",
3536
"standard": "^12.0.1",

third-party/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Third party
2+
3+
This directory contains thin wrappers (that intentionally _don't_ implement the full `abstract-leveldown` interface) around third-party databases and libraries like [`ioredis`](https://github.com/luin/ioredis) and [`sqlite3`](https://github.com/mapbox/node-sqlite3), to be able to run `level-bench` on them for comparison.
4+
5+
## Supported benchmarks (so far)
6+
7+
The `write` benchmark on `ioredis` (`redis-server` must be available in `PATH`):
8+
9+
```
10+
npm i ioredis leveldown
11+
level-bench run write ioredis
12+
level-bench run write leveldown
13+
level-bench plot write
14+
```
15+
16+
![write.ioredis-vs-leveldown](img/write.ioredis-vs-leveldown.png)
17+
18+
The `write` benchmark on `sqlite3` (100-1000x slower than `leveldown`, lower the amount of operations with `-n`):
19+
20+
```
21+
npm i sqlite3 leveldown
22+
level-bench run write sqlite3 -b [-n 2e4]
23+
level-bench run write leveldown -b [-n 2e4]
24+
level-bench plot write
25+
```
26+
27+
![write.sqlite3-vs-leveldown](img/write.sqlite3-vs-leveldown.png)
74.2 KB
Loading
43.7 KB
Loading

third-party/ioredis.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict'
2+
3+
// Note: redis-server must be available in PATH
4+
const tmpRedis = require('haredis-tmp')
5+
6+
module.exports = function inject (ioredis) {
7+
return function (location, options, callback) {
8+
let redis
9+
let shutdown
10+
11+
// Expire keys (which is cheap) to avoid blowing up memory
12+
const ttlSeconds = options.ttl || 10
13+
const port = options.port || 6389
14+
15+
// "For better performance. Recommended to be enabled when handling large
16+
// array response and you don't need the buffer support."
17+
const dropBufferSupport = !!options.dropBufferSupport
18+
19+
const wrapper = {
20+
status: 'new',
21+
22+
open: function (options, callback) {
23+
wrapper.status = 'opening'
24+
25+
tmpRedis([port], { verbose: true }, function (err, path, shutdown_) {
26+
if (err) return callback(err)
27+
28+
shutdown = shutdown_
29+
redis = new ioredis({
30+
port,
31+
retryStrategy: () => false,
32+
maxRetriesPerRequest: 0,
33+
autoResubscribe: false,
34+
autoResendUnfulfilledCommands: false,
35+
enableOfflineQueue: false,
36+
dropBufferSupport
37+
})
38+
39+
redis.once('ready', function () {
40+
wrapper.status = 'open'
41+
callback(null, wrapper)
42+
})
43+
44+
// ioredis swallows errors if there's no listener
45+
redis.on('error', function (err) {
46+
throw err
47+
})
48+
})
49+
},
50+
51+
put: function (key, value/*, options */, callback) {
52+
redis.set(key, value, 'EX', ttlSeconds, callback)
53+
},
54+
55+
close: function (callback) {
56+
redis.disconnect()
57+
shutdown(callback)
58+
},
59+
60+
// Trick reachdown
61+
_iterator: function () {}
62+
}
63+
64+
return wrapper
65+
}
66+
}

third-party/sqlite3.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use strict'
2+
3+
const path = require('path')
4+
5+
module.exports = function inject (sqlite3) {
6+
return function (location, options, callback) {
7+
let db
8+
let firstPut = true
9+
10+
const wrapper = {
11+
// So that benchmarks can determine the size of the database.
12+
location,
13+
status: 'new',
14+
15+
open: function (options, callback) {
16+
wrapper.status = 'opening'
17+
18+
db = new sqlite3.Database(path.join(location, 'sqlite.db'), function (err) {
19+
if (err) return callback(err)
20+
21+
db.run('CREATE TABLE bench (key VARCHAR(16), value VARCHAR(200))', function (err) {
22+
if (err) return callback(err)
23+
24+
setImmediate(function () {
25+
wrapper.status = 'open'
26+
callback(null, wrapper)
27+
})
28+
})
29+
})
30+
},
31+
32+
put: function (key, value/*, options */, callback) {
33+
if (firstPut) {
34+
firstPut = false
35+
36+
// TODO: expose this fact to benchmarks
37+
if (typeof key !== 'string') throw new TypeError('Key type not supported')
38+
if (typeof value !== 'string') throw new TypeError('Value type not supported')
39+
40+
if (key.length > 16) throw new RangeError('Key is too long')
41+
if (value.length > 200) throw new RangeError('Value is too long')
42+
}
43+
44+
db.exec(`INSERT INTO bench VALUES("${key}","${value}")`, callback)
45+
},
46+
47+
close: function (callback) {
48+
db.close(callback)
49+
},
50+
51+
// Trick reachdown
52+
_iterator: function () {}
53+
}
54+
55+
return wrapper
56+
}
57+
}

0 commit comments

Comments
 (0)