Repository navigation
fix(odsp-driver): remove invalid byteOffset === 0 assumption - #28194
Conversation
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (18 lines, 2 files), I've queued these reviewers:
How this works
|
There was a problem hiding this comment.
🟢 Approval recommended
The change removes a demonstrably unnecessary assertion and is covered by a focused regression test that exercises the previously failing non-zero-offset Uint8Array scenario.
Pull request overview
Removes an invalid compact snapshot parsing restriction in the ODSP driver that incorrectly asserted the input Uint8Array must have byteOffset === 0, and adds a regression test to ensure parsing works correctly when given a valid non-zero-offset view into a larger backing buffer.
Changes:
- Removed the
byteOffset === 0assertion from the compact snapshot string-loading path. - Added a regression test that loads a serialized snapshot from a
Uint8Array.subarray()with a non-zerobyteOffsetand validates the parsed tree matches.
File summaries
| File | Description |
|---|---|
| packages/drivers/odsp-driver/src/zipItDataRepresentationUtils.ts | Removes the incorrect byteOffset === 0 assumption from loadStrings() so non-zero-offset Uint8Array views are accepted. |
| packages/drivers/odsp-driver/src/test/zipItDataRepresentationTests.spec.ts | Adds a targeted test that constructs a non-zero-offset Uint8Array view and verifies TreeBuilder.load() produces an equivalent tree. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Are you confident that the offset change with the Node upgrade is not indicative of a bigger issue? I'm surprised a Node change would effect this, but also not my expertise area. |
Bundle size comparisonBase commit: Notable changesNo bundles changed by ≥ 500 bytes parsed. Per-bundle deltas
|
Jason Hartman (jason-ha)
left a comment
There was a problem hiding this comment.
I see no reason that assert was ever added. At least from my understanding of how the code works a non-zero offset was never problematic. Perhaps there was just a misunderstanding of the logic when originally written.
We discussed this offline a bit but the explanation to why this upgrade (Node 24.18 increasing the Buffer pool from 8 KiB to 64 KiB) caused a problem as I understand it is this: |
## Description Adds the missing changeset for the compact snapshot parsing fix in [PR #28194](#28194). Although the implementation change is internal, it fixes a consumer-visible compatibility issue where valid compact snapshots could fail to load with pooled buffers returned by Node.js 24.18 and later.
…soft#28203) ## Description Adds the missing changeset for the compact snapshot parsing fix in [PR microsoft#28194](microsoft#28194). Although the implementation change is internal, it fixes a consumer-visible compatibility issue where valid compact snapshots could fail to load with pooled buffers returned by Node.js 24.18 and later.
Description
Fixes compact snapshot parsing when the input is a valid
Uint8Arrayview with a non-zerobyteOffset.The ODSP compact snapshot parser asserted that its input must begin at offset zero:
The affected snapshots were valid, but their binary data was represented by a view into a larger backing buffer. The assertion closed the Fluid container during loading and caused eight of the 29 Office eDiscovery fixtures to fail with assertion
0x3e8.Update: Why this started failing now
The assertion has existed since 2022, but the relevant inputs previously happened to have a zero
byteOffset, leaving the invalid assumption hidden.The failure started after Node.js 24.18.0 increased the default
Buffer.poolSizefrom 8 KiB to 64 KiB. Node uses this pool forBuffer.allocUnsafe()allocations smaller than half the pool size. This increased the pooling threshold from approximately 4 KiB to 32 KiB.The Office eDiscovery export path reads binary snapshots using:
Node's
fs.readFileSync()allocates the returnedBufferusingBuffer.allocUnsafe(fileSize). A NodeBufferis also aUint8Arrayview.Before Node 24.18:
byteOffset === 0.Beginning with Node 24.18:
byteOffset.Node 24.20 and 24.21 inherited this behavior. This explains why the failure appeared when the Office External Partner pipeline moved from Node 24.11 to newer Node 24 releases.
The Office External Partner integration pipeline selected Node using this floating range:
The range selects the newest available matching Node 24 release, not the lowest version in the range. Normal Office CI did not initially reproduce the issue because its managed build image still provided Node 24.11.0.
Controlled runs confirmed the Node version as the differentiating variable:
Node did not introduce malformed snapshot data or change the
Uint8Arraycontract. The larger buffer pool caused more file reads to use valid offset views, exposing Fluid's existing assumption about the backing-buffer layout.Pinning Node 24.11 would avoid the expanded pooling threshold and can serve as a temporary mitigation. However, it would leave Fluid dependent on an allocation detail that Node does not guarantee. Other consumers can also provide valid non-zero-offset
Uint8Arrayviews independently of Node's file-reading behavior.Root cause
The assertion was originally added by Vlad Sudzilouski in #12058 as part of a string-parsing performance optimization. The PR discusses null-character handling and differences between browser and Node string decoding, but it does not document why a zero byte offset was required.
The parser does not require a zero-offset backing buffer:
Uint8Arrayindexing is relative to the view.Uint8Array.subarray()is relative to the view.ReadBufferpositions and reads are relative to the suppliedUint8Array.BlobShallowCopyhandling explicitly accounts fordata.byteOffset.For example, if a view begins at offset 100 in its backing buffer,
input[0]still reads the first byte of that view, not byte zero of the backing buffer. The string positions recorded by the parser are also relative to that same view.A non-zero offset is therefore a supported memory layout rather than evidence of a malformed or corrupted snapshot. The assertion rejected valid input without providing additional snapshot-integrity protection.
This change removes that assertion. It does not remove validation of snapshot structure, string lengths, tree markers, skipped ranges, or unexpected end-of-file conditions.
Copying the input into a new zero-offset buffer would also avoid the assertion, but it would introduce an unnecessary full-snapshot allocation and preserve the incorrect parser restriction.
Changes
byteOffset === 0assertion from compact snapshot string loading.subarray()toTreeBuilder.load().byteOffset.Reproduction and integration validation
The issue was isolated using controlled Office External Partner pipeline runs:
0x3e8The first two runs used the same Fluid build and Office test code, with only the Node version changed. This confirmed that the larger buffer pool in newer Node versions exposes the existing parser assumption.
The final run restored the triggering Node 24.21.0 environment and used Fluid build 422948 containing this change. All 29 eDiscovery tests passed, including the eight fixtures that previously failed. The overall integration pipeline also succeeded.
The ODSP driver build completed successfully, including compilation, linting, API checks, and test compilation. The Tree Representation test suite passed in both ESM and CommonJS configurations.
A Fluid Real Service End to End Tests run was also queued using client build 422948 to exercise the change against an actual ODSP tenant:
Reviewer Guidance
Please focus on whether any compact snapshot parsing operation depends on the backing buffer starting at offset zero.
The relevant string positions are relative to the supplied
Uint8Arrayview, and the added regression test exercises the parser using a non-zero-offset view and compares the fully parsed result with the original tree.The original assertion author was Vlad Sudzilouski (
vladsud) in #12058. The original PR does not contain an explanation of a required zero-offset invariant.