Reactor identity and renaming
A reactor’s id identifies its observer and processing position. By default, the id is the full type name, including its namespace. Moving a reactor to another namespace or renaming its class therefore creates a different observer unless you preserve the id explicitly.
Keep identity stable
Section titled “Keep identity stable”For a side-effecting reactor, choose an explicit id before its first deployment. That lets you reorganize code without changing its processing identity. This declaration excerpt leaves your existing handlers unchanged:
using Cratis.Chronicle.Reactors;
// For an already deployed reactor, use its existing stored observer id here.[Reactor("MyApp.Orders.SendWelcomeEmail")]public class SendWelcomeEmail : IReactor{ // Keep the existing event handlers unchanged.}For a reactor that is already deployed, use its current stored id, not a new friendly name. If its default id was MyApp.Orders.SendWelcomeEmail, pin that exact value before moving or renaming the class. Adding an explicit id with a different value is itself an identity change.
Why side effects run again
Section titled “Why side effects run again”A new observer catches up from the beginning of the event sequence as ordinary observation, not replay. Its handlers can send every historical notification or repeat every external write. OnceOnly excludes replay; it does not suppress this initial catch-up. See What OnceOnly guarantees.
Preserving the old id keeps its processing position. It does not provide exactly-once execution: recovery can still re-deliver an event, so keep side effects idempotent.
Find and remove abandoned observers
Section titled “Find and remove abandoned observers”Use the Workbench’s Observers page or cratis chronicle observers list to compare stored ids with the ids your application declares. Inspect the old observer before deleting anything: another service or namespace may still use it.
If the identity change was accidental, restore the old id in the reactor declaration. Removing the old observer does not transfer its position to the new one or undo side effects the new one has already performed.
If the old observer is genuinely abandoned, stop all clients that subscribe to it, then remove it by its stored id:
var result = await eventStore.Observers.Remove("the-observer");if (!result.IsRemoved){ Console.WriteLine($"Not removed: {result.Outcome} in namespace {result.BlockingNamespace}");}Removal affects the whole event store, across namespaces, and refuses while the observer is running or subscribed anywhere. See Removing an observer.
The .NET discovery diagnostics report likely identity changes before registration. Treat findings as evidence to investigate, not proof: a rolling deployment can hide a rename while the old instance is still subscribed.