From 1b6d0e5e9d7c301cb3dd9530eb95e2ed93727a85 Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Fri, 18 Sep 2026 12:25:44 -0400 Subject: [PATCH 1/9] session checkout --- index.js | 18 ++++++++++++++++++ test/checkout.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/index.js b/index.js index b734503..0fbee31 100644 --- a/index.js +++ b/index.js @@ -473,6 +473,24 @@ class HyperDB { return this._createSnapshot(this, false, context) } + session(options) { + maybeClosed(this) + + const context = (options && options.context) || this.context + const checkout = options ? { ...this.engine.head(), ...options } : this.engine.head() + + const snapshot = this.engine.checkout(checkout) + if (snapshot === null) throw new Error('Invalid snapshot') + + return new HyperDB(this.engine, this.definition, { + versions: this.versions, + snapshot, + rootInstance: this.rootInstance, + writable: this.writable, + context + }) + } + // in future major, lets move transaction to be exclusive (aka sync) always async exclusiveTransaction(options) { await this.engine.enter() diff --git a/test/checkout.js b/test/checkout.js index ae8a905..dac321d 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -59,3 +59,43 @@ test.bee2('empty checkout', async function ({ create }, t) { await db.close() }) + +test.bee2('checkout session', async function ({ create }, t) { + const db = await create() + + await db.insert('@db/members', { id: 'a', age: 40 }) + await db.flush() + + const session = db.session({ timeout: 100 }) + t.is(session.engineSnapshot.snapshot.config.timeout, 100) + + await db.insert('@db/members', { id: 'b', age: 41 }) + await db.flush() + + t.ok(await session.get('@db/members', { id: 'a' })) + t.absent(await session.get('@db/members', { id: 'b' })) + + await session.insert('@db/members', { id: 'c', age: 42 }) + + t.ok(await session.get('@db/members', { id: 'c' })) + + t.ok(await db.get('@db/members', { id: 'a' })) + t.ok(await db.get('@db/members', { id: 'b' })) + t.absent(await db.get('@db/members', { id: 'c' })) + + await session.close() + await db.close() +}) + +test.bee2('checkout session timeout', async function ({ create }, t) { + const db = await create() + await db.ready() + + // checking out a block that doesnt exist yet + const session = db.session({ timeout: 100, length: db.core.length + 1 }) + + await t.exception(session.get('@db/members', { id: 'a' }), /REQUEST_TIMEOUT/) + + await session.close() + await db.close() +}) From e267f483baf175556cd285399f7766cd64ce87c6 Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Mon, 21 Sep 2026 10:37:57 -0400 Subject: [PATCH 2/9] add transaction timeout --- index.js | 17 +++++++++++++++-- test/checkout.js | 12 ++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 0fbee31..fa3e838 100644 --- a/index.js +++ b/index.js @@ -512,10 +512,23 @@ class HyperDB { } const context = (options && options.context) || this.context - const tx = this._createSnapshot(this, true, context) - tx.update() + if (options) { + const snapshot = this.engine.checkout({ ...this.engine.head(), ...options }) + if (snapshot === null) throw new Error('Invalid snapshot') + + return new HyperDB(this.engine, this.definition, { + versions: this.versions, + snapshot, + updates: this.updates.ref(), + rootInstance: this, + writable: true, + context + }) + } + const tx = this._createSnapshot(this, true, context) + tx.update() return tx } diff --git a/test/checkout.js b/test/checkout.js index dac321d..25c48f5 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -99,3 +99,15 @@ test.bee2('checkout session timeout', async function ({ create }, t) { await session.close() await db.close() }) + +test.bee2('transaction timeout', async function ({ create }, t) { + const db = await create() + await db.ready() + + const tx = db.transaction({ timeout: 100, length: db.core.length + 1 }) + + await t.exception(tx.get('@db/members', { id: 'a' }), /REQUEST_TIMEOUT/) + + await tx.close() + await db.close() +}) From be0e183c141c35f58b4b4935cd5de4e4c0b16f99 Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Wed, 30 Sep 2026 08:53:25 -0400 Subject: [PATCH 3/9] remove session, update supports timeout --- index.js | 49 ++++++++++------------------------------------ lib/engine/bee2.js | 4 ++-- 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/index.js b/index.js index fa3e838..083ec12 100644 --- a/index.js +++ b/index.js @@ -473,24 +473,6 @@ class HyperDB { return this._createSnapshot(this, false, context) } - session(options) { - maybeClosed(this) - - const context = (options && options.context) || this.context - const checkout = options ? { ...this.engine.head(), ...options } : this.engine.head() - - const snapshot = this.engine.checkout(checkout) - if (snapshot === null) throw new Error('Invalid snapshot') - - return new HyperDB(this.engine, this.definition, { - versions: this.versions, - snapshot, - rootInstance: this.rootInstance, - writable: this.writable, - context - }) - } - // in future major, lets move transaction to be exclusive (aka sync) always async exclusiveTransaction(options) { await this.engine.enter() @@ -513,22 +495,8 @@ class HyperDB { const context = (options && options.context) || this.context - if (options) { - const snapshot = this.engine.checkout({ ...this.engine.head(), ...options }) - if (snapshot === null) throw new Error('Invalid snapshot') - - return new HyperDB(this.engine, this.definition, { - versions: this.versions, - snapshot, - updates: this.updates.ref(), - rootInstance: this, - writable: true, - context - }) - } - const tx = this._createSnapshot(this, true, context) - tx.update() + tx.update(options) return tx } @@ -766,18 +734,21 @@ class HyperDB { await Promise.all(promises) } - update() { + update(options) { maybeClosed(this) - if (!this.engine.outdated(this.engineSnapshot)) return + const outdated = this.engine.outdated(this.engineSnapshot) + const snapshot = this.engine.snapshot(options || {}) - if (this.updates.refs > 1) this.updates = this.updates.detach() - this.updates.flush() + if (outdated) { + if (this.updates.refs > 1) this.updates = this.updates.detach() + this.updates.flush() + } this.engineSnapshot.unref() - this.engineSnapshot = this.engine.snapshot() + this.engineSnapshot = snapshot - if (this.watchers !== null) { + if (outdated && this.watchers !== null) { for (const fn of this.watchers) fn() } } diff --git a/lib/engine/bee2.js b/lib/engine/bee2.js index e5ab238..944a024 100644 --- a/lib/engine/bee2.js +++ b/lib/engine/bee2.js @@ -157,9 +157,9 @@ module.exports = class Bee2Engine { return stream } - snapshot() { + snapshot({ timeout } = {}) { this.snaps.inc() - return new BeeSnapshot(this.db.snapshot(), this._freeSnapBound) + return new BeeSnapshot(this.db.snapshot({ timeout }), this._freeSnapBound) } checkout(opts) { From b82c64fb833adb5670565b68afb1ad63f094c39e Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Wed, 30 Sep 2026 09:07:58 -0400 Subject: [PATCH 4/9] fix test --- test/checkout.js | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/test/checkout.js b/test/checkout.js index 25c48f5..ca36d02 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -60,43 +60,30 @@ test.bee2('empty checkout', async function ({ create }, t) { await db.close() }) -test.bee2('checkout session', async function ({ create }, t) { +test.bee2('transaction discards dry-run writes', async function ({ create }, t) { const db = await create() await db.insert('@db/members', { id: 'a', age: 40 }) await db.flush() - const session = db.session({ timeout: 100 }) - t.is(session.engineSnapshot.snapshot.config.timeout, 100) + const tx = db.transaction({ timeout: 100 }) + t.is(tx.engineSnapshot.snapshot.config.timeout, 100) await db.insert('@db/members', { id: 'b', age: 41 }) await db.flush() - t.ok(await session.get('@db/members', { id: 'a' })) - t.absent(await session.get('@db/members', { id: 'b' })) + t.ok(await tx.get('@db/members', { id: 'a' })) + t.absent(await tx.get('@db/members', { id: 'b' })) - await session.insert('@db/members', { id: 'c', age: 42 }) + await tx.insert('@db/members', { id: 'c', age: 42 }) - t.ok(await session.get('@db/members', { id: 'c' })) + t.ok(await tx.get('@db/members', { id: 'c' })) t.ok(await db.get('@db/members', { id: 'a' })) t.ok(await db.get('@db/members', { id: 'b' })) t.absent(await db.get('@db/members', { id: 'c' })) - await session.close() - await db.close() -}) - -test.bee2('checkout session timeout', async function ({ create }, t) { - const db = await create() - await db.ready() - - // checking out a block that doesnt exist yet - const session = db.session({ timeout: 100, length: db.core.length + 1 }) - - await t.exception(session.get('@db/members', { id: 'a' }), /REQUEST_TIMEOUT/) - - await session.close() + await tx.close() await db.close() }) @@ -104,7 +91,9 @@ test.bee2('transaction timeout', async function ({ create }, t) { const db = await create() await db.ready() - const tx = db.transaction({ timeout: 100, length: db.core.length + 1 }) + // Select an unavailable head; snapshot() itself does not accept a length. + db.db.move({ length: db.core.length + 1 }) + const tx = db.transaction({ timeout: 100 }) await t.exception(tx.get('@db/members', { id: 'a' }), /REQUEST_TIMEOUT/) From 29f4257f0a6fdca60778ee8eba4e47c042cf10b4 Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Wed, 30 Sep 2026 09:10:13 -0400 Subject: [PATCH 5/9] tidy --- test/checkout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/checkout.js b/test/checkout.js index ca36d02..5b64c85 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -91,7 +91,7 @@ test.bee2('transaction timeout', async function ({ create }, t) { const db = await create() await db.ready() - // Select an unavailable head; snapshot() itself does not accept a length. + // unavailable block will trigger timeout db.db.move({ length: db.core.length + 1 }) const tx = db.transaction({ timeout: 100 }) From b271889b9d4b3713b7cc05c800bdd2d56ae0967d Mon Sep 17 00:00:00 2001 From: Daniel Christopher Date: Wed, 30 Sep 2026 09:17:10 -0400 Subject: [PATCH 6/9] avoid snapshot churn --- index.js | 4 ++++ test/checkout.js | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/index.js b/index.js index 083ec12..9350f2d 100644 --- a/index.js +++ b/index.js @@ -738,6 +738,10 @@ class HyperDB { maybeClosed(this) const outdated = this.engine.outdated(this.engineSnapshot) + const hasTimeout = options && options.timeout !== undefined + + if (!outdated && !hasTimeout) return + const snapshot = this.engine.snapshot(options || {}) if (outdated) { diff --git a/test/checkout.js b/test/checkout.js index 5b64c85..43fe7b2 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -66,6 +66,13 @@ test.bee2('transaction discards dry-run writes', async function ({ create }, t) await db.insert('@db/members', { id: 'a', age: 40 }) await db.flush() + const snapshot = db.engineSnapshot + db.update() + t.is(db.engineSnapshot, snapshot, 'current update reuses the snapshot') + const ordinary = db.transaction() + t.is(ordinary.engineSnapshot, snapshot, 'ordinary transaction reuses the snapshot') + await ordinary.close() + const tx = db.transaction({ timeout: 100 }) t.is(tx.engineSnapshot.snapshot.config.timeout, 100) From 606565bb67e0b3b524577b4c81b35d0b3d746124 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Wed, 30 Sep 2026 14:17:32 -0500 Subject: [PATCH 7/9] Include snapshot options with HyperDB instance for bee2 session config Adds support for setting the session config options on the hyperbee2 engine while remaining generic. The options are stored on the HyperDB instance because of snapshots being updated during `.update()`. Engines now support a `snapshotFrom` method which takes the current snapshot and the options giving a new configured version. This happens only in `_createSnapshot()` as it is the shared way of creating the new view / hyperdb instance. `snapshotFrom()` is necessary to maintain the position of the current snapshot just with options. This all supports setting the session config not just for transactions, but also for calling `db.snapshot()` at the top level (aka on HyperDB instance). --- index.js | 39 ++++++++++++++++++++------------------- lib/engine/bee.js | 4 ++++ lib/engine/bee2.js | 22 ++++++++++++++++++++-- lib/engine/rocks.js | 4 ++++ test/checkout.js | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index 9350f2d..805855c 100644 --- a/index.js +++ b/index.js @@ -274,7 +274,8 @@ class HyperDB { definition, { versions = definition.versions, - snapshot = engine.snapshot(), + snapshotOptions = null, + snapshot = engine.snapshot(snapshotOptions), updates = new Updates(1, []), rootInstance = null, writable = true, @@ -282,6 +283,7 @@ class HyperDB { } = {} ) { this.versions = versions + this.snapshotOptions = snapshotOptions this.context = context this.index = 0 // for the session this.engine = engine @@ -453,11 +455,16 @@ class HyperDB { this.engine = null } - _createSnapshot(rootInstance, writable, context) { - const snapshot = this.engineSnapshot.ref() + _createSnapshot(rootInstance, writable, context, options) { + const opts = options ? this.engine.snapshotOptions(options) : null + const snapshotOptions = opts ? { ...this.snapshotOptions, ...opts } : this.snapshotOptions + const snapshot = opts + ? this.engine.snapshotFrom(this.engineSnapshot, snapshotOptions) + : this.engineSnapshot.ref() return new HyperDB(this.engine, this.definition, { versions: this.versions, + snapshotOptions, snapshot, updates: this.updates.ref(), rootInstance, @@ -470,7 +477,7 @@ class HyperDB { maybeClosed(this) const context = (options && options.context) || this.context - return this._createSnapshot(this, false, context) + return this._createSnapshot(this, false, context, options) } // in future major, lets move transaction to be exclusive (aka sync) always @@ -494,9 +501,10 @@ class HyperDB { } const context = (options && options.context) || this.context + const tx = this._createSnapshot(this, true, context, options) + + tx.update() - const tx = this._createSnapshot(this, true, context) - tx.update(options) return tx } @@ -734,25 +742,18 @@ class HyperDB { await Promise.all(promises) } - update(options) { + update() { maybeClosed(this) - const outdated = this.engine.outdated(this.engineSnapshot) - const hasTimeout = options && options.timeout !== undefined - - if (!outdated && !hasTimeout) return - - const snapshot = this.engine.snapshot(options || {}) + if (!this.engine.outdated(this.engineSnapshot)) return - if (outdated) { - if (this.updates.refs > 1) this.updates = this.updates.detach() - this.updates.flush() - } + if (this.updates.refs > 1) this.updates = this.updates.detach() + this.updates.flush() this.engineSnapshot.unref() - this.engineSnapshot = snapshot + this.engineSnapshot = this.engine.snapshot(this.snapshotOptions) - if (outdated && this.watchers !== null) { + if (this.watchers !== null) { for (const fn of this.watchers) fn() } } diff --git a/lib/engine/bee.js b/lib/engine/bee.js index 385ba71..d166db2 100644 --- a/lib/engine/bee.js +++ b/lib/engine/bee.js @@ -140,6 +140,10 @@ module.exports = class BeeEngine { return new BeeSnapshot(this.db.snapshot(), this._freeSnapBound) } + snapshotOptions() { + return null // not supported + } + outdated(snap) { return ( snap === null || diff --git a/lib/engine/bee2.js b/lib/engine/bee2.js index 944a024..9828238 100644 --- a/lib/engine/bee2.js +++ b/lib/engine/bee2.js @@ -7,6 +7,9 @@ const c = require('compact-encoding') const toChangesStream = require('../changes-stream.js') const Sessions = require('../sessions.js') +// hyperbee2 session config keys that snapshot views accept +const SNAPSHOT_OPTIONS = ['timeout', 'wait'] + class BeeSnapshot { constructor(snap, onfree) { this.refs = 1 @@ -157,9 +160,24 @@ module.exports = class Bee2Engine { return stream } - snapshot({ timeout } = {}) { + snapshotOptions(options) { + let opts = null + for (const k of SNAPSHOT_OPTIONS) { + if (options[k] === undefined) continue + if (opts === null) opts = {} + opts[k] = options[k] + } + return opts + } + + snapshot(opts) { + this.snaps.inc() + return new BeeSnapshot(this.db.snapshot(opts || {}), this._freeSnapBound) + } + + snapshotFrom(snap, opts) { this.snaps.inc() - return new BeeSnapshot(this.db.snapshot({ timeout }), this._freeSnapBound) + return new BeeSnapshot(snap.snapshot.snapshot(opts || {}), this._freeSnapBound) } checkout(opts) { diff --git a/lib/engine/rocks.js b/lib/engine/rocks.js index 16a1b34..1e19cd6 100644 --- a/lib/engine/rocks.js +++ b/lib/engine/rocks.js @@ -153,6 +153,10 @@ module.exports = class RocksEngine { return new RocksSnapshot(this.db.snapshot(), this.clock) } + snapshotOptions() { + return null // not supported + } + outdated(snap) { return snap === null || snap.clock !== this.clock } diff --git a/test/checkout.js b/test/checkout.js index 43fe7b2..cd8ebe1 100644 --- a/test/checkout.js +++ b/test/checkout.js @@ -107,3 +107,35 @@ test.bee2('transaction timeout', async function ({ create }, t) { await tx.close() await db.close() }) + +test.bee2('snapshot options keep the parent position', async function ({ create }, t) { + const db = await create() + + await db.insert('@db/members', { id: 'a', age: 40 }) + await db.flush() + + // pending write on root, then a commit past it leaves root outdated + await db.insert('@db/members', { id: 'p', age: 1 }) + const ahead = db.transaction() + await ahead.insert('@db/members', { id: 'b', age: 41 }) + await ahead.flush() + + const snap = db.snapshot({ timeout: 100, wait: false }) + t.is(snap.engineSnapshot.snapshot.config.timeout, 100) + t.is(snap.engineSnapshot.snapshot.config.wait, false) + t.absent(await snap.get('@db/members', { id: 'b' }), 'stays at root position') + + const child = snap.snapshot({ timeout: 200 }) + t.is(child.engineSnapshot.snapshot.config.timeout, 200) + t.is(child.engineSnapshot.snapshot.config.wait, false, 'inherits wait') + + const tx = db.transaction({ timeout: 100 }) + t.is(tx.updates.size, 0, 'drops stale pending writes') + t.ok(await tx.get('@db/members', { id: 'b' })) + t.is(tx.engineSnapshot.snapshot.config.timeout, 100, 'keeps options after update') + + await child.close() + await snap.close() + await tx.close() + await db.close() +}) From 2960191564b3cb0da49287cd889da487b6a72e40 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Wed, 30 Sep 2026 14:27:54 -0500 Subject: [PATCH 8/9] Relax snapshot shape to be amorphous IE the shape is dictated by the underlying engine module so should be driven by that instead of being enforced at the engine level. --- index.js | 5 ++--- lib/engine/bee.js | 4 ---- lib/engine/bee2.js | 17 ++--------------- lib/engine/rocks.js | 4 ---- 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index 805855c..825d6a0 100644 --- a/index.js +++ b/index.js @@ -274,7 +274,7 @@ class HyperDB { definition, { versions = definition.versions, - snapshotOptions = null, + snapshotOptions = {}, snapshot = engine.snapshot(snapshotOptions), updates = new Updates(1, []), rootInstance = null, @@ -455,8 +455,7 @@ class HyperDB { this.engine = null } - _createSnapshot(rootInstance, writable, context, options) { - const opts = options ? this.engine.snapshotOptions(options) : null + _createSnapshot(rootInstance, writable, context, opts) { const snapshotOptions = opts ? { ...this.snapshotOptions, ...opts } : this.snapshotOptions const snapshot = opts ? this.engine.snapshotFrom(this.engineSnapshot, snapshotOptions) diff --git a/lib/engine/bee.js b/lib/engine/bee.js index d166db2..385ba71 100644 --- a/lib/engine/bee.js +++ b/lib/engine/bee.js @@ -140,10 +140,6 @@ module.exports = class BeeEngine { return new BeeSnapshot(this.db.snapshot(), this._freeSnapBound) } - snapshotOptions() { - return null // not supported - } - outdated(snap) { return ( snap === null || diff --git a/lib/engine/bee2.js b/lib/engine/bee2.js index 9828238..c6620a2 100644 --- a/lib/engine/bee2.js +++ b/lib/engine/bee2.js @@ -7,9 +7,6 @@ const c = require('compact-encoding') const toChangesStream = require('../changes-stream.js') const Sessions = require('../sessions.js') -// hyperbee2 session config keys that snapshot views accept -const SNAPSHOT_OPTIONS = ['timeout', 'wait'] - class BeeSnapshot { constructor(snap, onfree) { this.refs = 1 @@ -160,24 +157,14 @@ module.exports = class Bee2Engine { return stream } - snapshotOptions(options) { - let opts = null - for (const k of SNAPSHOT_OPTIONS) { - if (options[k] === undefined) continue - if (opts === null) opts = {} - opts[k] = options[k] - } - return opts - } - snapshot(opts) { this.snaps.inc() - return new BeeSnapshot(this.db.snapshot(opts || {}), this._freeSnapBound) + return new BeeSnapshot(this.db.snapshot(opts), this._freeSnapBound) } snapshotFrom(snap, opts) { this.snaps.inc() - return new BeeSnapshot(snap.snapshot.snapshot(opts || {}), this._freeSnapBound) + return new BeeSnapshot(snap.snapshot.snapshot(opts), this._freeSnapBound) } checkout(opts) { diff --git a/lib/engine/rocks.js b/lib/engine/rocks.js index 1e19cd6..16a1b34 100644 --- a/lib/engine/rocks.js +++ b/lib/engine/rocks.js @@ -153,10 +153,6 @@ module.exports = class RocksEngine { return new RocksSnapshot(this.db.snapshot(), this.clock) } - snapshotOptions() { - return null // not supported - } - outdated(snap) { return snap === null || snap.clock !== this.clock } From faaa21c3b7ae21bd70c76ae35544e18da040e295 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Wed, 30 Sep 2026 14:42:51 -0500 Subject: [PATCH 9/9] Remove `context` from options passed for snapshot Add noop `snapshotFrom` to the other engines --- index.js | 10 +++++++++- lib/engine/bee.js | 4 ++++ lib/engine/rocks.js | 4 ++++ test/snapshot.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 825d6a0..ecbee70 100644 --- a/index.js +++ b/index.js @@ -455,7 +455,8 @@ class HyperDB { this.engine = null } - _createSnapshot(rootInstance, writable, context, opts) { + _createSnapshot(rootInstance, writable, context, options) { + const opts = engineOptions(options) const snapshotOptions = opts ? { ...this.snapshotOptions, ...opts } : this.snapshotOptions const snapshot = opts ? this.engine.snapshotFrom(this.engineSnapshot, snapshotOptions) @@ -789,6 +790,13 @@ function maybeClosed(db) { if (db.closing !== null) throw new Error('Hyperdb is closed') } +// strips hyperdb-level options, returns null if nothing is left for the engine +function engineOptions(options) { + if (!options) return null + const { context, ...opts } = options + return Object.keys(opts).length > 0 ? opts : null +} + function withinRange(range, key) { if (range.gte && b4a.compare(range.gte, key) > 0) return false if (range.gt && b4a.compare(range.gt, key) >= 0) return false diff --git a/lib/engine/bee.js b/lib/engine/bee.js index 385ba71..a583962 100644 --- a/lib/engine/bee.js +++ b/lib/engine/bee.js @@ -140,6 +140,10 @@ module.exports = class BeeEngine { return new BeeSnapshot(this.db.snapshot(), this._freeSnapBound) } + snapshotFrom(snap) { + return snap.ref() // snapshot options not supported + } + outdated(snap) { return ( snap === null || diff --git a/lib/engine/rocks.js b/lib/engine/rocks.js index 16a1b34..9549fba 100644 --- a/lib/engine/rocks.js +++ b/lib/engine/rocks.js @@ -153,6 +153,10 @@ module.exports = class RocksEngine { return new RocksSnapshot(this.db.snapshot(), this.clock) } + snapshotFrom(snap) { + return snap.ref() // snapshot options not supported + } + outdated(snap) { return snap === null || snap.clock !== this.clock } diff --git a/test/snapshot.js b/test/snapshot.js index 7df947a..e032382 100644 --- a/test/snapshot.js +++ b/test/snapshot.js @@ -123,3 +123,46 @@ test('root close waits for snapshot closes', async function ({ create }, t) { t.ok(snap.closed) t.ok(snapOfSnap.closed) }) + +test('context-only options share the parent snapshot', async function ({ create }, t) { + const db = await create() + + await db.insert('@db/members', { id: 'a', age: 40 }) + await db.flush() + + const context = {} + + const snap = db.snapshot({ context }) + t.is(snap.context, context) + t.is(snap.engineSnapshot, db.engineSnapshot, 'snapshot reuses the engine snapshot') + t.absent('context' in snap.snapshotOptions, 'context is not an engine option') + + const tx = db.transaction({ context }) + t.is(tx.context, context) + t.is(tx.engineSnapshot, db.engineSnapshot, 'transaction reuses the engine snapshot') + t.absent('context' in tx.snapshotOptions, 'context is not an engine option') + + await snap.close() + await tx.close() + await db.close() +}) + +test('passing snapshot options works on every engine', async function ({ create }, t) { + const db = await create() + + await db.insert('@db/members', { id: 'a', age: 40 }) + await db.flush() + + const snap = db.snapshot({ timeout: 1000 }) + t.alike(await snap.get('@db/members', { id: 'a' }), { id: 'a', age: 40 }) + + const tx = db.transaction({ timeout: 1000 }) + t.alike(await tx.get('@db/members', { id: 'a' }), { id: 'a', age: 40 }) + await tx.insert('@db/members', { id: 'b', age: 41 }) + await tx.flush() + + t.alike(await db.get('@db/members', { id: 'b' }), { id: 'b', age: 41 }) + + await snap.close() + await db.close() +})