From 371a792cb62ce0ebf663031357165d60e505e7ec Mon Sep 17 00:00:00 2001 From: Caolan McMahon Date: Tue, 24 Mar 2026 17:02:53 +0000 Subject: [PATCH 1/5] Temporary workaround: register of in-progress getBlock requests --- lib/context.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/context.js b/lib/context.js index 76bea31..2354d8c 100644 --- a/lib/context.js +++ b/lib/context.js @@ -30,6 +30,7 @@ class CoreContext { this.opened = [] this.cores = [] this.changed = false + this.inProgress = new Map() } // TODO: remove, left here for easier debugging for now @@ -162,17 +163,28 @@ class CoreContext { return this.cores[index - 1].key } - async getBlock(seq, core, config) { - if (core !== 0 && core - 1 >= this.cores.length) await this.update(config) + getBlock(seq, core, config) { + if (!this.inProgress.has(core)) { + this.inProgress.set(core, new Map()) + } + if (this.inProgress.get(core).has(seq)) { + return this.inProgress.get(core).get(seq) + } + const promise = (async () => { + if (core !== 0 && core - 1 >= this.cores.length) await this.update(config) - const hc = this.getCore(core) - const buffer = await hc.get(seq, config) - if (buffer === null) throw BLOCK_NOT_AVAILABLE() + const hc = this.getCore(core) + const buffer = await hc.get(seq, config) + if (buffer === null) throw BLOCK_NOT_AVAILABLE() - if (config.trace !== null) config.trace(core, seq) + if (config.trace !== null) config.trace(core, seq) - const block = decodeBlock(buffer, seq) - return block + const block = decodeBlock(buffer, seq) + this.inProgress.get(core).delete(seq) + return block + })() + this.inProgress.get(core).set(seq, promise) + return promise } getLocalContext() { From 4cfbc688d7e5e1c337cc2eb294bab65f6580258e Mon Sep 17 00:00:00 2001 From: Caolan McMahon Date: Tue, 7 Apr 2026 14:21:51 +0100 Subject: [PATCH 2/5] Revert "Temporary workaround: register of in-progress getBlock requests" This reverts commit cfd4d80e0df122da918d0d7d9a4a0f16de605a66. --- lib/context.js | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/context.js b/lib/context.js index 2354d8c..76bea31 100644 --- a/lib/context.js +++ b/lib/context.js @@ -30,7 +30,6 @@ class CoreContext { this.opened = [] this.cores = [] this.changed = false - this.inProgress = new Map() } // TODO: remove, left here for easier debugging for now @@ -163,28 +162,17 @@ class CoreContext { return this.cores[index - 1].key } - getBlock(seq, core, config) { - if (!this.inProgress.has(core)) { - this.inProgress.set(core, new Map()) - } - if (this.inProgress.get(core).has(seq)) { - return this.inProgress.get(core).get(seq) - } - const promise = (async () => { - if (core !== 0 && core - 1 >= this.cores.length) await this.update(config) + async getBlock(seq, core, config) { + if (core !== 0 && core - 1 >= this.cores.length) await this.update(config) - const hc = this.getCore(core) - const buffer = await hc.get(seq, config) - if (buffer === null) throw BLOCK_NOT_AVAILABLE() + const hc = this.getCore(core) + const buffer = await hc.get(seq, config) + if (buffer === null) throw BLOCK_NOT_AVAILABLE() - if (config.trace !== null) config.trace(core, seq) + if (config.trace !== null) config.trace(core, seq) - const block = decodeBlock(buffer, seq) - this.inProgress.get(core).delete(seq) - return block - })() - this.inProgress.get(core).set(seq, promise) - return promise + const block = decodeBlock(buffer, seq) + return block } getLocalContext() { From e4edab30c2e2a3eba1d3e24e5bd418d6eee9bcc0 Mon Sep 17 00:00:00 2001 From: Caolan McMahon Date: Tue, 7 Apr 2026 13:50:25 +0100 Subject: [PATCH 3/5] Use pending inflate task when available --- lib/inflate.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/inflate.js b/lib/inflate.js index 8f5bcce..e307e0d 100644 --- a/lib/inflate.js +++ b/lib/inflate.js @@ -2,9 +2,21 @@ const b4a = require('b4a') const { Pointer, KeyPointer, ValuePointer, TreeNode } = require('./tree.js') const { DeltaOp, DeltaCohort, OP_COHORT } = require('./compression.js') +const pending = new WeakMap() + exports.inflate = async function inflate(ptr, config) { if (ptr.value) return ptr.value + if (pending.has(ptr)) return pending.get(ptr) + const promise = _inflate(ptr, config) + pending.set(ptr, promise) + try { + return await promise + } finally { + pending.delete(ptr) + } +} +async function _inflate(ptr, config) { const [block, context] = await Promise.all([ ptr.context.getBlock(ptr.seq, ptr.core, config), ptr.context.getContext(ptr.core, config) From 060cea610fc3e752147f24504f84284d474d93a9 Mon Sep 17 00:00:00 2001 From: Christophe Diederichs Date: Fri, 31 Jul 2026 14:37:54 +0100 Subject: [PATCH 4/5] move pending weakmap onto context --- lib/context.js | 1 + lib/inflate.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/context.js b/lib/context.js index 76bea31..05b652b 100644 --- a/lib/context.js +++ b/lib/context.js @@ -29,6 +29,7 @@ class CoreContext { this.checkpoint = 0 this.opened = [] this.cores = [] + this.pending = new WeakMap() this.changed = false } diff --git a/lib/inflate.js b/lib/inflate.js index e307e0d..f69bfa4 100644 --- a/lib/inflate.js +++ b/lib/inflate.js @@ -2,9 +2,8 @@ const b4a = require('b4a') const { Pointer, KeyPointer, ValuePointer, TreeNode } = require('./tree.js') const { DeltaOp, DeltaCohort, OP_COHORT } = require('./compression.js') -const pending = new WeakMap() - exports.inflate = async function inflate(ptr, config) { + const pending = ptr.context.pending if (ptr.value) return ptr.value if (pending.has(ptr)) return pending.get(ptr) const promise = _inflate(ptr, config) From 2000d8fec5590d414dfc727d5727ff2499daffaf Mon Sep 17 00:00:00 2001 From: Christophe Diederichs Date: Tue, 4 Aug 2026 09:58:21 +0100 Subject: [PATCH 5/5] store inflating promise on ptr --- lib/context.js | 1 - lib/inflate.js | 12 ++++++------ lib/tree.js | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/context.js b/lib/context.js index 05b652b..76bea31 100644 --- a/lib/context.js +++ b/lib/context.js @@ -29,7 +29,6 @@ class CoreContext { this.checkpoint = 0 this.opened = [] this.cores = [] - this.pending = new WeakMap() this.changed = false } diff --git a/lib/inflate.js b/lib/inflate.js index f69bfa4..6c80fb1 100644 --- a/lib/inflate.js +++ b/lib/inflate.js @@ -3,15 +3,15 @@ const { Pointer, KeyPointer, ValuePointer, TreeNode } = require('./tree.js') const { DeltaOp, DeltaCohort, OP_COHORT } = require('./compression.js') exports.inflate = async function inflate(ptr, config) { - const pending = ptr.context.pending if (ptr.value) return ptr.value - if (pending.has(ptr)) return pending.get(ptr) - const promise = _inflate(ptr, config) - pending.set(ptr, promise) + if (ptr.inflating) return ptr.inflating + + ptr.inflating = _inflate(ptr, config) + try { - return await promise + return await ptr.inflating } finally { - pending.delete(ptr) + ptr.inflating = null } } diff --git a/lib/tree.js b/lib/tree.js index e471063..f25b761 100644 --- a/lib/tree.js +++ b/lib/tree.js @@ -72,6 +72,8 @@ class TreeNodePointer extends Pointer { this.next = null this.prev = null + + this.inflating = null } commit() {