Skip to content

feat(journaling): add state operation observers - #11280

Closed
ReubenBond wants to merge 5 commits into
dotnet:mainfrom
ReubenBond:add-journaling-state-observers
Closed

ReubenBond wants to merge 5 commits into
dotnet:mainfrom
ReubenBond:add-journaling-state-observers

Conversation

@ReubenBond

@ReubenBond ReubenBond commented Sep 16, 2026 •

Copy link
Copy Markdown
Member

Problem

Journaled features need to prepare fallible work within the serialized write boundary, capture safe effects and completion state together, and stop outstanding external work when the journal manager is terminally fenced.

Solution and rationale

Add IJournaledStateObserver and a backward-compatible default IJournaledStateManager.RegisterObserver implementation. Unique observers register before initialization. Write/delete request guards validate each caller before admission; the work loop awaits all write preparations, then all finalizations, then runs start notifications and state capture synchronously. Later queued writes wait for the active operation. Successful logical writes include zero-byte boundaries.

Shared activation setup resolves activation-scoped observers after grain construction and assignment, before lifecycle startup. The standard grain-bound manager constructor enrolls its instance before resolution returns, and observers can register after a grain constructor has resolved that manager while journal initialization is still pending. This supports ordinary grains, application-defined base classes, injected state, and the optional DurableGrain convenience base. Custom implementations establish one enrollment owner in their constructor or registration factory. Explicit-JournalId factory-created managers retain caller-owned initialization and disposal; scoped factories which assign them a grain lifecycle explicitly enroll them before returning.

Initial replay reports restored state before initialization completes. An admitted operation failure uses the terminal manager lifecycle: the original failure is recorded, optional OnFaulted(Exception) notifications run exactly once, and owning-grain deactivation and queued/failing operation completion follow. Notification exceptions are logged and isolated; request admission vetoes reject only the unadmitted request. Idle shutdown completes normally, while cancellation during admitted preparation, finalization, or persistence remains terminal. Standalone owners recreate the manager and state instances for the same journal to recover the actual durable outcome.

Adapt the observer prerequisite extracted from #10693 to the terminal recovery contract merged in #11276. The observer API uses initial replay notifications and terminal fault visibility, with operation-local staging owned by each consumer. The generic asynchronous phase hooks preserve synchronous validate/apply in exclusively owned feature state. Callbacks complete independently of further operations on the same manager.

Focused regressions cover once-only fault notification before admitted/queued callers complete, original-failure preservation when notifications fail, serialization across suspended preparation, effects-plus-completion capture, caller-wait cancellation, ordinary idle shutdown, optional default compatibility, and explicit-JournalId provider-bound recovery. Composition cases verify scoped observer identity, initial replay, write phases, terminal outcomes, and fresh recovery across plain, application-base, injected-state, and convenience-base grains. Hosting, implementation registration, and explicit-ID factories verify their exact enrollment ownership and cleanup. XML docs and the generated API surface describe the lifecycle.

Dependency and review boundary

#11279 has landed on main as 0f9537dde1c10107d059fc10f1171f6f04a43f19. This PR is rebased onto that pinned main commit, which includes grain-context-constructor-owned lifecycle enrollment, shared activation setup from #11305, and the earlier Journaling/Jobs foundations.

This PR targets dotnet/orleans:main and contains only the five observer commits across six files. Review the observer-only layer using the immutable incremental compare. Named-provider registration and Jobs migration are inherited from main. Merging remains a human decision.

Microsoft Reviewers: Open in CodeFlow

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

🔵 Needs a closer look

Two moderate correctness issues remain, along with one documentation nit.

Review effort: Lite
Findings: None

What changed in this PR

Adds durable state operation observers and grain-scoped journaling participant initialization.

Changes:

  • Adds observer and participant contracts with lifecycle integration.
  • Implements callback ordering, recovery fencing, and notification logging.
  • Adds focused tests and regenerates the public API.
