Skip to content

Patching API and options reference

Types live in the Cratis.Choreography.Patching namespace of the Cratis.Choreography package, except IPatchStorage (Cratis.Choreography.Storage.Patching, in Cratis.Choreography.Storage). SemanticVersion and AppliedPatch also belong to Cratis.Choreography.Patching and ship in Cratis.Choreography.Storage. For the reasoning, read the overview.

OptionDefaultMeaning
HostVersioninformational version of the entry assembly, without build metadataThe version of the running host. When not set, unparseable or 1.0.0, it is unknown: patches run, the store is not marked and the older-host gate is bypassed
MaxAttempts5How many times the host asks the patch manager while waiting for it to reach a host at least as new as itself
RelocationDelay1 secondWait before asking again, doubled after every attempt up to 30 seconds
MaxWait15 minutesLongest the host keeps asking while calls fail transiently, before it throws PatchingDidNotComplete
MemberMeaning
SemanticVersion Version { get; }The release that introduced the change. Patches run in ascending order; one not newer than the patched version never runs
string Name { get; }Identifies the patch in the record. Defaults to GetType().Name; renaming applies the patch again. The SQL provider allows up to 256 characters
Task Up()Applies the patch. Recorded as applied only after it completes, so it must be safe to run more than once

Name is a default interface member, so reading it through a class variable needs the interface type: ((ICanApplyPatch)patch).Name.

MemberMeaning
Task ApplyPatches(CancellationToken cancellationToken = default)Applies every pending patch and returns when the patch manager has completed

Throws PatchManagerCouldNotRelocate when no host at least as new as this one takes over within MaxAttempts, throws PatchingDidNotComplete when transient call failures outlast MaxWait, throws what a failing patch throws, and honors the cancellation token between attempts and during the wait.

The grain behind the runner, with grain type name cratis-choreography-patch-manager, an integer key of 0 and local placement preference. Hosts do not call it directly.

MemberMeaning
Task<PatchOutcome> ApplyPatchesFor(SemanticVersion caller)Applies pending patches unless the activation runs on an older host than the caller
ValueMeaning
CompletedPatching completed, or was skipped because a newer host has already patched the store
RelocateThe manager is on an older host than the caller and is moving to the caller’s silo; ask again

An Exception raised by IPatchRunner.ApplyPatches after the last attempt. Its message names the host version and states that an older host is still holding the patch manager. The runner also logs it at Critical before it throws.

An Exception raised by IPatchRunner.ApplyPatches when response timeouts, an unavailable silo or rejected messages kept the call from completing for MaxWait. Its message names the host version and the time waited, and its inner exception is the last failure seen. The runner logs it at Critical before it throws.

record AppliedPatch(string Name, SemanticVersion Version, DateTimeOffset AppliedAt), the record of one patch that has run.

record SemanticVersion(int Major, int Minor, int Patch, string PreRelease = "", string BuildMetadata = ""). It implements IComparable<SemanticVersion>, ordering by major, minor and patch, then pre-release (a pre-release sorts before the release); build metadata is ignored in comparisons.

MemberMeaning
NotSet0.0.0
Parse(string)Parses major.minor.patch[-prerelease][+build]; throws ArgumentException on an invalid string
TryParse(string, out SemanticVersion?)Returns false instead of throwing
implicit conversion from stringSemanticVersion version = "1.5.0"; parses the string
<, <=, >, >=Compare by precedence

Implement this to keep the record somewhere else. There is one set of these facts per store.

MemberMeaning
Task<IEnumerable<AppliedPatch>> GetAll()Every applied patch
Task<bool> Has(string patchName)Whether a patch has been applied
Task Save(AppliedPatch patch)Records a patch; recording one that exists replaces its record
Task<SemanticVersion?> GetPatchedVersion()Version of the newest patch applied, or null
Task SetPatchedVersion(SemanticVersion version)Sets it
Task<SemanticVersion?> GetHostVersion()Version of the newest host that patched the store, or null
Task SetHostVersion(SemanticVersion version)Sets it

Provided implementations: InMemoryPatchStorage, MongoDBPatchStorage and SqlPatchStorage.

MethodReceiverDoes
AddCratisChoreographyPatching(Action<PatchingOptions>? configure = null)ISiloBuilderAdds the patching services and applies patches when the silo reaches the Active lifecycle stage
AddCratisChoreographyPatchingServices(Action<PatchingOptions>? configure = null)ISiloBuilderAdds the services and IPatchRunner only; you call the runner
CratisChoreographyOptions.Patching / ConfigurePatchingco-hosted optionsCalls AddCratisChoreographyPatching; with in-memory grain storage it also registers InMemoryPatchStorage
AddCratisChoreographyInMemoryPatchStorage()IServiceCollectionRegisters InMemoryPatchStorage
AddCratisChoreographyMongoDBPatchStorage(IMongoClient client, Action<MongoDBPatchStorageOptions>? configure = null)IServiceCollectionRegisters MongoDBPatchStorage; keeps a client registered earlier
AddCratisChoreographySqlPatchStorage(Action<SqlPatchStorageOptions> configure)IServiceCollectionRegisters SqlPatchStorage; the callback must set OptionsResolver

Storage options:

OptionDefaultMeaning
MongoDBPatchStorageOptions.DatabaseNamechoreographyDatabase that holds the patch records
SqlPatchStorageOptions.OptionsResolverrequiredFunc<DbContextOptions<PatchesDbContext>>, with no arguments

For names of collections and tables see Storage names.