Skip to content

Make SharedString snapshot format selection sticky - #28370

Open
Tony Murphy (anthony-murphy) wants to merge 6 commits into
microsoft:mainfrom
anthony-murphy:anthony-murphy-sticky-string-snapshots
Open

Tony Murphy (anthony-murphy) wants to merge 6 commits into
microsoft:mainfrom
anthony-murphy:anthony-murphy-sticky-string-snapshots

Conversation

@anthony-murphy

@anthony-murphy Tony Murphy (anthony-murphy) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

How contribute to this repo.

Guidelines for Pull Requests.

Description

Make SharedString's flat MergeTree snapshot format opt-in and sticky across summaries and loads. Select the next DDS summary format using explicit configuration/runtime flag, recorded flag, then legacy default. Flat summaries persist newMergeTreeSnapshotFormat: true in the DDS .attributes; legacy summaries use undefined, so the serialized field is omitted. An explicit false overrides recorded true and clears the remembered flat setting when the DDS next summarizes naturally.

Use the same effective setting for writer selection and internal bookkeeping, with per-instance attributes to prevent settings leaking between DDSs. Do not dirty an unchanged DDS or force a summary when the setting changes. Leave both snapshot formats, their readers, SharedMatrix, and snapshotFormatVersion unchanged.

Add option documentation, a changeset, attached/detached unit coverage, four SharedString fuzz workloads for inherited settings and explicit overrides, and focused local-server summary/reload tests. DDS fuzz summaries capture the real .attributes blob after summarization and load those snapshot-time attributes through joining, detached rehydration, and stashing. Fuzz assertions observe summaries already generated by the harness rather than introducing extra summaries that advance live client state. The conflict-farm additions have been removed because they bypass SharedString's DDS attributes and format-selection logic.

Omitting the default legacy flag preserves the existing serialized attributes shape. The snapshot-normalizer workaround and its tests have been removed; golden fixtures remain unchanged.

Breaking Changes

None. Both formats remain supported, public API signatures are unchanged, and SharedString defaults to legacy when neither an explicit nor recorded setting exists. Previously recorded false values are still accepted when loading.

Reviewer Guidance

The review process is outlined in the pull request guidelines.

Please focus on explicit-false precedence, clearing versus preserving the serialized flag, per-instance attribute isolation, snapshot-time metadata preservation through fuzz client loads, and agreement between the emitted format and recorded setting. Format changes take effect only when the DDS is naturally summarized, including normal summary-handle reuse for unchanged channels.

Validation

The latest simplification was published before running checks, following the author's requested review workflow. The following checks passed on the current revision. Test commands used FLUID_TEST_MODULE_SYSTEM=ESM; fuzz commands also used FUZZ_STRESS_RUN=short.

  • ESM build: from the repository root, pnpm exec fluid-build --root . --task build:test:esm '^@fluidframework/sequence$' '^@fluidframework/merge-tree$' '^@fluidframework/tool-utils$' '^@fluid-private/test-end-to-end-tests$' '^@fluid-internal/test-snapshots$' — passed.
  • Precedence and persistence units: from packages\dds\sequence, with MOCHA_SPEC=lib\test\snapshotFormat.spec.js, npm run test:mocha:esm -- --reporter dot — 65 passed, including serialized omission of the legacy flag and clearing a recorded flat setting.
  • SharedString snapshot-format fuzz: from packages\dds\sequence, with MOCHA_SPEC=lib\test\fuzz\sharedString.snapshotFormat.fuzz.spec.js and FUZZ_TEST_COUNT=100, npm run test:mocha:esm -- --reporter dot — 400 passed (100 seeds per workload).
  • Local server: from packages\test\test-end-to-end-tests, with MOCHA_SPEC=lib\test\sharedStringSnapshotFormat.spec.js, npm run test:realsvc:local -- --compatKind=None --reporter dot — 2 passed, checking the actual attributes blob and summary content through reloads.
  • Golden snapshot comparison: from packages\test\snapshots, npm run test:mocha:esm -- --grep '^Snapshots writes snapshot in correct format$' --reporter dot — 1 passed, with zero mismatches across all current fixture datasets and no special normalization for the new flag.
  • Targeted ESLint and Biome checks on the changed source files — passed.
  • From the repository root, pnpm policy-check --path '^([.]changeset.sticky-shared-string-snapshot-format[.]md|packages.dds.(sequence|merge-tree)|packages.test.test-end-to-end-tests)' — passed.

On the preceding fuzz-harness revision, the existing SharedString fuzz workloads also passed 497 cases with 3 pre-existing skips using npm run test:mocha:esm -- --reporter dot with MOCHA_SPEC=lib\test\fuzz\sharedString.fuzz.spec.js and FUZZ_TEST_COUNT=100. The complete DDS fuzz harness passed 41 cases using npm run test:mocha:esm -- --grep '^DDS Fuzz Harness' --reporter dot from packages\dds\test-dds-utils.

Broader CI-readiness and API-report generation checks have not been run locally.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 23:49
@github-actions github-actions Bot added area: tools area: dds Issues related to distributed data structures area: repo Repo related work area: website area: dds: sharedstring area: tests Tests to add, test infrastructure improvements, etc changeset-present base: main PRs targeted against main branch labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Hi! Thank you for opening this PR. Want me to review it?

