Table of Contents

Resource Budget Configuration (ResourceOptions)

Startup-time sizing of every fixed/growable resource limit, range-checked at DI resolution.

Status: โœ… Implemented ยท Visibility: Public ยท Level: ๐Ÿ”ต Core ยท Category: Resources

๐ŸŽฏ What it solves

Typhon's memory-bound components (page cache, WAL ring/segments, shadow buffer) must be sized before the engine starts โ€” there's no GC to grow them lazily, and getting it wrong either wastes memory or causes runtime exhaustion under load. Applications need one place to declare these limits, in domain units (transactions, bytes, milliseconds), and a way to catch an out-of-range value at startup instead of in production.

โš™๏ธ How it works (in brief)

ResourceOptions is a plain settings object hung off DatabaseEngineOptions.Resources. Each property maps to one bounded resource's limit (max active transactions, WAL ring bytes, page checksum verification, checkpoint cadence and barrier timeout) and ships with a sane default. Components never see this object directly โ€” each receives only its own limit at construction. There is no overall memory budget and no manual validation call: ResourceOptions has no TotalMemoryBudgetBytes property and no Validate() method, because neither would govern an allocation. Each wired knob is range-checked automatically at DI resolution by DatabaseEngineOptionsValidator.

๐Ÿ’ป Usage

using Typhon.Engine;

// DatabaseEngine's constructor is internal โ€” set the budget through the DI extension (see DI
// Registration & Wiring) or directly on DatabaseEngineOptions if you already have one.
services.AddDatabaseEngine(opt =>
{
    opt.Resources = new ResourceOptions
    {
        MaxActiveTransactions       = 1000,
        WalRingBufferSizeBytes      = 64 << 20,   // 64 MB (default โ€” 2 ร— 32 MB halves)
        PageChecksumVerification    = PageChecksumVerification.OnLoad,
        CheckpointIntervalMs        = 30_000,
        CheckpointBarrierTimeoutMs  = 30_000,
    };

    // No Validate() call โ€” every knob above is range-checked at DI resolution.
});

// Page-cache size is NOT a ResourceOptions knob โ€” it lives on the paged store:
services.AddManagedPagedMMF(o => o.DatabaseCacheSize = 512UL << 20);   // 512 MiB
Option Default Effect
MaxActiveTransactions 1000 CreateTransaction throws ResourceExhaustedException beyond this
WalRingBufferSizeBytes 64 MB Total pinned; 2 ร— 32 MB halves. Commit threads block once the ring drains slower than it fills. Sized for tail latency โ€” lower for memory-constrained deployments
PageChecksumVerification OnLoad CRC every page load ยท only during recovery ยท recovery-suspect mode
CheckpointIntervalMs 30000 Idle checkpoint cadence
CheckpointDirtyPageThresholdPercent 25 Run a checkpoint as soon as this percentage of the page cache owes a writeback, without waiting for the interval; 0 disables the trigger
CheckpointBarrierTimeoutMs 30000 How long a checkpoint waits for its barrier before giving up

That is the entire type. There is no PageCachePages, MaxPageCachePages, TransactionPoolSize, WalBackPressureThreshold, WalMaxSegmentSizeBytes, WalMaxSegments, CheckpointMaxDirtyPages or ShadowBufferPages knob โ€” nothing would read one. Page-cache size is PagedMMFOptions.DatabaseCacheSize (default 256 MiB); WAL segment sizing and group-commit cadence are WalWriterOptions; the transaction pool is a const 16 in TransactionChain, not a knob.

โš ๏ธ Guarantees & limits

  • Set once at construction; there is no supported way to change ResourceOptions after the engine starts โ€” resizing requires a restart.
  • Validation is range-checking, not budgeting. DatabaseEngineOptionsValidator rejects a non-positive MaxActiveTransactions, WalRingBufferSizeBytes, CheckpointIntervalMs or CheckpointBarrierTimeoutMs (and the wired WalWriterOptions sizes) at DI resolution. Nothing sums your allocations against a memory ceiling โ€” a configuration that passes can still ask for more RAM than the machine has.
  • There is no Validate(), CalculateFixedAllocationBytes() or CalculateAvailableBudgetBytes() to call โ€” the type exposes no budgeting API at all.
  • Each component receives only its own limit (constructor injection) โ€” there is no way to read another component's budget back out of a live engine via this type.
  • The exhaustion policy each limit triggers (FailFast, Wait, Evict, Degrade) is fixed per-component and not configurable here โ€” see the resource graph's ExhaustionPolicy metadata for what happens when a given limit is hit.

๐Ÿงช Tests