Skip to content

Commit 8880de3

Browse files
kriszypclaude
andcommitted
feat: make typed random-access structures opt-in (default off)
Typed structures keyed the per-encoder dictionary on per-field value WIDTH, which caused two production failure modes: an unbounded structure explosion (OOM) on wide/variably-typed schemas, and cross-replica/thread dictionary divergence (decode failures: "Record id is not defined" / "Unknown constant"). Classic shared structures are width-agnostic and bounded, and were the behavior that ran reliably pre-4.7.13. Disable typed-struct WRITES by default in RecordEncoder (new records use classic shared structures); keep READS (_readStruct stays active) so existing typed-struct data and cross-version replication still decode. Opt in per encoder via options.randomAccessStructure. Bail the write hook via () => 0 rather than clearing it, to preserve msgpackr's struct-safe fixint boundary. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent dae3940 commit 8880de3

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

resources/RecordEncoder.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export class RecordEncoder extends StructonEncoder {
8888
rootStore: any;
8989
declare saveStructures: any;
9090
declare getStructures: any;
91+
declare _writeStruct: any;
9192
structureUpdate?: any;
9293
isRocksDB: boolean;
9394
name: string;
@@ -110,6 +111,16 @@ export class RecordEncoder extends StructonEncoder {
110111

111112
options.structPrototype = RecordObject.prototype;
112113
super(options);
114+
// Typed random-access structures are opt-in as of 5.1. They keyed structures on per-field
115+
// value WIDTH, so a wide/variably-typed schema minted an unbounded dictionary (OOM), and
116+
// the per-encoder dictionary diverges across replicas/threads (decode failures). Disable
117+
// WRITES by default — new records use classic shared structures (bounded, width-agnostic).
118+
// Keep READS (_readStruct stays) so existing typed-struct data and cross-version
119+
// replication still decode. Bail via () => 0 (not clearing the hook) to preserve msgpackr's
120+
// struct-safe fixint boundary. Opt in per encoder with options.randomAccessStructure.
121+
if (!options.randomAccessStructure && this._writeStruct) {
122+
this._writeStruct = () => 0;
123+
}
113124
const superEncode = this.encode;
114125
this.encode = function (record, options?) {
115126
// this handles our custom metadata encoding, prefixing the record with metadata, including the local

0 commit comments

Comments
 (0)