Skip to content

Add application summary projection callback - #28319

Draft
Vlad Sudzilouski (vladsud) wants to merge 1 commit into
microsoft:mainfrom
vladsud:user/vladsud/summary-projection-callback
Draft

Vlad Sudzilouski (vladsud) wants to merge 1 commit into
microsoft:mainfrom
vladsud:user/vladsud/summary-projection-callback

Conversation

@vladsud

Copy link
Copy Markdown
Contributor

Description

Applications sometimes need to contribute their own projection of data into the container summary — for example, a denormalized or application-format representation of content that is otherwise stored inside a DDS, produced incrementally alongside the runtime's ordinary summarize flow.

This PR adds a small, self-contained extension point for that: an optional applicationSummaryProjection callback on LoadContainerRuntimeParams. When provided, ContainerRuntime invokes it during summarization and inserts the returned subtree alongside its own summary roots (.channels, .metadata, etc.), tracks acceptance against summary acks/nacks, and calls back into the application (onAccepted) once a generated summary is durably accepted. Unchanged content can be returned as an ISummaryHandle pointing at the previous summary so it isn't re-serialized/re-uploaded every summary cycle.

This is intentionally scoped down from the larger prototype in #28280 (which also handles detached-construction options like fullTreePolicy and GC-adoption-correlation retry logic). Only the "register callback → contribute a subtree during summarize → correlate acceptance with the right summary" capability is included here; the detached-construction pieces are left as potential future follow-up work if/when needed.

New API surface (@fluidframework/container-runtime, @legacy @beta)

  • IApplicationSummaryProjection — the callback contract (key, summarize).
  • IApplicationSummaryProjectionContext — passed to summarize; exposes the previously-accepted projection (if any) for incremental reuse.
  • IApplicationSummaryProjectionResult — returned from summarize; the projected ISummaryTree plus an optional onAccepted callback.
  • LoadContainerRuntimeParams.applicationSummaryProjection — new optional field wiring the callback into ContainerRuntime.loadRuntime/loadRuntime2.

Tests and example

  • src/test/applicationSummaryProjection.spec.ts — unit tests for the controller in isolation (key validation, handle-without-previous-summary rejection, onAccepted rejection for promise-like values, pruning of stale pending generations, multi-cycle generate → submit → ack tracking).
  • src/test/summaryProjectionExample/ — a documented, runnable example (projectionSample.ts + projectionExample.spec.ts + README.md) demonstrating incremental reuse across two independently-projected blobs (left/right): after an unchanged second summary, one blob is emitted as a handle while the other remains a fresh blob. The README explicitly separates "sample code" (usable close to as-is by consumers) from "test-harness code" (written only to drive/assert the sample in this repo's test suite).

Reviewer Guidance

The review process is outlined in the pull request guidelines.

  • containerRuntime.ts is the highest-risk file touched (widely used, heavily tested core class). The new capability is wired in via a new public method, initializeApplicationSummaryProjection(), called once right after construction in loadRuntime2, rather than adding a new constructor parameter — this was a deliberate choice to avoid touching the already very long constructor parameter list that other packages (e.g. mixinAttributor) depend on. Feedback on whether this is the right integration seam vs. an alternative is welcome.
  • Start review at src/summary/applicationSummaryProjection.ts for the core contract/controller, then src/test/summaryProjectionExample/README.md for the intended consumer-facing usage pattern.
  • This PR was extracted from a larger prototype (Application projections: deterministic loading and incremental summaries #28280) at the request of the PR author to land as an independent, minimal, well-documented capability first.

Adds an optional applicationSummaryProjection callback to
LoadContainerRuntimeParams, letting applications contribute an
incrementally-reusable subtree to the container summary alongside
the runtime's own summary roots. Includes unit tests and a
documented, runnable example (summaryProjectionExample) demonstrating
incremental reuse across two independently-projected blobs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added area: tools area: runtime Runtime related issues area: repo Repo related work area: website public api change Changes to a public API changeset-present base: main PRs targeted against main branch labels Sep 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

Based on the diff (865 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

@github-actions

Copy link
Copy Markdown
Contributor

🔗 No broken links found! ✅

Your attention to detail is admirable.

linkcheck output

$ start-server-and-test "npm run serve -- --host 127.0.0.1 --no-open" http://127.0.0.1:3000 check-links
1: starting server using command "npm run serve -- --host 127.0.0.1 --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> fluid-framework-website@0.0.0 serve
> docusaurus serve --host 127.0.0.1 --no-open

[SUCCESS] Serving "build" directory at: http://127.0.0.1:3000/

> fluid-framework-website@0.0.0 check-links
> linkcheck http://127.0.0.1:3000 --skip-file skipped-urls.txt

Crawling...

Stats:
  658102 links
    3579 destination URLs
    3898 URLs ignored
       0 warnings
       0 errors


@github-actions

Copy link
Copy Markdown
Contributor

Bundle size comparison

Base commit: 6b08531dfee2e3fb97fd1bdfff1b5445b020a55e
Head commit: 9e7e933f46f724d0246442edea39bc9a14cbd1b2

Notable changes

  • 🔴 azureClient.js: parsed 634639 → 638328 (+3689), gzip 170109 → 171114 (+1005)
  • 🔴 odspClient.js: parsed 606600 → 610403 (+3803), gzip 163053 → 164093 (+1040)
  • 🔴 aqueduct.js: parsed 533117 → 536829 (+3712), gzip 143294 → 144258 (+964)
  • 🔴 containerRuntime.js: parsed 315027 → 318705 (+3678), gzip 86438 → 87360 (+922)
Per-bundle deltas

@fluid-example/bundle-size-tests

  • fluidFrameworkAllAlpha.js: parsed 801454 → 801510 (+56), gzip 220511 → 220583 (+72)
  • 🔴 azureClient.js: parsed 634639 → 638328 (+3689), gzip 170109 → 171114 (+1005)
  • 🔴 odspClient.js: parsed 606600 → 610403 (+3803), gzip 163053 → 164093 (+1040)
  • 🔴 aqueduct.js: parsed 533117 → 536829 (+3712), gzip 143294 → 144258 (+964)
  • fluidFramework.js: parsed 415378 → 415411 (+33), gzip 117808 → 117845 (+37)
  • sharedTree.js: parsed 404757 → 404783 (+26), gzip 115250 → 115271 (+21)
  • 🔴 containerRuntime.js: parsed 315027 → 318705 (+3678), gzip 86438 → 87360 (+922)
  • sharedString.js: parsed 170105 → 170112 (+7), gzip 48455 → 48462 (+7)
  • 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 106695 → 106753 (+58), gzip 33227 → 33293 (+66)
  • 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 8069 → 8103 (+34)
  • 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)

@markfields Mark Fields (markfields) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[release-hold:client:3.3.0] Process hold to avoid release conflicts; not code-review feedback. Will dismiss after the version bump.

@markfields
Mark Fields (markfields) dismissed their stale review September 30, 2026 22:24

Client 3.3.0 release hold lifted: 3.4.0 bump #28355 merged. Not code-review feedback.

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

Labels

area: repo Repo related work area: runtime Runtime related issues area: tools area: website base: main PRs targeted against main branch changeset-present public api change Changes to a public API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants