Cabal.PostgreSQL
1.0.0
See the version list below for details.
dotnet add package Cabal.PostgreSQL --version 1.0.0
NuGet\Install-Package Cabal.PostgreSQL -Version 1.0.0
<PackageReference Include="Cabal.PostgreSQL" Version="1.0.0" />
<PackageVersion Include="Cabal.PostgreSQL" Version="1.0.0" />
<PackageReference Include="Cabal.PostgreSQL" />
paket add Cabal.PostgreSQL --version 1.0.0
#r "nuget: Cabal.PostgreSQL, 1.0.0"
#:package Cabal.PostgreSQL@1.0.0
#addin nuget:?package=Cabal.PostgreSQL&version=1.0.0
#tool nuget:?package=Cabal.PostgreSQL&version=1.0.0
<div align="center"> <img src="assets/logo.png" alt="Cabal Scheduler Logo" width="200" height="200">
<h1>Cabal Scheduler</h1> <p><strong>A lightweight background job engine for .NET. No Redis, no ORMs, no bloat.</strong></p> </div>
Sometimes you just need to run a task every few minutes and know if it crashes. Hangfire and Quartz are great tools, but they come with real setup costs β infrastructure dependencies, massive schemas, learning curves. Cabal is built for the cases where all of that is simply overkill.
πͺΆ Package Size & Footprint
Cabal is expressly designed to be ultra-lightweight and is fully compatible with Trimmed and Native AOT Self-Contained deployments.
| Package | NuGet (.nupkg) |
Binary (.dll) |
|---|---|---|
| Cabal.Scheduler (Core) | ~23.5 KB | ~71.5 KB |
| Cabal.SQLite | ~13.4 KB | ~33.5 KB |
| Cabal.PostgreSQL | ~13.5 KB | ~34.0 KB |
Total payload: The engine plus a database driver will add just around ~105 KB to your final binary.
β‘ Features
- Zero external dependencies in the core package. The only reference is
Microsoft.AspNetCore.App. - Trim-Compatible. Fully annotated for AOT and trimming. No reflection used in the core execution engine.
- Raw ADO.NET against SQLite or PostgreSQL. No ORM overhead.
- Concurrency-safe. SQLite uses WAL mode and atomic
UPDATE β¦ RETURNING. PostgreSQL usesFOR UPDATE SKIP LOCKEDfor native row-level locking. - Batch Processing. Heavily optimized for high-throughput environments.
- Concurrency Limits. Protects your
ThreadPooland database connection pools via strictSemaphoreSlimlimiting. - Exponential Backoff. Configure max retries per job.
- Built-in Dashboard. Embedded directly in the binaryβno external static assets to host.
π Benchmarks
Cabal is heavily optimized to reduce garbage collection pressure. Compared to industry standards, Cabal operates with significantly fewer memory allocations, effectively eradicating N+1 query problems and utilizing a zero-allocation task state machine.
Job Scheduling Overhead (1000 Jobs)
| Library | Mean Time | Allocated Memory | Alloc Ratio |
|---|---|---|---|
| Cabal (SQLite In-Memory) | 11.75 ms | 2.46 MB | 1.00x |
| Cabal (PostgreSQL Local) | 28.25 ms | 3.18 MB | 1.29x |
| Quartz (MemoryStorage) | 3.46 ms | 5.93 MB | 2.41x |
| Hangfire (MemoryStorage) | 118.69 ms | 68.30 MB | 27.76x |
(Note: Quartz uses in-memory buffering for faster raw times, whereas Cabal guarantees immediate atomic persistence to disk. Cabal's PostgreSQL test includes network latency, but notice how Cabal strictly maintains an extremely low memory footprint compared to Hangfire).
Job Execution & State Machine
During job dispatching, Cabal fetches jobs in batches using RETURNING clauses (avoiding secondary GetJobById lookups) and coordinates parallelism purely via SemaphoreSlim capacity. This allows Cabal to scale to thousands of concurrent background jobs without maintaining heavy blocking collections (List<Task>) or producing ContinueWith closure allocations, resulting in zero-allocation graceful shutdowns.
βοΈ Zero-Allocation Architecture
Cabal's dispatching mechanism is built entirely around robust database locks and in-memory semaphores:
graph TD
A[Worker Polling Loop] -->|Timeout / Tick| B{Fetch Jobs}
B -- "UPDATE ... RETURNING" --> C[Dispatch Batch]
C --> D[Wait on SemaphoreSlim]
D --> E((Execute Delegate))
E --> F[Atomic Completion]
F --> G[Release SemaphoreSlim]
G -.-> D
When to use Hangfire Instead (Fire-and-Forget Queues)
Cabal is strictly a Scheduler. It is designed to run recurring background tasks reliably. Every execution involves calculating the next interval, updating the database record atomically, and writing an execution history log.
If your use case involves blasting thousands of Fire-and-Forget messages into a queue where you don't need recurrence or strict execution history, Hangfire (with memory/Redis storage) or RabbitMQ are much better suited tools.
Fire-and-Forget Burst (10,000 Jobs)
| Library | Scenario Type | Mean Time |
|---|---|---|
| Hangfire (MemoryStorage) | In-Memory Message Queue | ~0.38 s |
| Cabal (SQLite In-Memory) | Forced Database Transactions | ~20.96 s |
(Note: Hangfire processes this load exponentially faster because BackgroundJob.Enqueue acts as a pure in-memory ConcurrentQueue loop, while Cabal performs 10,000 atomic database roundtrips to lock, execute, and write history logs for each job).
π¦ Installation
(Coming soon to NuGet π)
dotnet add package Cabal.Scheduler
Then, choose your storage provider:
dotnet add package Cabal.SQLite
# or
dotnet add package Cabal.PostgreSQL
π How to Integrate
Integrating Cabal takes only 3 simple steps in your Program.cs.
Step 1: Register Storage
Pick your preferred database engine. Cabal will automatically create and migrate the required lightweight tables on startup.
For SQLite:
builder.Services.AddCabalSqlite("Data Source=cabal.db;");
For PostgreSQL:
builder.Services.AddCabalPostgreSql("Host=localhost;Database=cabal;Username=user;Password=pass;");
Step 2: Define your Jobs
Jobs are strongly typed and defined via a fluent API. You have access to the service provider, meaning you can easily resolve scoped services (like your Entity Framework DbContext) inside the delegate.
// Simple Action
Schedule.Every(5).Seconds()
.WithName("System Ping")
.Do(() => Console.WriteLine("Ping!"));
// Async Action with Dependency Injection
Schedule.Every(10).Minutes()
.WithName("Send Pending Emails")
.WithRetries(3)
.Do(async (services, ct) =>
{
var db = services.GetRequiredService<AppDbContext>();
await EmailService.ProcessQueueAsync(db, ct);
});
// Basic Fire-and-Forget (Runs exactly once on startup)
Schedule.Once()
.WithName("Run Migrations")
.Do(async (services, ct) =>
{
var db = services.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync(ct);
});
Step 3: Mount the Dashboard (Optional)
Cabal comes with an embedded, post-apocalyptic terminal-themed dashboard. Just pick a route!
var app = builder.Build();
// Mount the UI at /cabal
// WARNING: The dashboard exposes sensitive system data. Always secure it in production!
app.UseCabalDashboard("/cabal", options =>
{
// Example: Require a specific authorization policy
options.RequireAuthorization("AdminPolicy");
});
app.Run();
Tip: Use the
compose.yamlfile in this repo to easily spin up a PostgreSQL instance for local development:docker compose up -d
π₯οΈ Dashboard View
Navigate to your mounted path (e.g., /cabal) to monitor active jobs, check upcoming execution times, view history logs, and see an RPM graph.
<p align="center"> <img src="assets/dashboard.png" alt="Cabal dashboard" width="800"> </p>
π License
MIT
| 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
- Cabal.Scheduler (>= 1.0.0-beta)
- Npgsql (>= 8.0.9)
-
net8.0
- Cabal.Scheduler (>= 1.0.0-beta)
- Npgsql (>= 8.0.9)
-
net9.0
- Cabal.Scheduler (>= 1.0.0-beta)
- Npgsql (>= 8.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1-beta | 96 | 5/9/2026 |
| 1.0.0 | 108 | 5/9/2026 |