Skip to content

[release/10.0] Backport fix for handling of expired client-persisted circuit state#67453

Open
oroztocil wants to merge 2 commits into
release/10.0from
backport/pr-64798-to-release/10.0
Open

[release/10.0] Backport fix for handling of expired client-persisted circuit state#67453
oroztocil wants to merge 2 commits into
release/10.0from
backport/pr-64798-to-release/10.0

Conversation

@oroztocil

@oroztocil oroztocil commented Jun 29, 2026

Copy link
Copy Markdown
Member

Backport of #64798 to release/10.0

Fixes #67257

Description

Blazor Server supports pausing a circuit (the serialized state is handed to the client) and later resuming it. When the client tries to resume after the client-held serialized state has expired (its lifetime is bounded by PersistedCircuitInMemoryRetentionPeriod / PersistedCircuitDistributedRetentionPeriod), the resume path failed without proper handling.

Problem analysis:

  • During resume, the deserialization of the persisted root component descriptors happened late, inside UpdateRootComponents. If the persisted markers had expired, that produced a null operation batch and threw a NullReferenceException. The circuit was left half-initialized, so the page appeared restored but was frozen, and clicking any interactive element produced a silent "There is no event handler associated with this event." error. There was no error surfaced and no clean way to detect or recover.
  • The expiration window for the client-held markers was also miscalculated, so they could expire earlier than expected.

Fix:

  • Move deserialization of the root component descriptors earlier, from UpdateRootComponents to ResumeCircuit, so the server can validate the state up front. If it is invalid or expired, resume fails explicitly and the client falls back to its reconnection/reload logic instead of entering the frozen state.
  • Fix the calculation of the expiration date for the client-persisted state.
  • Add E2E coverage for the expired-state scenario and update existing tests for the refactor.

Customer Impact

Users are hitting this in production. After a tab is left idle past the retention period, resuming returns the user to an apparently normal page that is actually frozen: interactive elements do nothing and there is often no error at all, so the failure is unexpected and very hard to diagnose.

There is no clean client-side workaround: the issue cannot be fixed from JavaScript because the full fix requires the server-side change in this backport, and the suggested mitigations are hacky and unreliable.

Multiple customers are reporting and asking for a .NET 10 backport: #64607, #67257 (tracking the backport request), and #64228 (a different reconnection issue whose later comments describe this exact pause/resume freeze, with customers confirming there is no usable workaround).

Regression?

  • Yes
  • No

This is a user-facing behavior that did not exist prior to .NET 10 (introduced with circuit pause/resume persistence).

Risk

  • High
  • Medium
  • Low

The change touches the circuit resume path. Risk is mitigated: the fix has been released in .NET 11 since Preview 1 with no bug reports filed against it, and all changes are internal (no public API changes).

Verification

  • Manual (required)
  • Automated

New E2E tests reproduce both the happy path (resume restores state and stays interactive) and the exact issue (resume with expired state reloads cleanly instead of freezing). Verified manually as well.

Packaging changes reviewed?

  • Yes
  • No
  • N/A
@oroztocil oroztocil changed the title [release/10.0] Backport fix for expiration handling of client-persisted circuit state Jun 29, 2026
@oroztocil oroztocil requested a review from Copilot June 29, 2026 08:07

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.

Pull request overview

Backports a Blazor Server circuit-resume fix to better handle expired/invalid client-persisted circuit state by validating persisted root component data during resume and hard-failing invalid resume/update flows so the client can recover (e.g., via reload).

Changes:

  • Add resume-time validation/deserialization of persisted root component descriptors and attach a resumed-only persisted state model.
  • Harden UpdateRootComponents to terminate/abort the circuit when persisted state is invalid.
  • Add/adjust E2E + unit tests and a test-server knob to force persisted-circuit expiration for coverage.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs Adds a configuration-driven retention period override to force persisted state expiry during tests.
src/Components/test/E2ETest/ServerExecutionTests/ServerResumeWithExpiredStateTest.cs New E2E coverage for client pause/resume when client-held persisted state is expired.
src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionWithoutStateTest.cs Refactors reconnection/pause UI handling to align with the new resume behavior and custom UI mode.
src/Components/Server/test/Circuits/ComponentHubTest.cs Updates test data to provide valid (empty) root components payload for resume validation.
src/Components/Server/test/Circuits/CircuitRegistryTest.cs Adjusts test to use the new resumed persisted state type.
src/Components/Server/test/Circuits/CircuitPersistenceManagerTest.cs Updates unit tests for new ToRootComponentOperationBatch signature and behavior.
src/Components/Server/src/ComponentHub.cs Validates persisted root component data on resume; terminates circuit when persisted resume/update data is invalid.
src/Components/Server/src/Circuits/ResumedPersistedCircuitState.cs Introduces a resumed-only persisted state container holding application state + root component descriptors.
src/Components/Server/src/Circuits/CircuitPersistenceManager.cs Splits descriptor validation/deserialization from operation-batch creation; fixes retention selection logic.
src/Components/Server/src/Circuits/CircuitHost.cs Switches persisted-state attachment from PersistedCircuitState to ResumedPersistedCircuitState.
Comment thread src/Components/Server/src/Circuits/CircuitPersistenceManager.cs
Comment thread src/Components/Server/src/Circuits/CircuitPersistenceManager.cs
@oroztocil oroztocil added area-blazor Includes: Blazor, Razor Components Servicing-consider Shiproom approval is required for the issue labels Jun 29, 2026
@oroztocil oroztocil added this to the 10.0.x milestone Jun 29, 2026
@oroztocil oroztocil self-assigned this Jun 29, 2026
@oroztocil oroztocil marked this pull request as ready for review June 29, 2026 10:27
@oroztocil oroztocil requested a review from a team as a code owner June 29, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components Servicing-consider Shiproom approval is required for the issue

3 participants