Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.
/ memdown Public archive

Commit a0856c4

Browse files
authored
Optimize db.clear() (#213)
Ref Level/community#79
1 parent 3abb824 commit a0856c4

File tree

1 file changed

+70
-32
lines changed

1 file changed

+70
-32
lines changed

memdown.js

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ const ltgt = require('ltgt')
66
const createRBT = require('functional-red-black-tree')
77
const { Buffer } = require('buffer')
88

9-
const NONE = Symbol('none')
9+
const rangeOptions = ['gt', 'gte', 'lt', 'lte']
10+
const kNone = Symbol('none')
11+
const kKeys = Symbol('keys')
12+
const kValues = Symbol('values')
13+
const kIncrement = Symbol('increment')
1014

1115
// TODO (perf): replace ltgt.compare with a simpler, buffer-only comparator
1216
function gt (value) {
@@ -35,24 +39,26 @@ function MemIterator (db, options) {
3539

3640
this.keyAsBuffer = options.keyAsBuffer !== false
3741
this.valueAsBuffer = options.valueAsBuffer !== false
42+
this[kKeys] = options.keys
43+
this[kValues] = options.values
3844
this._reverse = options.reverse
3945
this._options = options
4046
this._done = 0
4147

4248
if (!this._reverse) {
4349
this._incr = 'next'
44-
this._lowerBound = ltgt.lowerBound(options, NONE)
45-
this._upperBound = ltgt.upperBound(options, NONE)
50+
this._lowerBound = ltgt.lowerBound(options, kNone)
51+
this._upperBound = ltgt.upperBound(options, kNone)
4652

47-
if (this._lowerBound === NONE) {
53+
if (this._lowerBound === kNone) {
4854
this._tree = tree.begin
4955
} else if (ltgt.lowerBoundInclusive(options)) {
5056
this._tree = tree.ge(this._lowerBound)
5157
} else {
5258
this._tree = tree.gt(this._lowerBound)
5359
}
5460

55-
if (this._upperBound !== NONE) {
61+
if (this._upperBound !== kNone) {
5662
if (ltgt.upperBoundInclusive(options)) {
5763
this._test = lte
5864
} else {
@@ -61,18 +67,18 @@ function MemIterator (db, options) {
6167
}
6268
} else {
6369
this._incr = 'prev'
64-
this._lowerBound = ltgt.upperBound(options, NONE)
65-
this._upperBound = ltgt.lowerBound(options, NONE)
70+
this._lowerBound = ltgt.upperBound(options, kNone)
71+
this._upperBound = ltgt.lowerBound(options, kNone)
6672

67-
if (this._lowerBound === NONE) {
73+
if (this._lowerBound === kNone) {
6874
this._tree = tree.end
6975
} else if (ltgt.upperBoundInclusive(options)) {
7076
this._tree = tree.le(this._lowerBound)
7177
} else {
7278
this._tree = tree.lt(this._lowerBound)
7379
}
7480

75-
if (this._upperBound !== NONE) {
81+
if (this._upperBound !== kNone) {
7682
if (ltgt.lowerBoundInclusive(options)) {
7783
this._test = gte
7884
} else {
@@ -85,30 +91,23 @@ function MemIterator (db, options) {
8591
inherits(MemIterator, AbstractIterator)
8692

8793
MemIterator.prototype._next = function (callback) {
88-
let key
89-
let value
90-
91-
if (this._done++ >= this._limit) return this._nextTick(callback)
94+
if (!this[kIncrement]()) return this._nextTick(callback)
9295
if (!this._tree.valid) return this._nextTick(callback)
9396

94-
key = this._tree.key
95-
value = this._tree.value
97+
let key = this._tree.key
98+
let value = this._tree.value
9699

97100
if (!this._test(key)) return this._nextTick(callback)
98101

99-
if (!this.keyAsBuffer) {
100-
key = key.toString()
101-
}
102-
103-
if (!this.valueAsBuffer) {
104-
value = value.toString()
105-
}
102+
key = !this[kKeys] ? undefined : this.keyAsBuffer ? key : key.toString()
103+
value = !this[kValues] ? undefined : this.valueAsBuffer ? value : value.toString()
106104

107105
this._tree[this._incr]()
106+
this._nextTick(callback, null, key, value)
107+
}
108108

109-
this._nextTick(function callNext () {
110-
callback(null, key, value)
111-
})
109+
MemIterator.prototype[kIncrement] = function () {
110+
return this._done++ < this._limit
112111
}
113112

114113
MemIterator.prototype._test = function () {
@@ -118,7 +117,7 @@ MemIterator.prototype._test = function () {
118117
MemIterator.prototype._outOfRange = function (target) {
119118
if (!this._test(target)) {
120119
return true
121-
} else if (this._lowerBound === NONE) {
120+
} else if (this._lowerBound === kNone) {
122121
return false
123122
} else if (!this._reverse) {
124123
if (ltgt.lowerBoundInclusive(this._options)) {
@@ -168,9 +167,7 @@ function MemDOWN () {
168167
inherits(MemDOWN, AbstractLevelDOWN)
169168

170169
MemDOWN.prototype._open = function (options, callback) {
171-
this._nextTick(() => {
172-
callback(null, this)
173-
})
170+
this._nextTick(callback)
174171
}
175172

176173
MemDOWN.prototype._serializeKey = function (key) {
@@ -207,9 +204,7 @@ MemDOWN.prototype._get = function (key, options, callback) {
207204
value = value.toString()
208205
}
209206

210-
this._nextTick(function callNext () {
211-
callback(null, value)
212-
})
207+
this._nextTick(callback, null, value)
213208
}
214209

215210
MemDOWN.prototype._getMany = function (keys, options, callback) {
@@ -248,6 +243,39 @@ MemDOWN.prototype._batch = function (array, options, callback) {
248243
this._nextTick(callback)
249244
}
250245

246+
MemDOWN.prototype._clear = function (options, callback) {
247+
if (!hasLimit(options) && !Object.keys(options).some(isRangeOption)) {
248+
// Delete everything by creating a new empty tree.
249+
this._store = createRBT(ltgt.compare)
250+
return this._nextTick(callback)
251+
}
252+
253+
const iterator = this._iterator({
254+
...options,
255+
keys: true,
256+
values: false,
257+
keyAsBuffer: true
258+
})
259+
260+
const loop = () => {
261+
// TODO: add option to control "batch size"
262+
for (let i = 0; i < 500; i++) {
263+
if (!iterator[kIncrement]()) return callback()
264+
if (!iterator._tree.valid) return callback()
265+
if (!iterator._test(iterator._tree.key)) return callback()
266+
267+
// Must also include changes made in parallel to clear()
268+
this._store = this._store.remove(iterator._tree.key)
269+
iterator._tree[iterator._incr]()
270+
}
271+
272+
// Some time to breathe
273+
this._nextTick(loop)
274+
}
275+
276+
this._nextTick(loop)
277+
}
278+
251279
MemDOWN.prototype._iterator = function (options) {
252280
return new MemIterator(this, options)
253281
}
@@ -269,3 +297,13 @@ if (typeof process !== 'undefined' && !process.browser && typeof global !== 'und
269297
}
270298
}
271299
}
300+
301+
function isRangeOption (k) {
302+
return rangeOptions.includes(k)
303+
}
304+
305+
function hasLimit (options) {
306+
return options.limit != null &&
307+
options.limit >= 0 &&
308+
options.limit < Infinity
309+
}

0 commit comments

Comments
 (0)