Skip to content

Storage in Cratis Choreography

Choreography keeps two kinds of durable facts, and they find their database in different ways. Mixing them up is the usual source of “my jobs are in one database and my patches in another” surprises, so the split comes first.

FactsResolved byScoped byProviders
Job and job step stateIJobsStoragescope and namespaceMongoDB, SQL, in-memory
Which patches have run and how farIPatchStoragenothing - one set per storeMongoDB, SQL, in-memory

You register one provider for each. Registering jobs storage does not register patch storage.

Jobs storage is resolved per scope and namespace

Section titled “Jobs storage is resolved per scope and namespace”

Every storage touch of the job system - reading a job, saving a step, counting progress - goes through one seam:

JobsStorage GetFor(string scope, string @namespace);

The engine never decides where job state lives. The two values are opaque to it: it takes them from the job grain’s key and hands them back unchanged. A provider decides what the pair resolves to:

  • MongoDB maps the pair to a database name, {scope}+jobs for an empty namespace and {scope}+jobs+{namespace} otherwise. You can replace that rule with a resolver.
  • SQL asks your resolver for the EF Core DbContextOptions of the pair, so you choose the database provider and connection per scope and namespace.

The pair travels inside the grain key, so it survives everything the grain survives. A job activated on another silo lands in the same database as the silo that started it.

ConsumerScopeNamespace
Cratis Chronicle’s kernelevent store nameevent store namespace
An applicationthe service, the tenant, or anything storage should hang offa subdivision of it, or empty

Because the choice is open, keep the values stable. Changing what a scope maps to moves jobs out of sight: the database name you compute for the new value does not exist, and reads come back empty rather than failing.

A patch changes the data the whole store holds, so every silo in the cluster must read and write the same record of what has been applied. IPatchStorage therefore has no scope or namespace: the MongoDB provider uses one database for the whole store, and the SQL provider uses one PatchesDbContext. See Choose patch storage for the choice and Patching for what the records are used for.

IJobsStorage.Reset() forgets everything resolved so far. Resolution is cached, and for SQL part of resolving is applying migrations once per scope and namespace. If you empty or recreate the database behind the job system, call Reset() afterward. Nothing fails at that moment; without it, the next write reports a table that is not there.

  • Storage names lists every collection, table and grain storage provider name.
  • Host the silo shows how the co-hosted silo picks up jobs storage.