From 2d27bd01afbc1d0ee235fe2134000998fa97cba2 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Tue, 2 Dec 2025 13:23:18 -0800 Subject: [PATCH] fix(storage): populate cache before write buffer for read-after-write consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloud storage adapters (GCS, S3, R2, Azure) use write buffers in high-volume mode to batch network operations. However, the cache was not being populated when items were added to the buffer, causing add() to return successfully but immediate relate() calls to fail with "Source entity not found". This fix ensures the cache is populated BEFORE adding to the write buffer, guaranteeing read-after-write consistency even when writes are buffered for asynchronous flushing. Affected adapters: - GcsStorage: saveNode, saveEdge - S3CompatibleStorage: saveNode - R2Storage: saveNode, saveEdge - AzureBlobStorage: saveNode, saveEdge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/storage/adapters/azureBlobStorage.ts | 11 +++++++++++ src/storage/adapters/gcsStorage.ts | 13 +++++++++++++ src/storage/adapters/r2Storage.ts | 10 ++++++++++ src/storage/adapters/s3CompatibleStorage.ts | 8 ++++++++ 4 files changed, 42 insertions(+) diff --git a/src/storage/adapters/azureBlobStorage.ts b/src/storage/adapters/azureBlobStorage.ts index 5aae6ae2..9b5c80dc 100644 --- a/src/storage/adapters/azureBlobStorage.ts +++ b/src/storage/adapters/azureBlobStorage.ts @@ -531,6 +531,13 @@ export class AzureBlobStorage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.nounWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding noun ${node.id} to write buffer (high-volume mode active)`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + // Without this, add() returns but relate() can't find the entity (cloud storage production bug) + if (node.vector && Array.isArray(node.vector) && node.vector.length > 0) { + this.nounCacheManager.set(node.id, node) + } + await this.nounWriteBuffer.add(node.id, node) return } else if (!this.highVolumeMode) { @@ -1033,6 +1040,10 @@ export class AzureBlobStorage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.verbWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding verb ${edge.id} to write buffer`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + this.verbCacheManager.set(edge.id, edge) + await this.verbWriteBuffer.add(edge.id, edge) return } diff --git a/src/storage/adapters/gcsStorage.ts b/src/storage/adapters/gcsStorage.ts index 877733e4..d699e008 100644 --- a/src/storage/adapters/gcsStorage.ts +++ b/src/storage/adapters/gcsStorage.ts @@ -442,6 +442,14 @@ export class GcsStorage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.nounWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding noun ${node.id} to write buffer (high-volume mode active)`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + // Without this, add() returns but relate() can't find the entity (GCS production bug) + // The buffer flushes asynchronously, but cache ensures immediate reads succeed + if (node.vector && Array.isArray(node.vector) && node.vector.length > 0) { + this.nounCacheManager.set(node.id, node) + } + await this.nounWriteBuffer.add(node.id, node) return } else if (!this.highVolumeMode) { @@ -833,6 +841,11 @@ export class GcsStorage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.verbWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding verb ${edge.id} to write buffer`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + // Without this, relate() might not find the verb immediately after creation + this.verbCacheManager.set(edge.id, edge) + await this.verbWriteBuffer.add(edge.id, edge) return } diff --git a/src/storage/adapters/r2Storage.ts b/src/storage/adapters/r2Storage.ts index 7ee051c5..7a025773 100644 --- a/src/storage/adapters/r2Storage.ts +++ b/src/storage/adapters/r2Storage.ts @@ -486,6 +486,13 @@ export class R2Storage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.nounWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding noun ${node.id} to write buffer`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + // Without this, add() returns but relate() can't find the entity (cloud storage production bug) + if (node.vector && Array.isArray(node.vector) && node.vector.length > 0) { + this.nounCacheManager.set(node.id, node) + } + await this.nounWriteBuffer.add(node.id, node) return } @@ -759,6 +766,9 @@ export class R2Storage extends BaseStorage { this.checkVolumeMode() if (this.highVolumeMode && this.verbWriteBuffer) { + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + this.verbCacheManager.set(edge.id, edge) + await this.verbWriteBuffer.add(edge.id, edge) return } diff --git a/src/storage/adapters/s3CompatibleStorage.ts b/src/storage/adapters/s3CompatibleStorage.ts index 37f1b1da..19e0011d 100644 --- a/src/storage/adapters/s3CompatibleStorage.ts +++ b/src/storage/adapters/s3CompatibleStorage.ts @@ -1069,6 +1069,14 @@ export class S3CompatibleStorage extends BaseStorage { // Use write buffer in high-volume mode if (this.highVolumeMode && this.nounWriteBuffer) { this.logger.trace(`📝 BUFFERING: Adding noun ${node.id} to write buffer (high-volume mode active)`) + + // v6.2.6: CRITICAL FIX - Populate cache BEFORE buffering for read-after-write consistency + // Without this, add() returns but relate() can't find the entity (cloud storage production bug) + // The buffer flushes asynchronously, but cache ensures immediate reads succeed + if (node.vector && Array.isArray(node.vector) && node.vector.length > 0) { + this.nounCacheManager.set(node.id, node) + } + await this.nounWriteBuffer.add(node.id, node) return } else if (!this.highVolumeMode) {