rocksdb-native 2.6.9 → 3.0.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,165 +1,104 @@
1
- /* global Bare */
2
- const ReadyResource = require('ready-resource')
3
- const binding = require('./binding')
4
1
  const { ReadBatch, WriteBatch } = require('./lib/batch')
2
+ const ColumnFamily = require('./lib/column-family')
5
3
  const Iterator = require('./lib/iterator')
6
4
  const Snapshot = require('./lib/snapshot')
5
+ const DBState = require('./lib/state')
7
6
 
8
- module.exports = class RocksDB extends ReadyResource {
9
- constructor(
10
- path,
11
- {
12
- // default options, https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning
13
- readOnly = false,
14
- createIfMissing = true,
15
- maxBackgroundJobs = 6,
16
- bytesPerSync = 1048576,
17
- // blob options, https://github.com/facebook/rocksdb/wiki/BlobDB
18
- enableBlobFiles = false,
19
- minBlobSize = 0,
20
- blobFileSize = 0,
21
- enableBlobGarbageCollection = true,
22
- // (block) table options
23
- tableBlockSize = 16384,
24
- tableCacheIndexAndFilterBlocks = true,
25
- tableFormatVersion = 4
26
- } = {}
27
- ) {
28
- super()
29
-
30
- this.path = path
31
-
32
- this.readOnly = readOnly
33
- this.createIfMissing = createIfMissing
34
- this.maxBackgroundJobs = maxBackgroundJobs
35
- this.bytesPerSync = bytesPerSync
36
-
37
- this.enableBlobFiles = enableBlobFiles
38
- this.minBlobSize = minBlobSize
39
- this.blobFileSize = blobFileSize
40
- this.enableBlobGarbageCollection = enableBlobGarbageCollection
41
-
42
- this.tableBlockSize = tableBlockSize
43
- this.tableCacheIndexAndFilterBlocks = tableCacheIndexAndFilterBlocks
44
- this.tableFormatVersion = tableFormatVersion
45
-
46
- this._snapshots = new Set()
47
- this._refs = 0
48
- this._resolvePreclose = null
49
- this._resolveOnIdle = null
50
- this._idlePromise = null
51
-
52
- this._handle = binding.init()
53
- }
54
-
55
- async _open() {
56
- const opts = new Uint32Array(16)
57
-
58
- opts[0] = this.readOnly ? 1 : 0
59
- opts[1] = this.createIfMissing ? 1 : 0
60
- opts[2] = this.maxBackgroundJobs
61
- opts[3] = this.bytesPerSync & 0xffffffff
62
- opts[4] = Math.floor(this.bytesPerSync / 0x100000000)
63
- opts[5] = 0
64
- opts[6] = this.enableBlobFiles ? 1 : 0
65
- opts[7] = this.minBlobSize & 0xffffffff
66
- opts[8] = Math.floor(this.minBlobSize / 0x100000000)
67
- opts[9] = this.blobFileSize & 0xffffffff
68
- opts[10] = Math.floor(this.blobFileSize / 0x100000000)
69
- opts[11] = this.enableBlobGarbageCollection ? 1 : 0
70
- opts[12] = this.tableBlockSize & 0xffffffff
71
- opts[13] = Math.floor(this.tableBlockSize / 0x100000000)
72
- opts[14] = this.tableCacheIndexAndFilterBlocks ? 1 : 0
73
- opts[15] = this.tableFormatVersion
74
-
75
- const req = { resolve: null, reject: null, handle: null }
76
-
77
- const promise = new Promise((resolve, reject) => {
78
- req.resolve = resolve
79
- req.reject = reject
80
- })
7
+ class RocksDB {
8
+ constructor(path, opts = {}) {
9
+ const {
10
+ columnFamily,
11
+ state = new DBState(this, path, opts),
12
+ snapshot = null
13
+ } = opts
81
14
 
82
- req.handle = binding.open(this, this._handle, this.path, opts, req, onopen)
15
+ this._state = state
16
+ this._snapshot = snapshot
17
+ this._columnFamily = state.getColumnFamily(columnFamily)
18
+ this._index = this._state.addSession(this)
19
+ }
83
20
 
84
- await promise
21
+ get opened() {
22
+ return this._state.opened
23
+ }
85
24
 
86
- for (const snapshot of this._snapshots) snapshot._init()
25
+ get closed() {
26
+ return this.isRoot() ? this._state.closed : this._destroyed
27
+ }
87
28
 
88
- function onopen(err) {
89
- if (err) req.reject(new Error(err))
90
- else req.resolve()
91
- }
29
+ get path() {
30
+ return this._state.path
92
31
  }
93
32
 
94
- async _close() {
95
- if (this._refs > 0) {
96
- await new Promise((resolve) => {
97
- this._resolvePreclose = resolve
98
- })
99
- }
33
+ get defaultColumnFamily() {
34
+ return this._columnFamily
35
+ }
100
36
 
101
- for (const snapshot of this._snapshots) snapshot.destroy()
37
+ session({
38
+ columnFamily = this._columnFamily,
39
+ snapshot = this._snapshot !== null
40
+ } = {}) {
41
+ let snap = null
102
42
 
103
- const req = { resolve: null, reject: null, handle: null }
43
+ if (snapshot) {
44
+ snap = this._snapshot
45
+ if (snap === null) snap = new Snapshot(this._state)
46
+ else snap.ref()
47
+ }
104
48
 
105
- const promise = new Promise((resolve, reject) => {
106
- req.resolve = resolve
107
- req.reject = reject
49
+ return new RocksDB(null, {
50
+ state: this._state,
51
+ columnFamily,
52
+ snapshot: snap
108
53
  })
54
+ }
109
55
 
110
- req.handle = binding.close(this._handle, req, onclose)
111
-
112
- await promise
113
-
114
- function onclose(err) {
115
- if (err) req.reject(new Error(err))
116
- else req.resolve()
117
- }
56
+ columnFamily(name) {
57
+ return this.session({ columnFamily: name })
118
58
  }
119
59
 
120
- _incRef() {
121
- if (this.closing !== null) {
122
- throw new Error('Database closed')
123
- }
60
+ snapshot() {
61
+ return this.session({ snapshot: true })
62
+ }
124
63
 
125
- this._refs++
64
+ isRoot() {
65
+ return this === this._state.db
126
66
  }
127
67
 
128
- _decRef() {
129
- if (--this._refs !== 0) return
68
+ ready() {
69
+ return this._state.ready()
70
+ }
130
71
 
131
- if (this._resolveOnIdle !== null) {
132
- const resolve = this._resolveOnIdle
133
- this._resolveOnIdle = null
134
- this._idlePromise = null
135
- resolve()
72
+ async close({ force } = {}) {
73
+ if (this._index !== -1) {
74
+ this._state.removeSession(this)
75
+ this._index = -1
76
+ if (this._snapshot) this._snapshot.unref()
136
77
  }
137
78
 
138
- if (this._resolvePreclose !== null) {
139
- const resolve = this._resolvePreclose
140
- this._resolvePreclose = null
141
- resolve()
79
+ if (force) {
80
+ for (let i = this._state.sessions.length - 1; i >= 0; i--) {
81
+ await this._state.sessions[i].close()
82
+ }
142
83
  }
143
- }
144
84
 
145
- isIdle() {
146
- return this._refs === 0
85
+ return this.isRoot() ? this._state.close() : Promise.resolve()
147
86
  }
148
87
 
149
- idle() {
150
- if (this.isIdle()) return Promise.resolve()
88
+ suspend() {
89
+ return this._state.suspend()
90
+ }
151
91
 
152
- if (!this._idlePromise) {
153
- this._idlePromise = new Promise((resolve) => {
154
- this._resolveOnIdle = resolve
155
- })
156
- }
92
+ resume() {
93
+ return this._state.resume()
94
+ }
157
95
 
158
- return this._idlePromise
96
+ isIdle() {
97
+ return this._state.refs.isIdle()
159
98
  }
160
99
 
161
- snapshot(opts) {
162
- return new Snapshot(this, opts)
100
+ idle() {
101
+ return this._state.refs.idle()
163
102
  }
164
103
 
165
104
  iterator(range, opts) {
@@ -168,7 +107,6 @@ module.exports = class RocksDB extends ReadyResource {
168
107
 
169
108
  async peek(range, opts) {
170
109
  for await (const value of this.iterator({ ...range, ...opts, limit: 1 })) {
171
- // eslint-disable-line no-unreachable-loop
172
110
  return value
173
111
  }
174
112
 
@@ -184,27 +122,46 @@ module.exports = class RocksDB extends ReadyResource {
184
122
  }
185
123
 
186
124
  async get(key, opts) {
187
- const batch = this.read(opts)
188
- const value = batch.get(key)
189
- await batch.flush()
190
- return value
125
+ const batch = this.read({ ...opts, capacity: 1 })
126
+ try {
127
+ const value = batch.get(key)
128
+ batch.tryFlush()
129
+ return await value
130
+ } finally {
131
+ batch.destroy()
132
+ }
191
133
  }
192
134
 
193
135
  async put(key, value, opts) {
194
- const batch = this.write(opts)
195
- batch.put(key, value)
196
- await batch.flush()
136
+ const batch = this.write({ ...opts, capacity: 1 })
137
+ try {
138
+ batch.tryPut(key, value)
139
+ await batch.flush()
140
+ } finally {
141
+ batch.destroy()
142
+ }
197
143
  }
198
144
 
199
145
  async delete(key, opts) {
200
- const batch = this.write(opts)
201
- batch.delete(key)
202
- await batch.flush()
146
+ const batch = this.write({ ...opts, capacity: 1 })
147
+ try {
148
+ batch.tryDelete(key)
149
+ await batch.flush()
150
+ } finally {
151
+ batch.destroy()
152
+ }
203
153
  }
204
154
 
205
155
  async deleteRange(start, end, opts) {
206
- const batch = this.write(opts)
207
- batch.deleteRange(start, end)
208
- await batch.flush()
156
+ const batch = this.write({ ...opts, capacity: 1 })
157
+ try {
158
+ batch.tryDeleteRange(start, end)
159
+ await batch.flush()
160
+ } finally {
161
+ batch.destroy()
162
+ }
209
163
  }
210
164
  }
165
+
166
+ module.exports = exports = RocksDB
167
+ exports.ColumnFamily = ColumnFamily
package/lib/batch.js CHANGED
@@ -1,23 +1,23 @@
1
1
  const c = require('compact-encoding')
2
- const b4a = require('b4a')
3
2
  const binding = require('../binding')
4
3
 
5
- const empty = b4a.alloc(0)
6
- const emptyPromise = Promise.resolve()
4
+ const empty = Buffer.alloc(0)
5
+ const resolved = Promise.resolve()
7
6
 
8
7
  class RocksDBBatch {
9
8
  constructor(db, opts = {}) {
10
9
  const {
10
+ columnFamily = db.defaultColumnFamily,
11
11
  capacity = 8,
12
12
  encoding = null,
13
13
  keyEncoding = encoding,
14
- valueEncoding = encoding,
15
- autoDestroy = true
14
+ valueEncoding = encoding
16
15
  } = opts
17
16
 
18
- db._incRef()
17
+ db._state.ref()
19
18
 
20
- this._db = db
19
+ this._state = db._state
20
+ this._columnFamily = columnFamily
21
21
  this._destroyed = false
22
22
  this._capacity = capacity
23
23
  this._operations = []
@@ -25,27 +25,25 @@ class RocksDBBatch {
25
25
 
26
26
  this._keyEncoding = keyEncoding
27
27
  this._valueEncoding = valueEncoding
28
- this._autoDestroy = autoDestroy !== false
29
28
 
30
29
  this._enqueuePromise = this._enqueuePromise.bind(this)
31
30
 
32
31
  this._request = null
33
- this._resolveRequest = null
32
+ this._resolve = null
34
33
 
35
34
  this._handle = null
36
35
  this._buffer = null
37
36
 
38
- if (db.opened === true) this.ready()
37
+ if (this._state.opened === true) this.ready()
39
38
  }
40
39
 
41
40
  _onfinished() {
42
- const resolve = this._resolveRequest
41
+ const resolve = this._resolve
43
42
 
44
43
  this._operations = []
45
44
  this._promises = []
46
45
  this._request = null
47
- this._resolveRequest = null
48
- if (this._autoDestroy) this.destroy()
46
+ this._resolve = null
49
47
 
50
48
  if (resolve !== null) resolve()
51
49
  }
@@ -63,34 +61,38 @@ class RocksDBBatch {
63
61
  async ready() {
64
62
  if (this._handle !== null) return
65
63
 
66
- if (this._db.opened === false) await this._db.ready()
64
+ if (this._state.opened === false) await this._state.ready()
67
65
 
68
66
  this._init()
69
67
  }
70
68
 
71
69
  destroy() {
72
- if (this._request) throw new Error('Batch already flushed')
70
+ if (this._request) throw new Error('Request in progress')
73
71
  if (this._destroyed) return
72
+
74
73
  this._destroyed = true
75
- this._db._decRef()
74
+
75
+ this._state.unref()
76
76
  }
77
77
 
78
78
  flush() {
79
+ if (this._request) throw new Error('Request in progress')
79
80
  if (this._destroyed) throw new Error('Batch is destroyed')
80
- if (this._request) throw new Error('Request already in progress')
81
81
 
82
82
  this._request = new Promise((resolve) => {
83
- this._resolveRequest = resolve
83
+ this._resolve = resolve
84
84
  })
85
+
85
86
  this._flush()
86
87
 
87
88
  return this._request
88
89
  }
89
90
 
90
91
  tryFlush() {
91
- if (this._request) throw new Error('Request already in progress')
92
+ if (this._request) throw new Error('Request in progress')
93
+
94
+ this._request = resolved
92
95
 
93
- this._request = emptyPromise
94
96
  this._flush()
95
97
  }
96
98
 
@@ -104,14 +106,14 @@ class RocksDBBatch {
104
106
 
105
107
  _encodeKey(k) {
106
108
  if (this._keyEncoding) return c.encode(this._keyEncoding, k)
107
- if (typeof k === 'string') return b4a.from(k)
109
+ if (typeof k === 'string') return Buffer.from(k)
108
110
  return k
109
111
  }
110
112
 
111
113
  _encodeValue(v) {
112
114
  if (this._valueEncoding) return c.encode(this._valueEncoding, v)
113
115
  if (v === null) return empty
114
- if (typeof v === 'string') return b4a.from(v)
116
+ if (typeof v === 'string') return Buffer.from(v)
115
117
  return v
116
118
  }
117
119
 
@@ -122,12 +124,10 @@ class RocksDBBatch {
122
124
  }
123
125
 
124
126
  exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
125
- constructor(db, opts = {}) {
126
- const { snapshot = null } = opts
127
-
127
+ constructor(db, opts) {
128
128
  super(db, opts)
129
129
 
130
- this._snapshot = snapshot
130
+ this._snapshot = db._snapshot
131
131
  }
132
132
 
133
133
  _init() {
@@ -145,7 +145,7 @@ exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
145
145
  await super._flush()
146
146
 
147
147
  binding.read(
148
- this._db._handle,
148
+ this._state.handle,
149
149
  this._handle,
150
150
  this._operations,
151
151
  this._snapshot ? this._snapshot._handle : null,
@@ -166,7 +166,7 @@ exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
166
166
  promise.resolve(
167
167
  values[i].byteLength === 0
168
168
  ? null
169
- : this._decodeValue(b4a.from(values[i]))
169
+ : this._decodeValue(Buffer.from(values[i]))
170
170
  )
171
171
  }
172
172
 
@@ -178,7 +178,10 @@ exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {
178
178
 
179
179
  const promise = new Promise(this._enqueuePromise)
180
180
 
181
- this._operations.push(new RocksDBGet(this._encodeKey(key)))
181
+ this._operations.push(
182
+ new RocksDBGet(this._encodeKey(key), this._columnFamily)
183
+ )
184
+
182
185
  this._resize()
183
186
 
184
187
  return promise
@@ -201,7 +204,7 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
201
204
  await super._flush()
202
205
 
203
206
  binding.write(
204
- this._db._handle,
207
+ this._state.handle,
205
208
  this._handle,
206
209
  this._operations,
207
210
  this,
@@ -227,8 +230,13 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
227
230
  const promise = new Promise(this._enqueuePromise)
228
231
 
229
232
  this._operations.push(
230
- new RocksDBPut(this._encodeKey(key), this._encodeValue(value))
233
+ new RocksDBPut(
234
+ this._encodeKey(key),
235
+ this._encodeValue(value),
236
+ this._columnFamily
237
+ )
231
238
  )
239
+
232
240
  this._resize()
233
241
 
234
242
  return promise
@@ -238,9 +246,15 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
238
246
  if (this._request) throw new Error('Request already in progress')
239
247
 
240
248
  this._operations.push(
241
- new RocksDBPut(this._encodeKey(key), this._encodeValue(value))
249
+ new RocksDBPut(
250
+ this._encodeKey(key),
251
+ this._encodeValue(value),
252
+ this._columnFamily
253
+ )
242
254
  )
255
+
243
256
  this._promises.push(null)
257
+
244
258
  this._resize()
245
259
  }
246
260
 
@@ -249,7 +263,10 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
249
263
 
250
264
  const promise = new Promise(this._enqueuePromise)
251
265
 
252
- this._operations.push(new RocksDBDelete(this._encodeKey(key)))
266
+ this._operations.push(
267
+ new RocksDBDelete(this._encodeKey(key), this._columnFamily)
268
+ )
269
+
253
270
  this._resize()
254
271
 
255
272
  return promise
@@ -258,8 +275,12 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
258
275
  tryDelete(key) {
259
276
  if (this._request) throw new Error('Request already in progress')
260
277
 
261
- this._operations.push(new RocksDBDelete(this._encodeKey(key)))
278
+ this._operations.push(
279
+ new RocksDBDelete(this._encodeKey(key), this._columnFamily)
280
+ )
281
+
262
282
  this._promises.push(null)
283
+
263
284
  this._resize()
264
285
  }
265
286
 
@@ -269,8 +290,13 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
269
290
  const promise = new Promise(this._enqueuePromise)
270
291
 
271
292
  this._operations.push(
272
- new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end))
293
+ new RocksDBDeleteRange(
294
+ this._encodeKey(start),
295
+ this._encodeKey(end),
296
+ this._columnFamily
297
+ )
273
298
  )
299
+
274
300
  this._resize()
275
301
 
276
302
  return promise
@@ -280,16 +306,23 @@ exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {
280
306
  if (this._request) throw new Error('Request already in progress')
281
307
 
282
308
  this._operations.push(
283
- new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end))
309
+ new RocksDBDeleteRange(
310
+ this._encodeKey(start),
311
+ this._encodeKey(end),
312
+ this._columnFamily
313
+ )
284
314
  )
315
+
285
316
  this._promises.push(null)
317
+
286
318
  this._resize()
287
319
  }
288
320
  }
289
321
 
290
322
  class RocksDBGet {
291
- constructor(key) {
323
+ constructor(key, columnFamily) {
292
324
  this.key = key
325
+ this.columnFamily = columnFamily._handle
293
326
  }
294
327
 
295
328
  get type() {
@@ -298,9 +331,10 @@ class RocksDBGet {
298
331
  }
299
332
 
300
333
  class RocksDBPut {
301
- constructor(key, value) {
334
+ constructor(key, value, columnFamily) {
302
335
  this.key = key
303
336
  this.value = value
337
+ this.columnFamily = columnFamily._handle
304
338
  }
305
339
 
306
340
  get type() {
@@ -309,8 +343,9 @@ class RocksDBPut {
309
343
  }
310
344
 
311
345
  class RocksDBDelete {
312
- constructor(key) {
346
+ constructor(key, columnFamily) {
313
347
  this.key = key
348
+ this.columnFamily = columnFamily._handle
314
349
  }
315
350
 
316
351
  get type() {
@@ -319,9 +354,10 @@ class RocksDBDelete {
319
354
  }
320
355
 
321
356
  class RocksDBDeleteRange {
322
- constructor(start, end) {
357
+ constructor(start, end, columnFamily) {
323
358
  this.start = start
324
359
  this.end = end
360
+ this.columnFamily = columnFamily._handle
325
361
  }
326
362
 
327
363
  get type() {
@@ -0,0 +1,56 @@
1
+ const binding = require('../binding')
2
+
3
+ class RocksDBColumnFamily {
4
+ constructor(name, opts = {}) {
5
+ const {
6
+ // Blob options
7
+ enableBlobFiles = false,
8
+ minBlobSize = 0,
9
+ blobFileSize = 0,
10
+ enableBlobGarbageCollection = true,
11
+ // Block table options
12
+ tableBlockSize = 16384,
13
+ tableCacheIndexAndFilterBlocks = true,
14
+ tableFormatVersion = 4,
15
+ // In case we are cloning
16
+ settings = null
17
+ } = opts
18
+
19
+ this._name = name
20
+ this._settings =
21
+ settings ||
22
+ Uint32Array.from([
23
+ 0,
24
+ enableBlobFiles ? 1 : 0,
25
+ minBlobSize & 0xffffffff,
26
+ Math.floor(minBlobSize / 0x100000000),
27
+ blobFileSize & 0xffffffff,
28
+ Math.floor(blobFileSize / 0x100000000),
29
+ enableBlobGarbageCollection ? 1 : 0,
30
+ tableBlockSize & 0xffffffff,
31
+ Math.floor(tableBlockSize / 0x100000000),
32
+ tableCacheIndexAndFilterBlocks ? 1 : 0,
33
+ tableFormatVersion
34
+ ])
35
+
36
+ this._handle = binding.columnFamilyInit(name, this._settings)
37
+ }
38
+
39
+ cloneSettings(name) {
40
+ return new RocksDBColumnFamily(name, { settings: this._settings })
41
+ }
42
+
43
+ get name() {
44
+ return this._name
45
+ }
46
+
47
+ destroy() {
48
+ if (this._handle === null) return
49
+
50
+ binding.columnFamilyDestroy(this._handle)
51
+
52
+ this._handle = null
53
+ }
54
+ }
55
+
56
+ module.exports = RocksDBColumnFamily