-
Notifications
You must be signed in to change notification settings - Fork 0
Cadenza Worker
Background services and daemons in a single file. Run hosts a BackgroundService on the .NET Generic Host and respects Ctrl+C and SIGTERM out of the box.
#:sdk Cadenza.Worker@1.0.15| Base SDK | Microsoft.NET.Sdk.Worker |
| Output | Long-running console host |
| AOT-clean | Yes |
| Default deploy | Self-contained binary, or container (Deployment Container) |
#!/usr/bin/env dotnet run
#:sdk Cadenza.Worker@1.0.15
await Run(async (ct) =>
{
while (!ct.IsCancellationRequested)
{
Log.Info($"Heartbeat at {DateTime.UtcNow:O}");
await Task.Delay(TimeSpan.FromSeconds(30), ct);
}
});How it works:
-
Runbuilds the Generic Host and registers your lambda as aBackgroundService. - The
CancellationTokenpassed to the lambda fires on graceful shutdown (SIGTERMon Linux/macOS,Ctrl+Ceverywhere). Honor it for prompt shutdown. -
Log.Info/Log.Warn/Log.Error/Log.Debugroute throughILogger, so any structured-logging provider Just Works.
await Run(async (CancellationToken ct) => { … });
await Run(async (IServiceProvider sp, CancellationToken ct) => { … }); // DI overload
Worker.Config<T>(key); // typed configuration — call inside Run
Worker.Service<T>(); // resolve a DI service — call inside Run
Log.Info / Log.Warn / Log.Error / Log.DebugPlus the shared modules (Fs, Sh, Http, Env, Json, Prompt) — see Tier 1 API.
Worker.Config<T>(key) reads from the host's IConfiguration — env vars, command-line arguments, appsettings.json (if you drop one next to the script).
Important: call
Config<T>andService<T>inside theRuncallback. They need the host to be built; calling them at the top of the script beforeRunwill throwWorker host has not been started.
#:sdk Cadenza.Worker@1.0.15
await Run(async (ct) =>
{
var endpoint = Worker.Config<string>("Api:Endpoint");
var interval = Worker.Config<int>("Api:IntervalSeconds");
Log.Info($"Polling {endpoint} every {interval}s");
while (!ct.IsCancellationRequested)
{
try
{
var probe = await Http.GetJson<Probe>($"{endpoint}/health", Ctx.Default, ct);
Log.Info($"status: {probe.status}");
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
{
Log.Warn($"probe failed: {ex.Message}");
}
await Task.Delay(TimeSpan.FromSeconds(interval), ct);
}
});
record Probe(string status);
[System.Text.Json.Serialization.JsonSerializable(typeof(Probe))]
partial class Ctx : System.Text.Json.Serialization.JsonSerializerContext { }- Command-line arguments —
dotnet run app.cs -- Api:Endpoint=https://… - Environment variables —
Api__Endpoint=https://…(double underscore for nested keys) -
appsettings.jsonnext to the script (if present) - Defaults baked into the script
await Run(async (sp, ct) =>
{
var db = sp.GetRequiredService<MyDbClient>();
// …
});Or with the no-arg overload:
await Run(async (ct) =>
{
var db = Worker.Service<MyDbClient>();
// …
});To register services up front, use the DI overload of Run — or, if you need to add services before the host starts, drop down to the Generic Host directly (Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder()).
Log.Info("started");
Log.Debug("verbose detail");
Log.Warn("retrying after transient error");
Log.Error("fatal", exception);Log.* routes through ILogger<Program>. Levels honor Logging:LogLevel:Default from configuration. JSON logs, OpenTelemetry, Seq, Application Insights — all the usual providers attach via standard IServiceCollection extensions on the underlying host. For provider setup, drop down to the Generic Host directly.
On Ctrl+C or SIGTERM:
- The host signals the cancellation token.
- Your lambda should observe
ct.IsCancellationRequested(and passcttoTask.Delay,Http.GetJson, etc.). - Once the lambda returns, the host exits.
A misbehaving lambda that ignores ct will block shutdown for up to the host's stop timeout (5 seconds by default).
- Polling jobs, queue consumers, schedulers
- systemd / Windows Service / container daemons
- Side processes that need lifecycle + DI + config but no HTTP endpoint
- One-shot scripts → use Cadenza Console
- Anything where HTTP is the primary surface → use Cadenza Web
-
worker-heartbeat.cs— minimalRun(async ct => …)loop -
worker-polling.cs— config + HTTP + level-correct logging
See Samples for the full annotated list.
# Single binary
dotnet publish app.cs -c Release --self-contained -r linux-x64
# Container (no Dockerfile)
dotnet publish app.cs --os linux --arch x64 /t:PublishContainerSee Deployment Single Binary and Deployment Container.
- Tier 1 API
- Cadenza Web — when the worker also needs HTTP
- Troubleshooting
Cadenza · MIT · .NET 10+ · github.com/rkttu/cadenza · No orchestra. No project. Just one file, playing solo.