From 22e9e6470d6252e453cf052ea9ed5f84ab02e842 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 22 Dec 2024 02:08:30 +0100 Subject: [PATCH] Implement explicit snapshots See https://github.com/Level/community/issues/118. Depends on https://github.com/Level/abstract-level/pull/93. Category: addition --- README.md | 2 +- index.js | 45 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bcbcd83..3bcdaf0 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ for await (const [key, value] of db.iterator({ gt: 'a' })) { ## API -The API of `memory-level` follows that of [`abstract-level`](https://github.com/Level/abstract-level) with a one additional constructor option (see below). The `createIfMissing` and `errorIfExists` options of `abstract-level` are not relevant here. Data is discarded when the last reference to the database is released (i.e. `db = null`). Closing or reopening the database has no effect on the data. Data is _not_ copied: when storing a Buffer value for example, subsequent mutations to that Buffer will affect the stored data too. +The API of `memory-level` follows that of [`abstract-level`](https://github.com/Level/abstract-level) with one additional constructor option (see below). The `createIfMissing` and `errorIfExists` options of `abstract-level` are not relevant here. Both implicit and explicit snapshots are supported. Data is discarded when the last reference to the database is released (i.e. `db = null`). Closing or reopening the database has no effect on the data. Data is _not_ copied: when storing a Buffer value for example, subsequent mutations to that Buffer will affect the stored data too. ### `db = new MemoryLevel([options])` diff --git a/index.js b/index.js index c1bee4c..5eb27d1 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,8 @@ const { AbstractLevel, AbstractIterator, AbstractKeyIterator, - AbstractValueIterator + AbstractValueIterator, + AbstractSnapshot } = require('abstract-level') const ModuleError = require('module-error') @@ -59,7 +60,7 @@ function lte (value) { class MemoryIterator extends AbstractIterator { constructor (db, options) { super(db, options) - this[kInit](db[kTree], options) + this[kInit](db, options) } async _next () { @@ -103,7 +104,7 @@ class MemoryIterator extends AbstractIterator { class MemoryKeyIterator extends AbstractKeyIterator { constructor (db, options) { super(db, options) - this[kInit](db[kTree], options) + this[kInit](db, options) } async _next () { @@ -145,7 +146,7 @@ class MemoryKeyIterator extends AbstractKeyIterator { class MemoryValueIterator extends AbstractValueIterator { constructor (db, options) { super(db, options) - this[kInit](db[kTree], options) + this[kInit](db, options) } async _next (options) { @@ -187,7 +188,11 @@ class MemoryValueIterator extends AbstractValueIterator { } for (const Ctor of [MemoryIterator, MemoryKeyIterator, MemoryValueIterator]) { - Ctor.prototype[kInit] = function (tree, options) { + Ctor.prototype[kInit] = function (db, options) { + const tree = options.snapshot != null + ? options.snapshot[kTree] + : db[kTree] + this[kReverse] = options.reverse this[kOptions] = options @@ -281,6 +286,7 @@ class MemoryLevel extends AbstractLevel { super({ seek: true, + explicitSnapshots: true, permanence: false, createIfMissing: false, errorIfExists: false, @@ -305,12 +311,20 @@ class MemoryLevel extends AbstractLevel { } async _get (key, options) { + const tree = options.snapshot != null + ? options.snapshot[kTree] + : this[kTree] + // Is undefined if not found - return this[kTree].get(key) + return tree.get(key) } async _getMany (keys, options) { - return keys.map(key => this[kTree].get(key)) + const tree = options.snapshot != null + ? options.snapshot[kTree] + : this[kTree] + + return keys.map(getFromThis, tree) } async _del (key, options) { @@ -335,7 +349,7 @@ class MemoryLevel extends AbstractLevel { } async _clear (options) { - if (options.limit === -1 && !Object.keys(options).some(isRangeOption)) { + if (options.limit === -1 && !Object.keys(options).some(isRangeOption) && !options.snapshot) { // Delete everything by creating a new empty tree. this[kTree] = createRBT(compare) return @@ -374,6 +388,17 @@ class MemoryLevel extends AbstractLevel { _values (options) { return new MemoryValueIterator(this, options) } + + _snapshot (options) { + return new MemorySnapshot(this[kTree], options) + } +} + +class MemorySnapshot extends AbstractSnapshot { + constructor (tree, options) { + super(options) + this[kTree] = tree + } } exports.MemoryLevel = MemoryLevel @@ -391,6 +416,10 @@ if (typeof process !== 'undefined' && !process.browser && typeof global !== 'und breathe = async function () {} } +function getFromThis (key) { + return this.get(key) +} + function isRangeOption (k) { return rangeOptions.has(k) }