Reject stale Blazor Virtualize viewport measurements#68691
Conversation
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
…e-base.yml (#68685) Bumps [dotnet/arcade/.github/workflows/inter-branch-merge-base.yml](https://github.com/dotnet/arcade) from acdb3e708ba600e766667825c84f9fa4a49e6c8f to 1353cab671305cff0ae5afc0d96ff3d03f239e0c. - [Commits](dotnet/arcade@acdb3e7...1353cab) --- updated-dependencies: - dependency-name: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml dependency-version: 1353cab671305cff0ae5afc0d96ff3d03f239e0c dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#68686) Bumps [dotnet/arcade/.github/workflows/backport-base.yml](https://github.com/dotnet/arcade) from acdb3e708ba600e766667825c84f9fa4a49e6c8f to 1353cab671305cff0ae5afc0d96ff3d03f239e0c. - [Commits](dotnet/arcade@acdb3e7...1353cab) --- updated-dependencies: - dependency-name: dotnet/arcade/.github/workflows/backport-base.yml dependency-version: 1353cab671305cff0ae5afc0d96ff3d03f239e0c dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
@copilot please keep working on this PR. The rendered-window ownership direction looks right, but keep it draft until these are addressed:
Please retain the all-producer ownership invariant: capture geometry plus version at measurement time, carry it through synchronous/deferred alignment and both spacer observers, and reject stale results before recalibration or redistribution. Keep the PR draft if any required control or repeated green run remains incomplete. |
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Addressed in Validation:
|
|
@copilot please keep working on this. I reproduced the current PR head
The filter runs both execution classes, so there are six assertions. All six fail on this head. Both QuickGrid cases end at The baseline evidence is materially different. Build 1562310 at
So the current result is a PR regression, not just the existing QuickGrid flake. Please do not weaken, quarantine, or remove these assertions. The strongest mechanism is in the observer producer. Please rework the IntersectionObserver path so every accepted tuple is truly from one committed window. My preferred correction is to treat the native entry as a notification only: in one synchronous block, read both current spacer versions and remeasure the current target rect, current viewport intersection, spacer separation, spacer size, and container size from the live DOM. Use only that current geometry with the current version, and ignore the notification if the target is disconnected or no longer intersects. Do not combine snapshotted entry rects with live DOM ownership. If you choose another mechanism, it must prove that the version was captured at the browser's observation epoch, not merely at callback delivery. Keep the cancellation-phase guard and all-producer managed checks. Add deterministic coverage for a render occurring between observer sampling and processing; the current managed unit tests cannot catch live-DOM relabeling of an old browser entry. Before calling this ready:
|
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
|
@copilot the stale observer correction is the right shape, and the original focused controls are now green locally on For a window-scrolled I froze one identical Jest assertion and ran it against both heads with
The identical final assertion SHA-256 was Can you keep the notification-only/live-DOM ownership fix, but derive After that, rerun the unchanged redistribution/QuickGrid filter three times and include the window-root test in the focused Jest run. Please also update the PR body with the corrected head and exact evidence before taking it out of draft. |
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Fixed at |
|
@copilot this head is not ready. Components E2E build 1563063 failed on both CoreCLR and Mono, and the failures point to three concrete gaps in the observer rewrite. First, I need to correct part of my previous guidance. The
The container is 300px and the observer has 50px root margin on each side. The old Second, apply the same effective viewport extent to the alignment producer. Third, don't skip the observer state machine when current remeasurement produces no intersecting targets. These are not theoretical-only failures. Build 1563063 regressed unchanged tests across both runtimes, including:
Please keep the notification-only same-epoch measurement fix, but address all three paths above. Then run the full Components E2E virtualization coverage, not only the six-test filter. The unchanged |
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
|
@copilot the latest head fixed the three issues from the last round, but one real Virtualize regression remains. Please keep working on this rather than retrying CI. On
It times out after ~39 seconds with
The regression starts with the notification-only rewrite. The callback stores only target identities in Please keep the same-epoch fix, but move the live remeasurement to IntersectionObserver callback delivery time rather than the later throttled dispatch. In other words, treat each native entry as a notification only, synchronously read current spacer versions and all current geometry in that callback, then queue the resulting coherent versioned measurement for throttled delivery. Do not use This preserves the existing throttle without dropping a notification because the viewport moved again before the timer fired. Please add deterministic coverage where a second observer callback arrives during the throttle window, its target moves before dispatch, and the measurement captured at callback delivery is still dispatched with its captured version. Also rerun The other red checks on this head are unrelated: the quarantined NavigationLock alert failure is tracked by #66041, and the aspnetcore-ci failure came from an HTTP/3 interop test followed by Helix upload-path noise. The cancellation failure is the one that still blocks this PR. |
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Addressed in |
|
The role of Edit: |
Reject stale Blazor Virtualize viewport measurements
Reject viewport measurements owned by an older rendered window
Description
Resize exposes stale geometry produced during near-end initial alignment: an asynchronous measurement for an older item slice can recalibrate and redistribute a newer slice. This change prevents cross-render measurement use; it does not expand Virtualize’s resize-anchor contract.
Rendered-window ownership
All-producer propagation
Regression coverage
InitialItemIndex=1990, and three bidirectional resize cycles.Evidence
de8ed19463ed91e5dc00824dc1b959d19abcb252: three red runs retained item 1950 but produced corrupted scroll heights of 96,879px, 99,692px, and 99,581px instead of 100,000px.638248695dd8e8c55399976d6622ebb1792abc69: the identical browser assertion passed once.VirtualizeTestrun passed 42/43; the existing cancellation test timed out once and passed immediately in isolation.Follow-up to #67936 and the empirical report.
Original prompt
Create and open a follow-up pull request in dotnet/aspnetcore that fixes stale asynchronous viewport measurements in Blazor Virtualize after merged PR #67936.
Repository and process requirements
Background
PR #67936, "Fix InitialItemIndex viewport underfill for small items in big container or on window resize," merged as commit fde48e9 from head 944cbbd.
The merged change fixes the original near-end viewport underfill, but it exposes a deterministic stale-measurement race during initial alignment.
Frozen real-browser repro from the exact merged head:
Observed on exact head 944cbbd:
Instrumented root cause
This is not a request to establish a new general contract that an exact scroll anchor must survive arbitrary container resizing. The correctness requirement is that geometry measured for an older rendered slice must never recalibrate or redistribute a newer rendered slice.
Required implementation invariant
Implement explicit all-producer rendered-window ownership:
A locally validated candidate on the pre-merge head used this approximate shape. Treat this as design evidence, not a patch to apply blindly. Reconcile it with current main and improve it if needed: