-
Notifications
You must be signed in to change notification settings - Fork 4
Background tasks
Scrinium offloads three kinds of maintenance work to a task runner so it runs off the request path: propagating denormalized reference updates, propagating domain deletes to the referencing documents, and running migrations. By default that runner is Hangfire; you can plug in your own.
A task runner implements ITaskRunner, which schedules exactly three jobs:
public interface ITaskRunner
{
void RunDeleteDocDependenciesTask(
Type dbContextType,
Type deletedDbContextType,
string deletedRepositoryName,
object modelId,
IEnumerable<string> idMemberMapIdentifiers);
void RunMigrateDbTask(Type dbContextType, string dbMigrationOpId);
void RunUpdateDocDependenciesTask(
Type dbContextType,
Type referenceDbContextType,
string referenceRepositoryName,
object modelId,
IEnumerable<string> idMemberMapIdentifiers);
}-
UpdateDocDependenciesTask— enqueued when a changed model is saved (bySaveChangesAsync, or byReplaceAsyncwith dependent-document updates enabled) and at least one changed member is denormalized by a reference summary sourced on the saving repository — of the same db context, or of a writable parent context of the application whose reference declares that source with the typed factory — one task per involved context; it propagates the changed members' new values to every document of the application that denormalized them. When no changed member is denormalized by any summary, no task is enqueued: there would be nothing to propagate. How the propagation writes, and which paths it can't address, is covered in References and denormalization. -
DeleteDocDependenciesTask— enqueued when a model is deleted through its repository and at least one reference sourced on the deleting repository — of the same db context, or of a writable parent context whose reference declares that source with the typed factory — can host its type declaring an origin delete policy other than keeping the reference — one task per involved context; it removes the references to the deleted document, or deletes the referencing documents where the reference declares the cascade. The removal shapes and the cascade chaining are covered in References and denormalization. -
MigrateDbContextTask— runs a db-context migration under exclusive access; a dry run operation runs without it, since it persists nothing. Either way the task resumes the db context lock claim its start made, keeps the lease renewed while it works, and releases it when the operation closes.
Both propagation payloads identify the changed model's repository by db context type and repository
name together (referenceDbContextType/deletedDbContextType plus the repository name), re-verified
at execution: repository names are unique per db context only, so a same-named repository of another
context never serves a stale payload.
AddScriniumWithHangfire registers HangfireTaskRunner and wires Hangfire's MongoDB storage. You only
need a running Hangfire server to process the jobs:
builder.Services.AddHangfireServer();
builder.Services.AddScriniumWithHangfire()
.AddDbContext<ISampleDbContext, SampleDbContext>();Maintenance jobs are enqueued on the queue named by ScriniumOptions.DbMaintenanceQueueName
(default "default"). Point it at a dedicated queue to isolate maintenance load:
builder.Services.AddScriniumWithHangfire(
configureScriniumOptions: o => o.DbMaintenanceQueueName = "scrinium-maintenance");
// The Hangfire server must listen on that queue too, or the jobs are never processed:
builder.Services.AddHangfireServer(o => o.Queues = ["scrinium-maintenance", "default"]);Each Hangfire job runs in its own DI scope (a fresh DbContext) — provided by
Hangfire's ASP.NET Core integration — and gets its own async-local
execution context from the filter AddScriniumWithHangfire registers — nothing
to wire yourself.
To use another scheduler, implement ITaskRunner and ITaskRunnerBuilder, then register Scrinium
with AddScrinium<TTaskRunner> (from Etherna.Scrinium.AspNetCore) instead of the Hangfire helper:
public sealed class MyTaskRunner : ITaskRunner, ITaskRunnerBuilder
{
public void SetScriniumOptions(ScriniumOptions options) { /* receive options at build */ }
public void RunDeleteDocDependenciesTask(
Type dbContextType, Type deletedDbContextType, string deletedRepositoryName,
object modelId, IEnumerable<string> idMemberMapIdentifiers) { /* schedule it */ }
public void RunMigrateDbTask(Type dbContextType, string dbMigrationOpId) { /* schedule it */ }
public void RunUpdateDocDependenciesTask(
Type dbContextType, Type referenceDbContextType, string referenceRepositoryName,
object modelId, IEnumerable<string> idMemberMapIdentifiers) { /* schedule it */ }
}builder.Services.AddScrinium<MyTaskRunner>()
.AddDbContext<ISampleDbContext, SampleDbContext>();Your runner is responsible for scheduling each call onto its own worker; the actual task bodies
(IUpdateDocDependenciesTask, IDeleteDocDependenciesTask, IMigrateDbContextTask) are provided by
Scrinium and resolved per job.
Give each job a fresh DI scope and an execution context, as the Hangfire runner
does — and retry failed jobs, as Hangfire does automatically: a propagation task executed while a
flow holds exclusive access fails by design, and converges through the retry.
Mind the pickup delay of your queue: from the moment a migration starts to the moment your runner executes it, nothing renews the db context lock claim, which the start sizes with its lease duration. A job picked up after that expires still resumes the claim, unless another owner took the lock over meanwhile — then the operation closes cancelled without migrating.
Next: References and denormalization for what the dependency-update task keeps in sync, Migrations for the migration task, or Startup and configuration for registration.
Scrinium — source · issues (SCR) · GNU LGPL-3.0 · info@etherna.io
Getting started
Core concepts
Working with data
Serialization & mapping
Operations & maintenance
Advanced & reference