Skip to content

Latest commit

 

History

History
214 lines (144 loc) · 13.7 KB

README.md

File metadata and controls

214 lines (144 loc) · 13.7 KB

NexJob

Background jobs for .NET that survive the worst day.
Retries, dead-letter handling, deadlines and a built-in dashboard.
PostgreSQL · SQL Server · Redis · MongoDB · in-memory.

📖 Documentation: oluciano.github.io/NexJob  |  🎮 Live Demo: nexjob-playground.fly.dev  |  📦 NuGet

NuGet NuGet Downloads Build License: MIT
OpenSSF Scorecard OpenSSF Best Practices CodeQL

A job fails three times and reaches the dead-letter queue

A job fails three times and lands in the dead-letter queue with its error, ready to requeue. The two others succeed.

dotnet add package NexJob

Quick Start

// Program.cs: register NexJob (in-memory storage by default) and scan for jobs
builder.Services.AddNexJob();
builder.Services.AddNexJobJobs(typeof(Program).Assembly);
// A job
public sealed class SendInvoiceJob : IJob<SendInvoiceInput>
{
    private readonly IEmailService _email;

    public SendInvoiceJob(IEmailService email) => _email = email;

    public async Task ExecuteAsync(SendInvoiceInput input, CancellationToken ct)
        => await _email.SendAsync(input.Email, "Your invoice", ct);
}

public sealed record SendInvoiceInput(string Email);
// Enqueue it from anywhere. It expires if it does not start within 5 minutes.
var scheduler = app.Services.GetRequiredService<IScheduler>();
await scheduler.EnqueueAsync<SendInvoiceJob, SendInvoiceInput>(
    new SendInvoiceInput("customer@example.com"),
    deadlineAfter: TimeSpan.FromMinutes(5));

To watch it run, add the dashboard (dotnet add package NexJob.Dashboard) and app.UseNexJobDashboard("/dashboard");, then open /dashboard. See Dashboard for the Worker Service variant, or try the live demo first.

To use a database instead of memory, see the storage providers.


Why you can trust it

A job library is judged on the worst day: a node that dies, a deploy in the middle of a job, an upgrade from the previous version. Every failure has a documented cost (see Delivery guarantees). This is what the test suite proves (the full page):

  • The same tests on every provider. One contract suite runs on InMemory, PostgreSQL, SQL Server, Redis and MongoDB.
  • Several nodes, one database. Reliability scenarios run many workers and nodes on the four databases: each job runs once, no job is lost under concurrent enqueue, and a job left behind by a dead node is recovered and runs once.
  • A killed process. On SQL Server a node runs in its own process and is killed in the middle of a job.
  • Upgrades. A storage filled by the previous version opens with the new one, with its history and orphans intact.
  • An honest list of gaps. The process kill is tested on SQL Server only, and wake-up latency is not asserted. The list is in the page above.

The Scorecard, Best Practices and CodeQL badges rate the repository's security and engineering practices (pinned actions, least-privilege tokens, dependency updates, documented process, static analysis). The Best Practices badge is self-declared; the CodeQL badge says the analysis ran on the default branch, and the open alerts are in the Security tab. None of them says anything about throughput or behaviour under failure.


What you get

You need NexJob gives you
Jobs that retry and then stop Global and per-job retries, maxAttempts per enqueue, dead-letter handlers and forwarders, failures that skip retries, [ExecutionTimeout]
No zombie jobs Deadlines, crash recovery by heartbeat, DuplicatePolicy idempotency
Protect a dependency Queue circuit breaker, [Throttle] per resource (cluster-wide with Redis), execution windows
Several services, one database Queue isolation by default ({prefix}.default per application), multi-service guide
Schedule Recurring jobs from code or appsettings.json, with time zones; delayed jobs; continuations; priority
See and control it Dashboard, runtime control (pause, requeue, delete), OpenTelemetry, health checks, alerts
Feed it from brokers Triggers for Kafka, RabbitMQ, AWS SQS, Azure Service Bus, Google Pub/Sub and Salesforce, and a resilient outbox for RabbitMQ and Kafka

Storage Providers

Provider Package Status
InMemory NexJob (core) Production ready
PostgreSQL NexJob.Postgres Production ready
SQL Server NexJob.SqlServer Production ready
Redis NexJob.Redis Production ready
MongoDB NexJob.MongoDB Production ready

