fix(driver-utils): backport hasCompressionMarkup recursion fix for 2.82.1 (#27624)#27630
Conversation
… → snapshot.trees[key] (microsoft#27624) ## The bug `hasCompressionMarkup()` in `DocumentStorageServiceCompressionAdapter` tries to recursively search the snapshot tree for the `.metadata.blobHeaders` compression markup blob. The recursion was silently broken: ```ts for (const key of Object.keys(snapshot.trees)) { - const value = snapshot[key] as ISnapshotTree; // always undefined + const value = snapshot.trees[key]; // correct ``` `ISnapshotTree` stores subtrees in `snapshot.trees`, not as direct properties on the snapshot object. `snapshot[key]` is **always `undefined`**, so the loop body never executes and `hasCompressionMarkup()` only ever checks root-level blobs. This meant `_isCompressionEnabled` stayed `false` when the `.metadata.blobHeaders` markup blob was nested inside a DDS subtree, causing `readBlob()` to return raw LZ4-compressed bytes that then hit `JSON.parse` → **"Failed to get Channel: … is not valid JSON"**. ## Why it was latent until Fluid 2.82 Before Fluid 2.82, the `.metadata` blob (which determines where `putCompressionMarkup` places the markup) was at the summary root. `hasCompressionMarkup()` found it at root level without any recursion — the bug didn't matter. With Fluid 2.82 + SharedTree, `versionedSummarizer.ts` writes its own `.metadata` blob inside the DDS subtree (`sheetSetData/`). `findMetadataHolderSummary` finds this nested `.metadata` first (DFS), so `putCompressionMarkup` places `.metadata.blobHeaders` inside `sheetSetData/` — now the broken recursion matters and `_isCompressionEnabled` is never set. The bug has been present since microsoft#15656 and is in every release through `client_v2.102.0` and current HEAD. ## The fix One functional line changed. No behaviour change for backends where `.metadata.blobHeaders` is at the root level (existing test fixtures and Azure FRS). ## Verification Tested against Autodesk's cedit/routerlicious backend (developer-dev ring, `fluid` tenant) with a 2-client SharedTree + AWSClient repro across `routerlicious-client` v4.0.3–v4.0.7 (Fluid 2.72–2.82): - **Without fix:** v4.0.4–v4.0.7 reproduce "Failed to get Channel" (`compress=1/2, decompress=0`) - **With this fix only:** all 5 versions pass (`compress=1/2, decompress≥1`) Also confirmed: PropertyDDS never triggers the bug (its summarizer writes no `.metadata` blob in the DDS subtree, so `.metadata.blobHeaders` always lands at root). 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> (cherry picked from commit d639a8b)
|
Warning WARNING: This PR is targeting a release branch! All changes must first be merged into Changes to release branches require approval from the Patch Triage group before merging. For more details, see our internal documentation for the patch policy and processes for |
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (5 lines, 1 files), I've queued these reviewers:
How this works
|
|
|
@agarwal-navin |
|
This is targeting a no longer supported release. 2.110.0 is the current supported release branch for fluid framework client, which could be patched theoretically, but just waiting for today's 2.111.0 will be faster. Additionally, we only do patches for regressions. I don't think this change was a regression, but just a general fix which should wait to come out in the next release. |
Summary
Backports the fix from #27624 to
release/client/2.82for the upcoming 2.82.1 patch release.hasCompressionMarkup()inDocumentStorageServiceCompressionAdapterusedsnapshot[key]instead ofsnapshot.trees[key]when recursing intoISnapshotTreesubtrees.ISnapshotTreesubtrees live under.trees, not as direct properties, sosnapshot[key]was alwaysundefinedand the recursion silently never ran past the root level.This became user-visible starting with Fluid 2.82 + SharedTree, where
versionedSummarizer.tscan write a nested.metadatablob inside a DDS subtree. WhenputCompressionMarkupplaces.metadata.blobHeadersthere,hasCompressionMarkup()fails to find it,_isCompressionEnabledstaysfalse, andreadBlob()returns raw LZ4-compressed bytes that failJSON.parsewith"Failed to get Channel: … is not valid JSON".One functional line changed; no behavior change for backends where
.metadata.blobHeadersis at the summary root (existing test fixtures, Azure FRS).See #27624 for full repro details and verification against Autodesk's routerlicious backend.
Test plan
main(d639a8b) with no conflicts