Skip to content

Update ResilientFunctions to the CreateAndStart lifecycle and fix wrapper suspension - #13

Merged
stidsborg merged 1 commit into
mainfrom
create-and-start-adaptation
Jul 29, 2026
Merged

Update ResilientFunctions to the CreateAndStart lifecycle and fix wrapper suspension#13
stidsborg merged 1 commit into
mainfrom
create-and-start-adaptation

Conversation

@stidsborg

Copy link
Copy Markdown
Owner

Submodule update

Moves the ResilientFunctions submodule to latest main (5491be3b), which includes PR #221: FunctionsRegistry's constructor is private and the static CreateAndStart factories are the only public construction path, so background processing (cluster membership, message delivery, crash/postponed recovery) never runs before registration setup completes.

FlowsContainer follows the same shape

  • Private constructor + async CreateAndStart factories (Create renamed accordingly; parameter names preserved for named-argument callers).
  • The DI registration resolves the container through a singleton factory that blocks once during host start-up - mirroring the old constructor's synchronous replica-watchdog initialization.
  • All new FlowsContainer(...) / FlowsContainer.Create(...) call sites across tests, samples and service buses migrated.

Bug fix: wrapper flows could never suspend

Flows<...> registered through the internal Register* overloads taking Func<..., Task<Result<T>>>. Those overloads register the inner as-is - only the public overloads wrap the body in WithSuspension, the race that produces Suspend/Postpone results since the core's central-suspension redesign (core #204). The wrapper's catch (SuspendInvocationException) blocks compensated for exception-based suspension that no longer exists, so any wrapper flow awaiting a message sat Executing forever - no exception, no warning.

Minimal repro (same flow body, one line difference):

// public overload -> Suspended
async (string _, Workflow w) => { await w.Message<OrderConfirmed>(); }

// internal overload (what the wrapper did) -> Executing forever
async (string _, Workflow w) => { await w.Message<OrderConfirmed>(); return new Result<Unit>(Unit.Instance); }

Fix: the wrapper registers plain Task-returning inners through the public overloads and its hand-rolled exception-to-result catch blocks are deleted - the core adapter owns suspension racing and exception conversion.

Other core-drift adaptations

  • FlowOptions/FlowsContainer no longer reference the removed EnableWatchdogs/DelayStartup settings; MapToLocalSettings matches the new LocalSettings shape.
  • Flows.Interrupt removed, mirroring the interrupt-API deletion in the push-only core.
  • SuspendInvocationException usages removed (type deleted in core).
  • OptionsTests appends its message via the registration MessageWriter instead of the control panel - control-panel appends against a live flow race the owner's effect-version guard.

Testing

  • Solution builds clean.
  • Cleipnir.Tests: 25/25 passed.
  • Cleipnir.Tests.AspNet: 8/8 passed.
  • Service-bus test projects compile but need live brokers, so they were not executed.

🤖 Generated with Claude Code

…lows wrapper

The submodule moves to latest main (PR #221), where FunctionsRegistry's
constructor is private and the static CreateAndStart factories are the only
public construction path - guaranteeing background processing never starts
before construction completes.

FlowsContainer follows the same shape: private constructor plus async
CreateAndStart factories (the Create helper is renamed accordingly). The DI
registration resolves the container through a factory that blocks once during
host start-up, mirroring the old constructor's synchronous replica-watchdog
initialization.

Adaptations to the intervening core changes:

- Flows<...> now registers through the public Register* overloads with plain
  Task-returning inners. The previous internal-overload registration bypassed
  the core's WithSuspension wrapper, and since the central-suspension redesign
  (core #204) removed exception-based suspension, wrapper flows awaiting a
  message were stuck Executing forever instead of suspending. The wrapper's
  own exception-to-result catch blocks are deleted - the core adapter owns
  that conversion now.
- SuspendInvocationException catches removed (type deleted; suspension no
  longer surfaces as an exception).
- FlowOptions/FlowsContainer no longer reference the removed EnableWatchdogs
  and DelayStartup settings; MapToLocalSettings matches the new LocalSettings
  shape.
- Flows.Interrupt removed, mirroring the interrupt API deletion in the core.
- OptionsTests appends its message via the registration MessageWriter instead
  of the control panel - control-panel appends against a live flow race the
  owner's effect-version guard.
@stidsborg
stidsborg merged commit cf25de9 into main Jul 29, 2026
1 check passed
@stidsborg
stidsborg deleted the create-and-start-adaptation branch July 29, 2026 09:56
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.

1 participant