Based on the diff (788 lines, 11 files), I've queued these reviewers:

  • Correctness — logic errors, race conditions, lifecycle issues
  • Security — vulnerabilities, secret exposure, injection
  • API Compatibility — breaking changes, release tags, type design
  • Performance — algorithmic regressions, memory leaks
  • Testing — coverage gaps, hollow tests
  • Documentation / Developer Experience — missing or misleading docs, examples, and developer-facing guidance

How this works

  • Adjust the reviewer set by ticking/unticking boxes above. Reviewer toggles alone don't trigger anything.

  • Tick Start review below to dispatch the review fleet.

  • After review finishes, tick Start review again to request another run — it auto-resets after each dispatch.

  • This comment updates as new commits land; your reviewer selections are preserved.

  • Start review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Explicit overrides can be ignored for unchanged DDSs because the channel is not marked dirty.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Makes SharedString snapshot-format selection persistent while honoring explicit configuration overrides.

Changes:

  • Persists the effective snapshot format in per-instance DDS attributes.
  • Adds both-format unit, conflict-farm, and end-to-end coverage.
  • Documents the option and adds a minor changeset.
File Description
.changeset/​sticky-shared-string-snapshot-format.md Documents the behavior change.
packages/​dds/​merge-tree/​src/​mergeTree.ts Clarifies snapshot-option precedence.
packages/​dds/​merge-tree/​src/​test/​client.conflictFarm.spec.ts Runs conflict tests with both formats.
packages/​dds/​merge-tree/​src/​test/​testClient.ts Emits the selected test snapshot format.
packages/​dds/​sequence/​src/​intervalCollectionMapInterfaces.ts Adds the snapshot option to sequence options.
packages/​dds/​sequence/​src/​sequence.ts Implements sticky, per-instance format selection.
packages/​dds/​sequence/​src/​test/​snapshotFormat.spec.ts Tests precedence, persistence, and isolation.
packages/​test/​test-end-to-end-tests/​src/​test/​sharedStringSnapshotFormat.spec.ts Tests summary/reload behavior locally.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/dds/sequence/src/sequence.ts
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread packages/utils/tool-utils/src/snapshotNormalizer.ts Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: eabb9d3b1bb8dc4739847796dd387231ac513af4
Head commit: 6b469e53256fcc825bd4cfa43ef1efaa36a65947

Notable changes

No bundles changed by ≥ 500 bytes parsed.

Per-bundle deltas

@fluid-example/bundle-size-tests

  • fluidFrameworkAllAlpha.js: parsed 809701 → 810058 (+357), gzip 222862 → 222997 (+135)
  • azureClient.js: parsed 638881 → 638876 (-5), gzip 171229 → 171315 (+86)
  • odspClient.js: parsed 610840 → 610949 (+109), gzip 164171 → 164300 (+129)
  • aqueduct.js: parsed 537320 → 537634 (+314), gzip 144374 → 144470 (+96)
  • fluidFramework.js: parsed 417668 → 417701 (+33), gzip 118455 → 118511 (+56)
  • sharedTree.js: parsed 407047 → 407073 (+26), gzip 115905 → 115941 (+36)
  • containerRuntime.js: parsed 319226 → 319210 (-16), gzip 87551 → 87546 (-5)
  • sharedString.js: parsed 170105 → 170413 (+308), gzip 48456 → 48520 (+64)
  • experimentalSharedTree.js: parsed 161846 → 161846 (0), gzip 46722 → 46722 (0)
  • matrix.js: parsed 153720 → 153727 (+7), gzip 44381 → 44388 (+7)
  • loader.js: parsed 147328 → 147344 (+16), gzip 40039 → 40049 (+10)
  • odspDriver.js: parsed 106728 → 106786 (+58), gzip 33236 → 33304 (+68)
  • directory.js: parsed 65669 → 65676 (+7), gzip 18493 → 18502 (+9)
  • 578.js: parsed 58686 → 58686 (0), gzip 17657 → 17657 (0)
  • odspPrefetchSnapshot.js: parsed 46463 → 46444 (-19), gzip 15512 → 15522 (+10)
  • map.js: parsed 45820 → 45827 (+7), gzip 14120 → 14127 (+7)
  • 252.js: parsed 44384 → 44384 (0), gzip 13741 → 13741 (0)
  • summarizerDelayLoadedModule.js: parsed 31287 → 31287 (0), gzip 7929 → 7929 (0)
  • socketModule.js: parsed 27108 → 27078 (-30), gzip 8067 → 8103 (+36)
  • createNewModule.js: parsed 12464 → 12464 (0), gzip 4792 → 4805 (+13)
  • summaryModule.js: parsed 3888 → 3888 (0), gzip 1874 → 1874 (0)
  • connectionState.js: parsed 909 → 909 (0), gzip 500 → 500 (0)
  • sharedTreeAttributes.js: parsed 845 → 852 (+7), gzip 496 → 505 (+9)
  • debugAssert.js: parsed 429 → 429 (0), gzip 299 → 299 (0)
  • FluidFramework-HashFallback.js: parsed 419 → 419 (0), gzip 313 → 313 (0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: dds: sharedstring area: dds Issues related to distributed data structures area: repo Repo related work area: tests Tests to add, test infrastructure improvements, etc area: tools area: website base: main PRs targeted against main branch changeset-present

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants