Veggerby.Ignition
0.6.0
dotnet add package Veggerby.Ignition --version 0.6.0
NuGet\Install-Package Veggerby.Ignition -Version 0.6.0
<PackageReference Include="Veggerby.Ignition" Version="0.6.0" />
<PackageVersion Include="Veggerby.Ignition" Version="0.6.0" />
<PackageReference Include="Veggerby.Ignition" />
paket add Veggerby.Ignition --version 0.6.0
#r "nuget: Veggerby.Ignition, 0.6.0"
#:package Veggerby.Ignition@0.6.0
#addin nuget:?package=Veggerby.Ignition&version=0.6.0
#tool nuget:?package=Veggerby.Ignition&version=0.6.0
Veggerby.Ignition
A lightweight, extensible startup readiness ("ignition") coordination library for .NET applications. Register ignition signals representing asynchronous initialization tasks (cache warmers, external connections, background services) and await them collectively with rich diagnostics, configurable policies, timeouts, tracing, and health checks.
Features
- Simple
IIgnitionSignalabstraction (name, optional timeout,WaitAsync) - Coordinated waiting via
IIgnitionCoordinator - Global timeout (soft by default) with per-signal overrides
- Policies: FailFast, BestEffort, ContinueOnTimeout
- Health check integration (adds
ignition-readinesscheck) - Activity tracing (toggle via options)
- Slow handle logging (top N longest signals)
- Task and cancellable Task factory adapters (
IgnitionSignal.FromTask,FromTaskFactory) - Idempotent execution (signals evaluated once, result cached)
- Execution modes: Parallel (default) or Sequential
- Optional parallelism limiting via MaxDegreeOfParallelism
- Cooperative cancellation on global or per-signal timeout
Quick Start
// Program.cs or hosting setup
builder.Services.AddIgnition(options =>
{
options.GlobalTimeout = TimeSpan.FromSeconds(10);
options.Policy = IgnitionPolicy.BestEffort; // or FailFast / ContinueOnTimeout
options.EnableTracing = true; // emits Activity if diagnostics consumed
options.ExecutionMode = IgnitionExecutionMode.Parallel; // or Sequential
options.MaxDegreeOfParallelism = 4; // limit concurrency (Parallel mode only)
options.CancelOnGlobalTimeout = true; // attempt to cancel still-running signals if global timeout hits
options.CancelIndividualOnTimeout = true; // cancel a signal if its own timeout elapses
});
// Register concrete ignition signals
builder.Services.AddIgnitionSignal(new CustomConnectionSignal());
// Wrap an existing task
builder.Services.AddIgnitionFromTask("cache-warm", cacheWarmTask, timeout: TimeSpan.FromSeconds(5));
// Wrap a cancellable task factory (invoked lazily once)
builder.Services.AddIgnitionFromTask(
name: "search-index",
readyTaskFactory: ct => indexBuilder.BuildAsync(ct),
timeout: TimeSpan.FromSeconds(30));
// Adapt a single service exposing a readiness Task
builder.Services.AddIgnitionFor<MyBackgroundWorker>(w => w.ReadyTask);
// Composite: await all instances of a service type
builder.Services.AddIgnitionForAll<ShardProcessor>(p => p.ReadyTask);
// TaskCompletionSource helpers
_startupReady.Ignited();
_startupReady.IgnitionFailed(ex);
var app = builder.Build();
// Await readiness before starting interactive loop or accepting traffic
await app.Services.GetRequiredService<IIgnitionCoordinator>().WaitAllAsync();
Creating a Custom Signal
public sealed class CustomConnectionSignal : IIgnitionSignal
{
public string Name => "db-connection";
public TimeSpan? Timeout => TimeSpan.FromSeconds(8); // optional override
public async Task WaitAsync(CancellationToken cancellationToken = default)
{
// Perform initialization (e.g. open connection, ping server)
await DatabaseClient.InitializeAsync(cancellationToken);
}
}
Register it:
services.AddIgnitionSignal<CustomConnectionSignal>();
Policies
| Policy | Behavior |
|---|---|
| FailFast | Throws if any signal fails (aggregate exceptions). |
| BestEffort | Logs failures, continues startup (default). |
| ContinueOnTimeout | Proceeds when global timeout elapses; logs partial results. |
Diagnostics & Results
After ignition completes:
var coord = provider.GetRequiredService<IIgnitionCoordinator>();
var result = await coord.GetResultAsync();
if (result.TimedOut)
{
// handle degraded startup
}
foreach (var r in result.Results)
{
Console.WriteLine($"{r.Name}: {r.Status} in {r.Duration.TotalMilliseconds:F0} ms");
}
Health Check
AddIgnition automatically registers a health check named ignition-readiness returning:
- Healthy: all signals succeeded
- Degraded: soft global timeout elapsed without per-signal timeouts/failures
- Unhealthy: one or more signals failed, a hard global timeout (with cancellation) occurred, or exception during evaluation
Tracing
Set EnableTracing = true to emit an Activity named Ignition.WaitAll. Attach listeners via ActivitySource to integrate with OpenTelemetry or other observability pipelines.
Design Notes
- Thread-safe lazy execution ensures signals run at most once.
- Global timeout is soft unless cancellation is enabled (see Global Timeout Semantics).
- Factory-based signals honor cancellation tokens for cooperative shutdown scenarios.
- Sequential mode enables early fail-fast behavior and reduces resource contention.
- Per-signal cancellation requires signal implementations to observe passed CancellationToken.
Cancellation Token Semantics for Selectors
The cancellable selector overloads (AddIgnitionFor<TService>(Func<TService, CancellationToken, Task>), AddIgnitionForAll<TService>(Func<TService, CancellationToken, Task>), and scoped variants) receive the cancellation token from the FIRST wait invocation. That token is linked to coordinator-driven cancellations:
- Hard global timeout (
CancelOnGlobalTimeout = true) - Per-signal timeout cancellation (
CancelIndividualOnTimeout = true)
Because ignition evaluation is idempotent, subsequent calls to IIgnitionCoordinator.WaitAllAsync() reuse already created tasks; the token cannot be changed after the first invocation. Selector implementations should:
- Avoid capturing ambient tokens elsewhere (use only the provided one)
- Return promptly or cooperatively observe cancellation (e.g.
await Task.Delay(timeout, token)) - Not block synchronously; always async-await
If you require a fresh cancellation token per consumer, expose a custom IIgnitionSignal instead of using the built-in selector adapters.
Installation
Add a package reference (after publishing):
dotnet nuget add package Veggerby.Ignition
Roadmap Ideas
- Built-in signals (e.g.
HttpEndpointSignal,ChannelDrainSignal) - OpenTelemetry semantic conventions integration
- Structured metrics export (histograms for durations)
License
MIT
Additional Adapters
// Single instance
services.AddIgnitionFor<CachePrimer>(c => c.ReadyTask);
// Composite group
services.AddIgnitionForAll<Consumer>(c => c.ReadyTask, groupName: "Consumer[*]");
// Arbitrary provider-based readiness
services.AddIgnitionFromFactory(
taskFactory: sp => Task.WhenAll(
sp.GetRequiredService<PrimaryConnection>().OpenAsync(),
sp.GetRequiredService<ReplicaConnection>().WarmAsync()),
name: "datastore-connections");
// TCS helpers
_readyTcs.Ignited();
_readyTcs.IgnitionFailed(new Exception("boom"));
Global Timeout Semantics
Ignition exposes two timeout layers:
- Global timeout (
GlobalTimeout): A soft deadline unlessCancelOnGlobalTimeout = true.- Soft (default): Execution continues beyond the elapsed deadline; final result only marked timed out if any signal itself timed out.
- Hard (cancelling): Outstanding signals receive cancellation; result marked timed out and unfinished signals reported with their names and
TimedOutstatus.
- Per-signal timeout (
IIgnitionSignal.Timeout): Always enforced; if elapsed the signal is markedTimedOutand optionally cancelled whenCancelIndividualOnTimeout = true.
Classification summary:
| Scenario | Result.TimedOut | Signal statuses |
|---|---|---|
| Soft global timeout, all eventually succeed | False | All Succeeded |
| Soft global timeout, a signal timed out | True | TimedOut + Succeeded |
| Hard global timeout (cancel) | True | TimedOut (unfinished) + any completed |
| Per-signal timeout only | True | TimedOut + Succeeded |
This model avoids penalizing slow but successful initialization while still enabling an upper bound via opt-in cancellation.
Configuration Examples
// Soft global timeout (default behavior):
services.AddIgnition(o =>
{
o.GlobalTimeout = TimeSpan.FromSeconds(5); // deadline hint
o.CancelOnGlobalTimeout = false; // remain soft (default)
o.CancelIndividualOnTimeout = false; // per-signal timeouts won't cancel tasks
});
// Hard global timeout with cancellation:
services.AddIgnition(o =>
{
o.GlobalTimeout = TimeSpan.FromSeconds(5);
o.CancelOnGlobalTimeout = true; // cancel all outstanding signals at deadline
o.Policy = IgnitionPolicy.ContinueOnTimeout; // choose continuation policy
});
// Mixed: hard global timeout + per-signal timeout cancellation:
services.AddIgnition(o =>
{
o.GlobalTimeout = TimeSpan.FromSeconds(10);
o.CancelOnGlobalTimeout = true; // hard deadline
o.CancelIndividualOnTimeout = true; // cancel slow individual signals
o.ExecutionMode = IgnitionExecutionMode.Parallel;
o.MaxDegreeOfParallelism = 4;
});
// Defining a per-signal timeout (hard for that signal only):
services.AddIgnitionFromTask(
name: "search-index",
readyTaskFactory: ct => indexBuilder.BuildAsync(ct),
timeout: TimeSpan.FromSeconds(30) // this signal will be marked TimedOut if exceeded
);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Options (>= 10.0.1)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Options (>= 10.0.1)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Options (>= 10.0.1)
NuGet packages (21)
Showing the top 5 NuGet packages that depend on Veggerby.Ignition:
| Package | Downloads |
|---|---|
|
Veggerby.Ignition.RabbitMq
RabbitMQ readiness signals for Veggerby.Ignition - verify broker connections, queues, and exchanges during application startup. |
|
|
Veggerby.Ignition.MassTransit
MassTransit bus readiness signals for Veggerby.Ignition - verify message bus startup and connectivity during application initialization. |
|
|
Veggerby.Ignition.Marten
Marten document store readiness signals for Veggerby.Ignition - verify Marten readiness during application startup. |
|
|
Veggerby.Ignition.Postgres
PostgreSQL readiness signals for Veggerby.Ignition - verify database connections and schema during application startup. |
|
|
Veggerby.Ignition.SqlServer
SQL Server readiness signals for Veggerby.Ignition - verify database connections and schema during application startup. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.0 | 441 | 2/2/2026 |
| 0.5.1-prerelease0023 | 423 | 2/2/2026 |
| 0.5.1-prerelease0022 | 445 | 1/26/2026 |
| 0.5.1-prerelease0021 | 425 | 1/22/2026 |
| 0.5.1-prerelease0020 | 438 | 1/22/2026 |
| 0.5.1-prerelease0019 | 434 | 1/21/2026 |
| 0.5.1-prerelease0018 | 387 | 1/21/2026 |
| 0.5.1-prerelease0017 | 379 | 1/20/2026 |
| 0.5.1-prerelease0016 | 362 | 1/20/2026 |
| 0.5.1-prerelease0014 | 358 | 1/20/2026 |
| 0.5.1-prerelease0013 | 359 | 1/20/2026 |
| 0.5.1-prerelease0011 | 350 | 1/19/2026 |
| 0.5.1-prerelease0009 | 336 | 1/19/2026 |
| 0.5.1-prerelease0008 | 346 | 1/17/2026 |
| 0.5.1-prerelease0007 | 301 | 1/16/2026 |
| 0.5.1-prerelease0006 | 297 | 1/16/2026 |
| 0.5.1-prerelease0004 | 302 | 1/16/2026 |
| 0.5.1-prerelease0003 | 290 | 1/15/2026 |
| 0.5.1-prerelease0002 | 296 | 1/15/2026 |
| 0.5.1-prerelease0001 | 297 | 1/15/2026 |