rocksdb-native 1.2.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,12 +1,9 @@
1
1
  /* global Bare */
2
2
  const ReadyResource = require('ready-resource')
3
- const b4a = require('b4a')
4
3
  const binding = require('./binding')
5
- const Batch = require('./lib/batch')
4
+ const { ReadBatch, WriteBatch } = require('./lib/batch')
6
5
  const Iterator = require('./lib/iterator')
7
6
 
8
- const empty = b4a.alloc(0)
9
-
10
7
  const RocksDB = module.exports = class RocksDB extends ReadyResource {
11
8
  constructor (path, {
12
9
  // default options, https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning
@@ -104,48 +101,24 @@ const RocksDB = module.exports = class RocksDB extends ReadyResource {
104
101
  }
105
102
  }
106
103
 
107
- async deleteRange (start, end, opts = {}) {
108
- if (typeof start === 'string') start = b4a.from(start)
109
- if (typeof end === 'string') end = b4a.from(end)
104
+ iterator (opts) {
105
+ return new Iterator(this, opts)
106
+ }
110
107
 
111
- if (start && !b4a.isBuffer(start)) {
112
- opts = start
113
- start = empty
114
- } else if (end && !b4a.isBuffer(end)) {
115
- opts = end
116
- end = empty
108
+ async peek (opts) {
109
+ for await (const value of this.iterator({ ...opts, limit: 1 })) { // eslint-disable-line no-unreachable-loop
110
+ return value
117
111
  }
118
112
 
119
- const {
120
- gt = empty,
121
- gte = start,
122
- lt = end,
123
- lte = empty
124
- } = opts
125
-
126
- const req = { resolve: null, reject: null, handle: null }
127
-
128
- const promise = new Promise((resolve, reject) => {
129
- req.resolve = resolve
130
- req.reject = reject
131
- })
132
-
133
- req.handle = binding.deleteRange(this._handle, gt, gte, lt, lte, req, onstatus)
134
-
135
- return promise
136
-
137
- function onstatus (err) {
138
- if (err) req.reject(new Error(err))
139
- else req.resolve()
140
- }
113
+ return null
141
114
  }
142
115
 
143
- batch (opts) {
144
- return new Batch(this, opts)
116
+ read (opts) {
117
+ return new ReadBatch(this, opts)
145
118
  }
146
119
 
147
- iterator (start, end, opts) {
148
- return new Iterator(this, start, end, opts)
120
+ write (opts) {
121
+ return new WriteBatch(this, opts)
149
122
  }
150
123
 
151
124
  static _instances = new Set()
package/lib/batch.js CHANGED
@@ -1,20 +1,26 @@
1
+ const c = require('compact-encoding')
1
2
  const b4a = require('b4a')
2
3
  const binding = require('../binding')
3
4
 
4
5
  const empty = b4a.alloc(0)
5
6
 
6
- module.exports = class RocksDBBatch {
7
+ class RocksDBBatch {
7
8
  constructor (db, opts = {}) {
8
9
  const {
9
- capacity = 8
10
+ capacity = 8,
11
+ encoding = null,
12
+ keyEncoding = encoding,
13
+ valueEncoding = encoding
10
14
  } = opts
11
15
 
12
16
  this._db = db
13
17
  this._capacity = capacity
14
- this._keys = []
15
- this._values = []
18
+ this._operations = []
16
19
  this._promises = []
17
20
 
21
+ this._keyEncoding = keyEncoding
22
+ this._valueEncoding = valueEncoding
23
+
18
24
  this._enqueuePromise = this._enqueuePromise.bind(this)
19
25
 
20
26
  this._request = null
@@ -31,8 +37,7 @@ module.exports = class RocksDBBatch {
31
37
  _onfinished () {
32
38
  const resolve = this._resolveRequest
33
39
 
34
- this._keys = []
35
- this._values = []
40
+ this._operations = []
36
41
  this._promises = []
37
42
  this._request = null
38
43
  this._resolveRequest = null
@@ -40,6 +45,85 @@ module.exports = class RocksDBBatch {
40
45
  if (resolve !== null) resolve()
41
46
  }
42
47
 
48
+ _resize () {
49
+ if (this._operations.length <= this._capacity) return false
50
+
51
+ while (this._operations.length > this._capacity) {
52
+ this._capacity *= 2
53
+ }
54
+
55
+ return true
56
+ }
57
+
58
+ async ready () {
59
+ if (this._handle !== null) return
60
+
61
+ if (this._db.opened === false) await this._db.ready()
62
+
63
+ this._init()
64
+ }
65
+
66
+ flush () {
67
+ if (this._request) throw new Error('Request already in progress')
68
+
69
+ this._request = new Promise((resolve) => { this._resolveRequest = resolve })
70
+ this._flush()
71
+
72
+ return this._request
73
+ }
74
+
75
+ tryFlush () {
76
+ if (this._request) throw new Error('Request already in progress')
77
+
78
+ this._request = true
79
+ this._flush()
80
+ }
81
+
82
+ async _flush () {
83
+ if (this._handle === null) await this.ready()
84
+ }
85
+
86
+ _enqueuePromise (resolve, reject) {
87
+ this._promises.push({ resolve, reject })
88
+ }
89
+
90
+ _encodeKey (k) {
91
+ if (this._keyEncoding) return c.encode(this._keyEncoding, k)
92
+ if (typeof k === 'string') return b4a.from(k)
93
+ return k
94
+ }
95
+
96
+ _encodeValue (v) {
97
+ if (this._valueEncoding) return c.encode(this._valueEncoding, v)
98
+ if (v === null) return empty
99
+ if (typeof v === 'string') return b4a.from(v)
100
+ return v
101
+ }
102
+
103
+ _decodeValue (b) {
104
+ if (this._valueEncoding) return c.decode(this._valueEncoding, b)
105
+ return b
106
+ }
107
+ }
108
+
109
+ exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
110
+ async _init () {
111
+ this._handle = binding.readInit(this)
112
+ this._buffer = binding.readBuffer(this._handle, this._capacity)
113
+ }
114
+
115
+ _resize () {
116
+ if (super._resize() && this._handle !== null) {
117
+ this._buffer = binding.readBuffer(this._handle, this._capacity)
118
+ }
119
+ }
120
+
121
+ async _flush () {
122
+ await super._flush()
123
+
124
+ binding.read(this._db._handle, this._handle, this._operations, this._onread)
125
+ }
126
+
43
127
  _onread (errs, values) {
44
128
  for (let i = 0, n = this._promises.length; i < n; i++) {
45
129
  const promise = this._promises[i]
@@ -48,123 +132,158 @@ module.exports = class RocksDBBatch {
48
132
  const err = errs[i]
49
133
 
50
134
  if (err) promise.reject(new Error(err))
51
- else promise.resolve(values[i].byteLength === 0 ? null : b4a.from(values[i]))
135
+ else promise.resolve(values[i].byteLength === 0 ? null : this._decodeValue(b4a.from(values[i])))
52
136
  }
53
137
 
54
138
  this._onfinished()
55
139
  }
56
140
 
57
- _onwrite (err) {
58
- for (let i = 0, n = this._promises.length; i < n; i++) {
59
- const promise = this._promises[i]
60
- if (promise === null) continue
141
+ get (key) {
142
+ if (this._request) throw new Error('Request already in progress')
61
143
 
62
- if (err) promise.reject(new Error(err))
63
- else promise.resolve(this._values[i].byteLength === 0 ? null : this._values[i])
64
- }
144
+ const promise = new Promise(this._enqueuePromise)
65
145
 
66
- this._onfinished()
146
+ this._operations.push(new RocksDBGet(this._encodeKey(key)))
147
+ this._resize()
148
+
149
+ return promise
67
150
  }
68
151
 
69
- _resize () {
70
- if (this._keys.length <= this._capacity) return
152
+ tryPut (key, value) {
153
+ if (this._request) throw new Error('Request already in progress')
71
154
 
72
- while (this._keys.length > this._capacity) {
73
- this._capacity *= 2
74
- }
155
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
156
+ this._promises.push(null)
157
+ this._resize()
158
+ }
159
+ }
160
+
161
+ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
162
+ async _init () {
163
+ this._handle = binding.writeInit(this)
164
+ this._buffer = binding.writeBuffer(this._handle, this._capacity)
165
+ }
75
166
 
76
- if (this._handle !== null) {
77
- this._buffer = binding.batchBuffer(this._handle, this._capacity)
167
+ _resize () {
168
+ if (super._resize() && this._handle !== null) {
169
+ this._buffer = binding.writeBuffer(this._handle, this._capacity)
78
170
  }
79
171
  }
80
172
 
81
- async ready () {
82
- if (this._handle !== null) return
173
+ async _flush () {
174
+ await super._flush()
83
175
 
84
- if (this._db.opened === false) await this._db.ready()
176
+ binding.write(this._db._handle, this._handle, this._operations, this._onwrite)
177
+ }
178
+
179
+ _onwrite (err) {
180
+ for (let i = 0, n = this._promises.length; i < n; i++) {
181
+ const promise = this._promises[i]
182
+ if (promise === null) continue
85
183
 
86
- this._handle = binding.batchInit(this._db._handle, this)
184
+ if (err) promise.reject(new Error(err))
185
+ else promise.resolve()
186
+ }
87
187
 
88
- this._buffer = binding.batchBuffer(this._handle, this._capacity)
188
+ this._onfinished()
89
189
  }
90
190
 
91
- add (key, value = empty) {
191
+ put (key, value) {
92
192
  if (this._request) throw new Error('Request already in progress')
93
193
 
94
194
  const promise = new Promise(this._enqueuePromise)
95
195
 
96
- this._keys.push(this._encodeKey(key))
97
- this._values.push(this._encodeValue(value))
196
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
98
197
  this._resize()
99
198
 
100
199
  return promise
101
200
  }
102
201
 
103
- tryAdd (key, value = empty) {
202
+ tryPut (key, value) {
104
203
  if (this._request) throw new Error('Request already in progress')
105
204
 
106
- this._keys.push(this._encodeKey(key))
107
- this._values.push(this._encodeValue(value))
205
+ this._operations.push(new RocksDBPut(this._encodeKey(key), this._encodeValue(value)))
108
206
  this._promises.push(null)
109
207
  this._resize()
110
208
  }
111
209
 
112
- read () {
210
+ delete (key) {
113
211
  if (this._request) throw new Error('Request already in progress')
114
212
 
115
- this._request = new Promise((resolve) => { this._resolveRequest = resolve })
116
- this._read()
213
+ const promise = new Promise(this._enqueuePromise)
117
214
 
118
- return this._request
215
+ this._operations.push(new RocksDBDelete(this._encodeKey(key)))
216
+ this._resize()
217
+
218
+ return promise
119
219
  }
120
220
 
121
- tryRead () {
221
+ tryDelete (key) {
122
222
  if (this._request) throw new Error('Request already in progress')
123
223
 
124
- this._request = true
125
- this._read()
224
+ this._operations.push(new RocksDBDelete(this._encodeKey(key)))
225
+ this._promises.push(null)
226
+ this._resize()
126
227
  }
127
228
 
128
- async _read () {
129
- if (this._handle === null) await this.ready()
229
+ deleteRange (start, end) {
230
+ if (this._request) throw new Error('Request already in progress')
231
+
232
+ const promise = new Promise(this._enqueuePromise)
233
+
234
+ this._operations.push(new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end)))
235
+ this._resize()
130
236
 
131
- binding.batchRead(this._handle, this._keys, this._onread)
237
+ return promise
132
238
  }
133
239
 
134
- write () {
240
+ tryDeleteRange (start, end) {
135
241
  if (this._request) throw new Error('Request already in progress')
136
242
 
137
- this._request = new Promise((resolve) => { this._resolveRequest = resolve })
138
- this._write()
243
+ this._operations.push(new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end)))
244
+ this._promises.push(null)
245
+ this._resize()
246
+ }
247
+ }
139
248
 
140
- return this._request
249
+ class RocksDBGet {
250
+ constructor (key) {
251
+ this.key = key
141
252
  }
142
253
 
143
- tryWrite () {
144
- if (this._request) throw new Error('Request already in progress')
254
+ get type () {
255
+ return binding.GET
256
+ }
257
+ }
145
258
 
146
- this._request = true
147
- this._write()
259
+ class RocksDBPut {
260
+ constructor (key, value) {
261
+ this.key = key
262
+ this.value = value
148
263
  }
149
264
 
150
- async _write () {
151
- if (this._handle === null) await this.ready()
265
+ get type () {
266
+ return binding.PUT
267
+ }
268
+ }
152
269
 
153
- binding.batchWrite(this._handle, this._keys, this._values, this._onwrite)
270
+ class RocksDBDelete {
271
+ constructor (key) {
272
+ this.key = key
154
273
  }
155
274
 
156
- _enqueuePromise (resolve, reject) {
157
- this._promises.push({ resolve, reject })
275
+ get type () {
276
+ return binding.DELETE
158
277
  }
278
+ }
159
279
 
160
- _encodeKey (k) {
161
- if (typeof k === 'string') return Buffer.from(k)
162
- return k
280
+ class RocksDBDeleteRange {
281
+ constructor (start, end) {
282
+ this.start = start
283
+ this.end = end
163
284
  }
164
285
 
165
- _encodeValue (v) {
166
- if (v === null) return empty
167
- if (typeof v === 'string') return Buffer.from(v)
168
- return v
286
+ get type () {
287
+ return binding.DELETE_RANGE
169
288
  }
170
289
  }
package/lib/iterator.js CHANGED
@@ -1,40 +1,36 @@
1
1
  const { Readable } = require('streamx')
2
+ const c = require('compact-encoding')
2
3
  const b4a = require('b4a')
3
4
  const binding = require('../binding')
4
5
 
5
6
  const empty = b4a.alloc(0)
6
7
 
7
8
  module.exports = class RocksDBIterator extends Readable {
8
- constructor (db, start, end, opts = {}) {
9
- if (typeof start === 'string') start = b4a.from(start)
10
- if (typeof end === 'string') end = b4a.from(end)
11
-
12
- if (start && !b4a.isBuffer(start)) {
13
- opts = start
14
- start = empty
15
- } else if (end && !b4a.isBuffer(end)) {
16
- opts = end
17
- end = empty
18
- }
19
-
9
+ constructor (db, opts = {}) {
20
10
  const {
21
- gt = empty,
22
- gte = start,
23
- lt = end,
24
- lte = empty,
11
+ gt = null,
12
+ gte = null,
13
+ lt = null,
14
+ lte = null,
25
15
  reverse = false,
26
16
  limit = Infinity,
27
- capacity = 8
17
+ capacity = 8,
18
+ encoding = null,
19
+ keyEncoding = encoding,
20
+ valueEncoding = encoding
28
21
  } = opts
29
22
 
30
23
  super()
31
24
 
32
25
  this._db = db
33
26
 
34
- this._gt = toBuffer(gt) || empty
35
- this._gte = toBuffer(gte) || empty
36
- this._lt = toBuffer(lt) || empty
37
- this._lte = toBuffer(lte) || empty
27
+ this._keyEncoding = keyEncoding
28
+ this._valueEncoding = valueEncoding
29
+
30
+ this._gt = gt ? this._encodeKey(gt) : empty
31
+ this._gte = gte ? this._encodeKey(gte) : empty
32
+ this._lt = lt ? this._encodeKey(lt) : empty
33
+ this._lte = lte ? this._encodeKey(lte) : empty
38
34
 
39
35
  this._reverse = reverse
40
36
  this._limit = limit
@@ -66,7 +62,7 @@ module.exports = class RocksDBIterator extends Readable {
66
62
  this._limit -= n
67
63
 
68
64
  for (let i = 0; i < n; i++) {
69
- this.push({ key: b4a.from(keys[i]), value: b4a.from(values[i]) })
65
+ this.push({ key: this._decodeKey(b4a.from(keys[i])), value: this._decodeValue(b4a.from(values[i])) })
70
66
  }
71
67
 
72
68
  if (n < this._capacity) this.push(null)
@@ -91,12 +87,11 @@ module.exports = class RocksDBIterator extends Readable {
91
87
 
92
88
  if (this._db.opened === false) await this._db.ready()
93
89
 
94
- this._handle = binding.iteratorInit(this._db._handle, this,
95
- this._onopen,
96
- this._onclose,
97
- this._onread
98
- )
90
+ this._init()
91
+ }
99
92
 
93
+ _init () {
94
+ this._handle = binding.iteratorInit(this, this._onopen, this._onclose, this._onread)
100
95
  this._buffer = binding.iteratorBuffer(this._handle, this._capacity)
101
96
  }
102
97
 
@@ -105,7 +100,7 @@ module.exports = class RocksDBIterator extends Readable {
105
100
 
106
101
  this._pendingOpen = cb
107
102
 
108
- binding.iteratorOpen(this._handle, this._gt, this._gte, this._lt, this._lte, this._reverse)
103
+ binding.iteratorOpen(this._db._handle, this._handle, this._gt, this._gte, this._lt, this._lte, this._reverse)
109
104
  }
110
105
 
111
106
  _read (cb) {
@@ -121,8 +116,20 @@ module.exports = class RocksDBIterator extends Readable {
121
116
 
122
117
  binding.iteratorClose(this._handle)
123
118
  }
124
- }
125
119
 
126
- function toBuffer (data) {
127
- return typeof data === 'string' ? b4a.from(data) : data
120
+ _encodeKey (k) {
121
+ if (this._keyEncoding) return c.encode(this._keyEncoding, k)
122
+ if (typeof k === 'string') return b4a.from(k)
123
+ return k
124
+ }
125
+
126
+ _decodeKey (b) {
127
+ if (this._keyEncoding) return c.decode(this._keyEncoding, b)
128
+ return b
129
+ }
130
+
131
+ _decodeValue (b) {
132
+ if (this._valueEncoding) return c.decode(this._valueEncoding, b)
133
+ return b
134
+ }
128
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rocksdb-native",
3
- "version": "1.2.0",
3
+ "version": "2.1.0",
4
4
  "description": "librocksdb bindings for JavaScript",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -34,6 +34,7 @@
34
34
  "homepage": "https://github.com/holepunchto/rocksdb-native",
35
35
  "dependencies": {
36
36
  "b4a": "^1.6.6",
37
+ "compact-encoding": "^2.15.0",
37
38
  "load-addon": "^1.0.0",
38
39
  "ready-resource": "^1.0.0",
39
40
  "streamx": "^2.16.1"