File Description
test/​Orleans.Journaling.Tests/​StateManagerTests.cs Covers observer behavior and recovery boundaries.
test/​Orleans.Journaling.Tests/​JournaledGrainParticipantTests.cs Covers participant initialization and activation.
src/​Orleans.Journaling/​JournaledStateManager.cs Implements observer callbacks and recovery fencing.
src/​Orleans.Journaling/​IJournaledStateObserver.cs Defines the observer contract.
src/​Orleans.Journaling/​IJournaledStateManager.cs Adds observer registration.
src/​Orleans.Journaling/​IJournaledGrainParticipant.cs Defines the participant contract.
src/​Orleans.Journaling/​DurableGrain.cs Initializes registered participants.
src/​api/​Orleans.Journaling/​Orleans.Journaling.cs Updates generated API metadata.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ReubenBond
ReubenBond marked this pull request as ready for review September 16, 2026 21:09
@github-actions

github-actions Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage

Metric Pull request Current main Variance
Lines 82.24% (112,626 / 136,945) 82.18% (112,440 / 136,814) +0.0572 pp
Branches 71.51% (32,474 / 45,413) 71.47% (32,435 / 45,385) +0.0418 pp

Report-only conclusion: improved.

The current-main baseline is commit d515d75eaa and uses the same reviewed coverage matrix.

Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities.

The comparison remains report-only while normal line and branch variance is calibrated.

Coverage details

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

🔵 Needs a closer look

Validate null observers before throwing NotSupportedException in the default registration implementation.

Review effort: Lite
Findings: None

Copilot AI review requested due to automatic review settings September 17, 2026 00:22
@ReubenBond
ReubenBond force-pushed the add-journaling-state-observers branch from bdcc9c4 to 885fce7 Compare September 17, 2026 00:22

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

🔵 Needs a closer look

The changes affect durable operation boundaries, recovery, and observer lifecycle behavior; final human review is recommended.

Review effort: Lite
Findings: None

@ReubenBond
ReubenBond force-pushed the add-journaling-state-observers branch from 885fce7 to aaa76d6 Compare September 17, 2026 03:09
Copilot AI review requested due to automatic review settings September 17, 2026 03:09

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

The default registration implementation must validate null observers as documented.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Comment thread src/Orleans.Journaling/IJournaledStateManager.cs Outdated
Copilot AI review requested due to automatic review settings September 17, 2026 03:23

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

🔵 Needs a closer look

The lifecycle and terminal-failure changes require final human review.

Review effort: Lite
Findings: None

Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 17, 2026 07:08

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

🔵 Needs a closer look

The lifecycle and terminal-failure changes require final human review.

Review effort: Lite
Findings: None

Resolved since last review (1)

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

🔵 Needs a closer look

The lifecycle, persistence, and terminal-failure behavior require final human review.

Review effort: Lite
Findings: None

Copilot AI review requested due to automatic review settings September 17, 2026 23:02
@ReubenBond
ReubenBond force-pushed the add-journaling-state-observers branch from f001c9f to f1f4fd4 Compare September 17, 2026 23:02

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

🔵 Needs a closer look

The broad lifecycle, recovery, and observer behavior requires final human review.

Review effort: Lite
Findings: None

@ReubenBond

Copy link
Copy Markdown
Member Author

Superseded by the published replacement chain #11326 -> #11282 -> #11284 -> #11285 -> #10693 (final consumer head 375389b).

Durable Messaging now uses asynchronous handler preparation, synchronous application, and real journaled inbox/outbox state for write readiness, capture, acknowledgement, recovery, deletion, and terminal-failure handling. Ordinary Journaling write/delete calls remain the persistence boundary. The separate observer API, registration, and consumer dependency have been removed from the replacement chain.

Final integration passed all 526 cases on native .NET 8 and .NET 10 with zero failures or skips, including all 12 provider-cutover cases. Generated API, normal Release packaging, compiled snippets, and documentation validation also passed. Historical commits and checkpoints remain preserved.

Closing this proposal without merging it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants