Engine Options Configuration Surface
IOptions<T>-based configuration for every engine service, set with aconfiguredelegate on eachAdd*()call.
Status: โ Implemented ยท Visibility: Public ยท Level: ๐ต Core ยท Category: Hosting
๐ฏ What it solves
Bootstrapping a DatabaseEngine means tuning several independent subsystems โ page cache size,
WAL directory and durability knobs, lock timeouts, MVCC cleanup cadence, transient storage, and
background statistics. Hardcoding these in constructors would force every consumer to know the
engine's internal wiring and recompile for any tuning change. The Options Configuration Surface
gives every DI registration a single, idiomatic .NET pattern โ a configure delegate bound
through Microsoft.Extensions.Options โ to set these values at startup without touching how the
services are constructed or wired together.
โ๏ธ How it works (in brief)
Each Add* extension (AddDatabaseEngine, AddManagedPagedMMF, AddPagedMemoryMappedFile,
AddMemoryAllocator, AddResourceRegistry) calls services.AddOptions<TOptions>() and applies
your configure delegate via .Configure(...). The resulting IOptions<TOptions> is resolved
inside that service's factory when the container first builds it, so values are effectively frozen
at BuildServiceProvider() for singleton registrations. DatabaseEngineOptions is a composite: it
groups per-subsystem option types (Resources, Timeouts, DeferredCleanup, Wal, Transient,
Statistics) instead of one flat bag of properties, so you only touch the knobs you need.
๐ป Usage
Most apps set options through the one-line AddTyphon surface โ its Configure* delegates target
the very option types this page documents:
services.AddTyphon(o => o
.DatabaseFile(@"C:\Data\MyGame\game.typhon")
.PageCacheSize(512UL * 1024 * 1024) // 512 MiB page cache (default is 256 MiB)
.ConfigureEngine(e =>
{
e.Resources.MaxActiveTransactions = 2000;
e.Wal.UseFUA = false; // GroupCommit workload, no per-write FUA
e.Statistics = null; // disable background stats worker
}));
var engine = services.BuildServiceProvider().GetRequiredService<DatabaseEngine>();
AddTyphon folds those Configure* delegates into the per-Add* configuration surface below โ
reach for it directly when you need finer control over service lifetimes, or want to configure or
substitute a single subsystem. configure is optional on every Add* call; omit it to run on defaults.
var services = new ServiceCollection();
services.AddLogging(b => b.AddFilter((_, level) => level >= LogLevel.Warning));
services
.AddResourceRegistry()
.AddMemoryAllocator()
.AddEpochManager()
.AddHighResolutionSharedTimer()
.AddDeadlineWatchdog()
.AddManagedPagedMMF(opts =>
{
opts.DatabaseName = "MyGame";
opts.DatabaseDirectory = @"C:\Data\MyGame";
opts.DatabaseCacheSize = 65536UL * 8192; // 512 MiB page cache
})
.AddDatabaseEngine(engineOpts =>
{
engineOpts.Resources.MaxActiveTransactions = 2000;
engineOpts.Wal.UseFUA = false; // GroupCommit workload, no per-write FUA
engineOpts.Statistics = null; // disable background stats worker
});
var provider = services.BuildServiceProvider();
var engine = provider.GetRequiredService<DatabaseEngine>();
| Options type | Registered by | Key knobs |
|---|---|---|
DatabaseEngineOptions |
AddDatabaseEngine |
Resources, Timeouts, DeferredCleanup, Wal, Transient, Statistics |
PagedMMFOptions / ManagedPagedMMFOptions |
AddPagedMemoryMappedFile / AddManagedPagedMMF |
DatabaseName, DatabaseDirectory, DatabaseCacheSize |
MemoryAllocatorOptions |
AddMemoryAllocator |
Name (diagnostics label only) |
ResourceRegistryOptions |
AddResourceRegistry |
Name (diagnostics label only) |
โ ๏ธ Guarantees & limits
configureis optional on everyAdd*call โ omit it to run on defaults (256 MiB page cache; WAL in./wal;UseFUA = true; etc.). The page cache has an 8 MiB minimum floor; running near it risksPageCacheBackpressureTimeout, so a below-recommended size logs a startup warning.- Options are read once, effectively at
BuildServiceProvider()for singletons โ mutating an already-resolvedIOptions<T>.Valueafterward has no effect on a running engine. - Calling the same
Add*()more than once accumulatesConfigurecallbacks (every registered delegate runs, in order) rather than replacing the previous one โ avoid double-registration. - Scoped/Transient variants (
AddScopedDatabaseEngine,AddTransientManagedPagedMemoryMappedFile, etc.) accept the sameconfiguredelegate, but mixing lifetimes across the dependency chain risks a captive-dependency failure underBuildServiceProvider(validateScopes: true)โ the canonical setup is all-singleton. MemoryAllocatorOptionsandResourceRegistryOptionsare configurable but, in practice, only expose a diagnosticsNametoday โ there is nothing else to tune on them.- See Options Validation Hooks for what configuration mistakes are โ and are not โ caught before they reach the engine.
๐งช Tests
- ResourceOptionsTests โ
DatabaseEngineOptions.Resourcescomposite property and its defaults (DatabaseEngineOptions_HasResourceOptionsProperty/_ResourceOptions_HasDefaults) - TransactionChainTests โ
TransactionChainMaxActiveTestsfixture configuresAddScopedDatabaseEngine(options => options.Resources.MaxActiveTransactions = ...)and proves theconfiguredelegate value actually reaches and is enforced by the running engine
๐ Related
- Source:
TyphonBuilderExtensions.cs,DatabaseEngine.csโDatabaseEngineOptions,PagedMMFOptions.cs,WalWriterOptions.cs - Sub-features: Options Validation Hooks
- Sibling: DI Engine Bootstrap Chain โ the
Add*()calls this surface'sconfiguredelegates bind options onto.