All providers implement IRuntimeSettingsStore — runtime configuration persists across restarts.


Packages

All packages share one version number.

Package NuGet Description
NexJob.Dashboard NuGet Embedded ASP.NET Core dashboard middleware
NexJob.Dashboard.Standalone NuGet Embedded HTTP dashboard server for Worker Services
NexJob.OpenTelemetry NuGet OTel SDK instrumentation
NexJob.Trigger.AzureServiceBus NuGet Azure Service Bus trigger
NexJob.Trigger.AwsSqs NuGet AWS SQS trigger
NexJob.RabbitMQ NuGet RabbitMQ trigger & resilient outbox producer
NexJob.Kafka NuGet Apache Kafka trigger & resilient outbox producer
NexJob.Trigger.GooglePubSub NuGet Google Cloud Pub/Sub trigger
NexJob.Trigger.Salesforce NuGet Salesforce Pub/Sub API trigger (gRPC & Avro)
NexJob.Trigger.SalesforceStreaming NuGet Salesforce Streaming API trigger (CometD & Bayeux)

Dashboard

Real-time visibility into jobs, worker nodes and broker listeners, with no extra infrastructure.

NexJob Live Broker Listeners & Event Triggers

  • Live cluster status, Ctrl + K job search and a topology map from triggers to queues to worker nodes.
  • Streaming logs (SSE), progress bars and five themes.
  • Opt-in scenario simulator (EnablePlayground = true, off by default) to try burst loads and failures without scripts.

Setup for ASP.NET Core and for Worker Services: Dashboard guide.


Documentation

Complete documentation is on the Documentation Site. Key pages:


Samples

Runnable examples for the web, worker, storage and broker scenarios are in samples/, with the full catalog and a Docker Compose stack (PostgreSQL, Redis, RabbitMQ, Kafka) in samples/README.md.


Benchmarks

Hangfire is the usual reference for .NET background jobs, so it is the comparison here.

The figures below measure the cost of enqueueing one job, not processing throughput. Measured per individual enqueue operation with the benchmark in this repository (BenchmarkDotNet 0.15.8, .NET 8.0.31, RyuJIT, Intel Xeon E5-2667 v4, in-memory storage on both sides, Hangfire.Core 1.8.25 and Hangfire.InMemory 0.9.0), a job with no payload:

Metric NexJob Hangfire Comparison
Latency (mean) 34.5 µs 36.7 µs Same range; the difference is within the noise of the run
Latency (median) 31.7 µs 36.6 µs
Memory (allocated) 4.82 KB 12.57 KB 62% less memory
GC Gen0 (per 1k ops) 0.12 0.98 8× fewer Gen0 collections
GC Gen1 (per 1k ops) 0.00 0.24 No Gen1 collections

With a 1 KB payload NexJob took 44 µs and allocated 6 KB, against 54 µs and 26 KB for Hangfire; with 10 KB, 123 µs and 30 KB against 103 µs and 143 KB. Latency differs little between the two; the difference that holds in every run is allocation. This is enqueue only: processing throughput, a database storage and concurrent enqueue were not measured in this run. Earlier versions of this page showed 13.35 µs and 2.10 KB for NexJob; that figure did not reproduce, see #511.

Run it yourself: cd benchmarks/NexJob.Benchmarks && dotnet run -c Release -- --filter *EnqueueLatencyBenchmark*.

Benchmarks can be parameterized by payload size (PayloadBytes: 0, 1024, 10240) and concurrency levels (ConcurrencyLevel: 10, 50) in benchmarks/NexJob.Benchmarks.


Ecosystem & Companion Projects

  • qKafka: If you need event-driven choreographies, distributed sagas with compensating transactions, and native Kafka stream state machines, check out qKafka. NexJob focuses on background job scheduling and resilient outbox dispatch, seamlessly bridging events to and from Kafka.

Contributing

Found a bug or have an idea? Open an issue after searching the existing ones. To send code, read CONTRIBUTING.md: branch from develop, open the pull request against develop, build with zero warnings, and cover the change with tests (positive, negative and invalid input). Vulnerabilities go through the private channel in SECURITY.md, not a public issue.

Releases

The history is in the GitHub releases and the CHANGELOG. Security issues: see SECURITY.md.



Built with obsession over developer experience and production reliability.


License: MIT    © 2025 Luciano Azevedo