Class RegistrationOutcome
- Namespace
- Cratis.Chronicle.Registrations
- Assembly
- Cratis.Chronicle.dll
Represents what became of the client's declared artifacts when RegisterAll() ran.
public record RegistrationOutcome : IEquatable<RegistrationOutcome>
- Inheritance
-
RegistrationOutcome
- Implements
- Inherited Members
- Extension Methods
Remarks
This exists so that a consumer holding an IEventStore can tell three states apart that used to look identical from outside: every artifact registered, some subset registered and the rest were dropped, and registration has not run yet. A read model that cannot be built is isolated and logged rather than thrown, which is right - one unbuildable read model must not cost the whole read side - but it turns a loud total failure into a quiet partial one. This is the read-only seam for asking about it afterwards; it is not a failure policy, and nothing here decides whether a failed artifact should abort start-up, fail a spec, or be tolerated. That is the consumer's call.
What it covers. Projection artifacts - both the fluent IProjectionFor<TReadModel> implementations and the model-bound read models. Those are the artifacts whose registration round-trips to the kernel inside RegisterAll(), so an outcome for them is something the client can actually observe rather than assume. IsRegistered means the client built a definition for the artifact and the kernel accepted the batch it travelled in; the registration operation itself returns no per-definition verdict, so a finer-grained claim than that would be invented. For newly registered active projections, success also means the kernel subscribed their observers in the event store's existing namespaces. It does not mean an event appended afterwards is already reflected in materialized read models. For read-your-writes, use WaitForCompletion(IAppendResultForObserverCompletion, TimeSpan?) on the append result before reading, or use a passive read model.
What it deliberately does not cover: reactors and reducers. Their Register() only opens a duplex
stream - it marks itself registered and returns before any round trip to the kernel has happened. Reporting them
here would mean reporting a hope as a fact, so they are left out rather than represented optimistically. A consumer
that needs to know a reactor or reducer is live should wait on its observer state instead
(ReactorWaitExtensions.WaitTillSubscribed, ReducerWaitExtensions.WaitTillActive), which is answered by
the kernel.
Do not reach for IsConnected instead of this. It looks like it
carries a registration verdict and it partly does - ConnectionLifecycle.Connected() rolls it back to
false and rethrows when one of the connected handlers failed, and
RegisterAll() is one of those handlers. But it is set to true
before the handlers run, and only rolled back after they have all finished, so polling it races:
it reads true while registration is still in flight and while a registration that is about to be
reported as failed is still running. It answers "connected", which is necessary and not sufficient. Use
Registration and its IsRegistering flag instead.
Constructors
RegistrationOutcome(RegistrationOutcome)
protected RegistrationOutcome(RegistrationOutcome original)
Parameters
originalRegistrationOutcome
RegistrationOutcome(bool, IImmutableList<ArtifactRegistration>, Exception?)
Represents what became of the client's declared artifacts when RegisterAll() ran.
public RegistrationOutcome(bool HasRun, IImmutableList<ArtifactRegistration> Artifacts, Exception? Failure = null)
Parameters
HasRunboolWhether RegisterAll() has finished at least once, successfully or not.
ArtifactsIImmutableList<ArtifactRegistration>The declared artifacts, each carrying whether it registered and, if not, the failure that stopped it.
FailureExceptionThe Exception that stopped RegisterAll() itself, or null when it finished. This is not the same as an artifact that failed to build: those are isolated and reported in
Artifacts, and registration carries on. This one ended the run.
Remarks
This exists so that a consumer holding an IEventStore can tell three states apart that used to look identical from outside: every artifact registered, some subset registered and the rest were dropped, and registration has not run yet. A read model that cannot be built is isolated and logged rather than thrown, which is right - one unbuildable read model must not cost the whole read side - but it turns a loud total failure into a quiet partial one. This is the read-only seam for asking about it afterwards; it is not a failure policy, and nothing here decides whether a failed artifact should abort start-up, fail a spec, or be tolerated. That is the consumer's call.
What it covers. Projection artifacts - both the fluent IProjectionFor<TReadModel> implementations and the model-bound read models. Those are the artifacts whose registration round-trips to the kernel inside RegisterAll(), so an outcome for them is something the client can actually observe rather than assume. IsRegistered means the client built a definition for the artifact and the kernel accepted the batch it travelled in; the registration operation itself returns no per-definition verdict, so a finer-grained claim than that would be invented. For newly registered active projections, success also means the kernel subscribed their observers in the event store's existing namespaces. It does not mean an event appended afterwards is already reflected in materialized read models. For read-your-writes, use WaitForCompletion(IAppendResultForObserverCompletion, TimeSpan?) on the append result before reading, or use a passive read model.
What it deliberately does not cover: reactors and reducers. Their Register() only opens a duplex
stream - it marks itself registered and returns before any round trip to the kernel has happened. Reporting them
here would mean reporting a hope as a fact, so they are left out rather than represented optimistically. A consumer
that needs to know a reactor or reducer is live should wait on its observer state instead
(ReactorWaitExtensions.WaitTillSubscribed, ReducerWaitExtensions.WaitTillActive), which is answered by
the kernel.
Do not reach for IsConnected instead of this. It looks like it
carries a registration verdict and it partly does - ConnectionLifecycle.Connected() rolls it back to
false and rethrows when one of the connected handlers failed, and
RegisterAll() is one of those handlers. But it is set to true
before the handlers run, and only rolled back after they have all finished, so polling it races:
it reads true while registration is still in flight and while a registration that is about to be
reported as failed is still running. It answers "connected", which is necessary and not sufficient. Use
Registration and its IsRegistering flag instead.
Fields
NotRun
The outcome for an event store whose RegisterAll() has not finished yet.
public static readonly RegistrationOutcome NotRun
Field Value
Remarks
Registration is wired to the connection lifecycle, so "not yet" and "never" look the same from outside - this
value covers both. It does not cover "tried and failed": a run that throws still reports itself, carrying its
Failure. Use RegistrationWaitExtensions.WaitForRegistration to wait for whichever arrives.
Properties
Artifacts
The declared artifacts, each carrying whether it registered and, if not, the failure that stopped it.
public IImmutableList<ArtifactRegistration> Artifacts { get; init; }
Property Value
EqualityContract
protected virtual Type EqualityContract { get; }
Property Value
Failure
The Exception that stopped RegisterAll() itself, or null
when it finished. This is not the same as an artifact that failed to build: those are isolated and reported in
Artifacts, and registration carries on. This one ended the run.
public Exception? Failure { get; init; }
Property Value
Failures
Gets the declared artifacts that did not register, each carrying the failure that stopped it.
public IEnumerable<ArtifactRegistration> Failures { get; }
Property Value
HasRun
Whether RegisterAll() has finished at least once, successfully or not.
public bool HasRun { get; init; }
Property Value
IdentityDrift
Gets the likely identity changes detected before this registration run, retained across background retries. These are heuristic findings, not per-artifact registration failures. Only reactor and event type findings prevent registration under the Fail policy.
public IImmutableList<ArtifactIdentityDrift> IdentityDrift { get; init; }
Property Value
IsRegistering
Gets a value indicating whether a registration run is in flight, or a reconnect will re-register after a disconnect. The other members describe the previous run while this is true.
public bool IsRegistering { get; init; }
Property Value
IsSuccess
Gets a value indicating whether registration ran, finished, and every declared artifact registered.
public bool IsSuccess { get; }
Property Value
Methods
Deconstruct(out bool, out IImmutableList<ArtifactRegistration>, out Exception?)
public void Deconstruct(out bool HasRun, out IImmutableList<ArtifactRegistration> Artifacts, out Exception? Failure)
Parameters
HasRunboolArtifactsIImmutableList<ArtifactRegistration>FailureException
Equals(RegistrationOutcome?)
Compare with another RegistrationOutcome by value.
public virtual bool Equals(RegistrationOutcome? other)
Parameters
otherRegistrationOutcomeThe outcome to compare with.
Returns
- bool
True if the outcomes carry the same artifacts, false otherwise.
Remarks
The generated record equality would compare Artifacts with the list's own equality, which is by reference - so two outcomes carrying the same artifacts came out unequal, and a record's headline promise did not hold for the one member that matters. It ships a NotRun sentinel that invites being compared against, so this is not theoretical.
Equals(object?)
public override bool Equals(object? obj)
Parameters
objobject
Returns
GetHashCode()
public override int GetHashCode()
Returns
PrintMembers(StringBuilder)
protected virtual bool PrintMembers(StringBuilder builder)
Parameters
builderStringBuilder
Returns
ToString()
public override string ToString()
Returns
Operators
operator ==(RegistrationOutcome?, RegistrationOutcome?)
public static bool operator ==(RegistrationOutcome? left, RegistrationOutcome? right)
Parameters
leftRegistrationOutcomerightRegistrationOutcome
Returns
operator !=(RegistrationOutcome?, RegistrationOutcome?)
public static bool operator !=(RegistrationOutcome? left, RegistrationOutcome? right)
Parameters
leftRegistrationOutcomerightRegistrationOutcome