From 5485d588ee88217cae48b8f6d887ed9ab9f92812 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Fri, 27 Feb 2026 11:05:47 -0600 Subject: [PATCH 1/4] Add test w/ named session as example of clearing w/ deps failing --- test/sessions.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/sessions.js b/test/sessions.js index cf4f0aa16..12f71765f 100644 --- a/test/sessions.js +++ b/test/sessions.js @@ -175,4 +175,29 @@ test('sessions - checkout breaks prologue', async function (t) { uncaughts.off(noop) }) +test('named session doesnt clear wrong blocks', async (t) => { + const core = await create(t) + + const num = 10 + for (let i = 0; i < num; i++) { + await core.append('block' + i) + } + + t.ok(await core.has(num - 1), 'parent has block') + const named = core.session({ name: 'batch' }) + await named.ready() + + const clearEnd = num - 2 + + t.ok(await named.has(num - 1), 'named starts w/ block') + await t.execution(() => named.clear(1, clearEnd), 'clear runs') + t.absent(await named.has(1), 'named cleared start of range') + t.absent(await named.has(clearEnd), 'named cleared end of range') + t.ok(await named.has(num - 1), 'named ends w/ block') + + t.ok(await core.has(1), 'core has start of cleared range') + t.ok(await core.has(clearEnd), 'core has end of cleared range') + t.ok(await core.has(num - 1), 'core ends w/ block') +}) + function noop() {} From 02ab8a620084442a32ce0c02fb61f54dc8d7e7e6 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Fri, 27 Feb 2026 13:56:47 -0600 Subject: [PATCH 2/4] Copy blocks from prev dep before updating length when clearing The dependency length is truncated to the start of the clear range because the dependency's full length can no longer be used (as the session has now diverged in what blocks it has etc). But the remaining blocks need to be copied if there are indexes after the clear range end. --- lib/session-state.js | 17 ++++++++++++++++- test/sessions.js | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/session-state.js b/lib/session-state.js index 9c0a41541..1288a3d3f 100644 --- a/lib/session-state.js +++ b/lib/session-state.js @@ -541,7 +541,22 @@ module.exports = class SessionState { if (end - start === 1) tx.deleteBlock(start) else tx.deleteBlockRange(start, end) - const dependency = start < this.flushedLength() ? updateDependency(this, start, true) : null + const flushedLength = this.flushedLength() + const dependency = start < flushedLength ? updateDependency(this, start, true) : null + + // Dependency was reset but the end doesn't include all blocks + if (dependency && end < flushedLength - 1) { + const blocksPromises = [] + const rx = this.storage.read() + for (let i = end + 1; i < flushedLength; i++) { + blocksPromises.push(rx.getBlock(i)) + } + rx.tryFlush() + const blocks = await Promise.all(blocksPromises) + for (let i = end + 1; i < flushedLength; i++) { + tx.putBlock(i, blocks[i]) + } + } const flushed = await this.flush() diff --git a/test/sessions.js b/test/sessions.js index 12f71765f..073854e86 100644 --- a/test/sessions.js +++ b/test/sessions.js @@ -193,11 +193,26 @@ test('named session doesnt clear wrong blocks', async (t) => { await t.execution(() => named.clear(1, clearEnd), 'clear runs') t.absent(await named.has(1), 'named cleared start of range') t.absent(await named.has(clearEnd), 'named cleared end of range') + t.ok(await named.has(clearEnd + 1), 'named didnt clear beyond end') t.ok(await named.has(num - 1), 'named ends w/ block') t.ok(await core.has(1), 'core has start of cleared range') t.ok(await core.has(clearEnd), 'core has end of cleared range') t.ok(await core.has(num - 1), 'core ends w/ block') + + t.comment('clearing to end') + const namedToEnd = core.session({ name: 'batch-to-end' }) + await namedToEnd.ready() + + t.ok(await namedToEnd.has(num - 1), 'starts w/ end block') + await t.execution(() => namedToEnd.clear(1, num - 1), 'clear runs') + + { + const rx = namedToEnd.state.storage.read() + const b = rx.getBlock(num - 1) + rx.tryFlush() + t.absent(await b, 'didnt put blocks beyond end in storage only') + } }) function noop() {} From e644b8cf7f09255d56d1a4e59f6472c1a5ce599d Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Fri, 27 Feb 2026 14:47:31 -0600 Subject: [PATCH 3/4] Close sessions in test for clearing session w/ deps --- test/sessions.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/sessions.js b/test/sessions.js index 073854e86..a061a0c13 100644 --- a/test/sessions.js +++ b/test/sessions.js @@ -213,6 +213,9 @@ test('named session doesnt clear wrong blocks', async (t) => { rx.tryFlush() t.absent(await b, 'didnt put blocks beyond end in storage only') } + + await named.close() + await namedToEnd.close() }) function noop() {} From 9dccdf9558a1cdda26796f448d1d84e2c4c72387 Mon Sep 17 00:00:00 2001 From: Sean Zellmer Date: Fri, 27 Feb 2026 15:32:58 -0600 Subject: [PATCH 4/4] Use separate index when reading blocks to copy them --- lib/session-state.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/session-state.js b/lib/session-state.js index 1288a3d3f..9c4c11c04 100644 --- a/lib/session-state.js +++ b/lib/session-state.js @@ -553,8 +553,10 @@ module.exports = class SessionState { } rx.tryFlush() const blocks = await Promise.all(blocksPromises) + let j = 0 for (let i = end + 1; i < flushedLength; i++) { - tx.putBlock(i, blocks[i]) + tx.putBlock(i, blocks[j]) + j++ } }