Cabal.SQLite 1.0.0

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Cabal.SQLite --version 1.0.0
                    
NuGet\Install-Package Cabal.SQLite -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cabal.SQLite" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cabal.SQLite" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Cabal.SQLite" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cabal.SQLite --version 1.0.0
                    
#r "nuget: Cabal.SQLite, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cabal.SQLite@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cabal.SQLite&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Cabal.SQLite&version=1.0.0
                    
Install as a Cake Tool

<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 uses FOR UPDATE SKIP LOCKED for native row-level locking.
  • Batch Processing. Heavily optimized for high-throughput environments.
  • Concurrency Limits. Protects your ThreadPool and database connection pools via strict SemaphoreSlim limiting.
  • 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.yaml file 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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 89 5/9/2026
1.0.0 110 5/9/2026