Skip to content
This repository has been archived by the owner on Dec 2, 2024. It is now read-only.

Commit

Permalink
Optimize db.clear() (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers authored Oct 2, 2021
1 parent 3abb824 commit a0856c4
Showing 1 changed file with 70 additions and 32 deletions.
102 changes: 70 additions & 32 deletions memdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ const ltgt = require('ltgt')
const createRBT = require('functional-red-black-tree')
const { Buffer } = require('buffer')

const NONE = Symbol('none')
const rangeOptions = ['gt', 'gte', 'lt', 'lte']
const kNone = Symbol('none')
const kKeys = Symbol('keys')
const kValues = Symbol('values')
const kIncrement = Symbol('increment')

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

this.keyAsBuffer = options.keyAsBuffer !== false
this.valueAsBuffer = options.valueAsBuffer !== false
this[kKeys] = options.keys
this[kValues] = options.values
this._reverse = options.reverse
this._options = options
this._done = 0

if (!this._reverse) {
this._incr = 'next'
this._lowerBound = ltgt.lowerBound(options, NONE)
this._upperBound = ltgt.upperBound(options, NONE)
this._lowerBound = ltgt.lowerBound(options, kNone)
this._upperBound = ltgt.upperBound(options, kNone)

if (this._lowerBound === NONE) {
if (this._lowerBound === kNone) {
this._tree = tree.begin
} else if (ltgt.lowerBoundInclusive(options)) {
this._tree = tree.ge(this._lowerBound)
} else {
this._tree = tree.gt(this._lowerBound)
}

if (this._upperBound !== NONE) {
if (this._upperBound !== kNone) {
if (ltgt.upperBoundInclusive(options)) {
this._test = lte
} else {
Expand All @@ -61,18 +67,18 @@ function MemIterator (db, options) {
}
} else {
this._incr = 'prev'
this._lowerBound = ltgt.upperBound(options, NONE)
this._upperBound = ltgt.lowerBound(options, NONE)
this._lowerBound = ltgt.upperBound(options, kNone)
this._upperBound = ltgt.lowerBound(options, kNone)

if (this._lowerBound === NONE) {
if (this._lowerBound === kNone) {
this._tree = tree.end
} else if (ltgt.upperBoundInclusive(options)) {
this._tree = tree.le(this._lowerBound)
} else {
this._tree = tree.lt(this._lowerBound)
}

if (this._upperBound !== NONE) {
if (this._upperBound !== kNone) {
if (ltgt.lowerBoundInclusive(options)) {
this._test = gte
} else {
Expand All @@ -85,30 +91,23 @@ function MemIterator (db, options) {
inherits(MemIterator, AbstractIterator)

MemIterator.prototype._next = function (callback) {
let key
let value

if (this._done++ >= this._limit) return this._nextTick(callback)
if (!this[kIncrement]()) return this._nextTick(callback)
if (!this._tree.valid) return this._nextTick(callback)

key = this._tree.key
value = this._tree.value
let key = this._tree.key
let value = this._tree.value

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

if (!this.keyAsBuffer) {
key = key.toString()
}

if (!this.valueAsBuffer) {
value = value.toString()
}
key = !this[kKeys] ? undefined : this.keyAsBuffer ? key : key.toString()
value = !this[kValues] ? undefined : this.valueAsBuffer ? value : value.toString()

this._tree[this._incr]()
this._nextTick(callback, null, key, value)
}

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

MemIterator.prototype._test = function () {
Expand All @@ -118,7 +117,7 @@ MemIterator.prototype._test = function () {
MemIterator.prototype._outOfRange = function (target) {
if (!this._test(target)) {
return true
} else if (this._lowerBound === NONE) {
} else if (this._lowerBound === kNone) {
return false
} else if (!this._reverse) {
if (ltgt.lowerBoundInclusive(this._options)) {
Expand Down Expand Up @@ -168,9 +167,7 @@ function MemDOWN () {
inherits(MemDOWN, AbstractLevelDOWN)

MemDOWN.prototype._open = function (options, callback) {
this._nextTick(() => {
callback(null, this)
})
this._nextTick(callback)
}

MemDOWN.prototype._serializeKey = function (key) {
Expand Down Expand Up @@ -207,9 +204,7 @@ MemDOWN.prototype._get = function (key, options, callback) {
value = value.toString()
}

this._nextTick(function callNext () {
callback(null, value)
})
this._nextTick(callback, null, value)
}

MemDOWN.prototype._getMany = function (keys, options, callback) {
Expand Down Expand Up @@ -248,6 +243,39 @@ MemDOWN.prototype._batch = function (array, options, callback) {
this._nextTick(callback)
}

MemDOWN.prototype._clear = function (options, callback) {
if (!hasLimit(options) && !Object.keys(options).some(isRangeOption)) {
// Delete everything by creating a new empty tree.
this._store = createRBT(ltgt.compare)
return this._nextTick(callback)
}

const iterator = this._iterator({
...options,
keys: true,
values: false,
keyAsBuffer: true
})

const loop = () => {
// TODO: add option to control "batch size"
for (let i = 0; i < 500; i++) {
if (!iterator[kIncrement]()) return callback()
if (!iterator._tree.valid) return callback()
if (!iterator._test(iterator._tree.key)) return callback()

// Must also include changes made in parallel to clear()
this._store = this._store.remove(iterator._tree.key)
iterator._tree[iterator._incr]()
}

// Some time to breathe
this._nextTick(loop)
}

this._nextTick(loop)
}

MemDOWN.prototype._iterator = function (options) {
return new MemIterator(this, options)
}
Expand All @@ -269,3 +297,13 @@ if (typeof process !== 'undefined' && !process.browser && typeof global !== 'und
}
}
}

function isRangeOption (k) {
return rangeOptions.includes(k)
}

function hasLimit (options) {
return options.limit != null &&
options.limit >= 0 &&
options.limit < Infinity
}

0 comments on commit a0856c4

Please sign in to comment.