pengdows.hangfire 2.0.0

dotnet add package pengdows.hangfire --version 2.0.0
                    
NuGet\Install-Package pengdows.hangfire -Version 2.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="pengdows.hangfire" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="pengdows.hangfire" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="pengdows.hangfire" />
                    
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 pengdows.hangfire --version 2.0.0
                    
#r "nuget: pengdows.hangfire, 2.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 pengdows.hangfire@2.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=pengdows.hangfire&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=pengdows.hangfire&version=2.0.0
                    
Install as a Cake Tool

pengdows.hangfire

SQL-first Hangfire job storage built on pengdows.crud — a strongly-typed, cross-database data access layer for .NET.

Buy Me A Coffee

Supported Databases

Database Notes
PostgreSQL
SQL Server
Oracle
Firebird
CockroachDB
MariaDB
MySQL
SQLite
DuckDB
YugabyteDB
TiDB

Snowflake is not supported. pengdows.crud includes a Snowflake driver, but Snowflake is designed for analytical workloads — columnar storage, warehouse-level concurrency, and high per-query latency. Hangfire requires row-level locking, low-latency queue polling, high-frequency small writes and deletes, and reliable distributed lock semantics. These requirements are fundamentally at odds with Snowflake's architecture.

SQLite and DuckDB in Production

SQLite and DuckDB are fully supported for production use. pengdows.crud's SingleWriter mode serializes all write operations, eliminating the SQLITE_BUSY / database is locked errors that plague naive multi-threaded use of these engines. The PoolGovernor prevents connection pool saturation by bounding concurrent database access, so Hangfire's background threads cannot starve the rest of the application.

If your application already uses pengdows.crud with the same IDatabaseContext, Hangfire shares that context and its governance. Pool exhaustion from runaway background jobs becomes a non-issue.

If the rest of your application does not use pengdows.crud, you can still isolate Hangfire's impact by giving it a dedicated connection string pointing to the same database. ADO.NET pools connections per unique connection string, so Hangfire gets its own pool and cannot compete with your application's connections regardless of load.

Requirements

  • .NET 8.0
  • Hangfire.Core ≥ 1.8.x
  • pengdows.crud 2.x
  • An ADO.NET driver for your chosen database

Installation

dotnet add package pengdows.hangfire

Quick Start

using pengdows.crud;
using pengdows.crud.configuration;
using Microsoft.Data.Sqlite;  // swap for your ADO.NET driver
using Hangfire;

// SQLite
IDatabaseContext databaseContext = new DatabaseContext(
    new DatabaseContextConfiguration
    {
        ConnectionString = "Data Source=hangfire.db"
    },
    SqliteClientFactory.Instance);

// SQL Server — only the connection string and factory change
// IDatabaseContext databaseContext = new DatabaseContext(
//     new DatabaseContextConfiguration
//     {
//         ConnectionString = "Server=.;Database=myapp;Trusted_Connection=true"
//     },
//     SqlClientFactory.Instance);

GlobalConfiguration.Configuration
    .UsePengdowsCrudStorage(databaseContext, options =>
    {
        options.AutoPrepareSchema = true;
        options.QueuePollInterval = TimeSpan.FromSeconds(5);
    });

AutoPrepareSchema = true (the default) creates all required tables on first run. Set it to false if you manage schema migrations yourself. On schema-capable databases, pengdows.hangfire always uses the built-in HangFire schema. Custom schema names are not supported.

Configuration

All options are set via PengdowsCrudStorageOptions:

Option Default Description
SchemaName hangfire Obsolete and ignored. Custom database schemas are not supported
AutoPrepareSchema true Create schema tables on initialization if they do not exist
QueuePollInterval 5 sec How long a worker waits between queue polls when idle
QueuePollJitter true Randomize poll sleep to prevent thundering-herd on idle queues
JobExpirationCheckInterval 30 min How often the expiration manager purges expired jobs
CountersAggregateInterval 5 min How often fine-grained counters are rolled up into aggregates
DistributedLockTtl 5 min How long a distributed lock row lives before it can be force-taken
AdditionalMetricsContexts [] Extra database contexts to include in the Hangfire dashboard metrics

Architecture

All database access goes through gateway classes that extend TableGateway<TEntity, TId> from pengdows.crud. Each gateway owns exactly one table and exposes a typed interface.

Hangfire → PengdowsCrudJobStorage
              ├── PengdowsCrudConnection        (IStorageConnection)
              │     └── PengdowsCrudWriteOnlyTransaction  (IWriteOnlyTransaction)
              ├── PengdowsCrudMonitoringApi      (IMonitoringApi)
              ├── PengdowsCrudDistributedLock    (IDistributedLock)
              └── Background processes
                    ├── ExpirationManager
                    └── CountersAggregator

SQL is built exclusively with SqlContainer from pengdows.crud — quoted identifiers via AppendName() / WrapObjectName(), parameterized values via AppendParam(). No string interpolation for SQL values, ever.

Transactions are handled by collecting Func<IDatabaseContext, Task> commands in PengdowsCrudWriteOnlyTransaction and executing them atomically via BeginTransactionAsync() at commit time.

Schema Management

By default (AutoPrepareSchema = true) the schema is installed automatically on first use.

For manual schema management, two options are provided:

  • DefaultInstall.sql — SQL Server T-SQL dialect, ships with the package as an embedded resource
  • Liquibase/ — versioned, cross-database migrations for all supported databases (inside the pengdows.hangfire NuGet package source tree at pengdows.hangfire/Liquibase/)

The schema is versioned. Running against an existing schema applies only the missing migrations.

Running Liquibase Migrations

The changelog entry point is Liquibase/changelog-master.xml. The migrations target the built-in HangFire schema. Override parameters are not supported by the runtime storage and should not be used for deployed schemas.

Liquibase CLI (standalone)

Place your database JDBC driver in a local drivers/ directory, then run:

liquibase \
  --changelog-file=changelog-master.xml \
  --search-path=/path/to/pengdows.hangfire/Liquibase \
  --driver-classpath=drivers/your-jdbc-driver.jar \
  --url="jdbc:postgresql://localhost:5432/myapp" \
  --username=myuser \
  --password=mypassword \
  update

JDBC URL examples by database:

Database JDBC URL
PostgreSQL jdbc:postgresql://host:5432/database
SQL Server jdbc:sqlserver://host:1433;databaseName=database
MySQL jdbc:mysql://host:3306/database
MariaDB jdbc:mariadb://host:3306/database
Oracle jdbc:oracle:thin:@host:1521/service
Firebird jdbc:firebirdsql://host:3050/database
CockroachDB jdbc:postgresql://host:26257/database
YugabyteDB jdbc:postgresql://host:5433/database
TiDB jdbc:mysql://host:4000/database
DuckDB jdbc:duckdb:/path/to/database.db
SQLite jdbc:sqlite:/path/to/database.db
docker-compose

A liquibase service is defined in docker-compose.yml. It mounts the changelogs into the container and expects JDBC drivers in a local ./liquibase_drivers/ directory. Override the command to run migrations:

# Start your target database first
docker compose up -d postgres

# Run migrations (override the default --version command)
docker compose run --rm liquibase \
  --url="jdbc:postgresql://postgres:5432/myapp" \
  --username=hangfire \
  --password=password \
  --parameter.schemaName=HangFire \
  update

Drop your database's JDBC .jar into ./liquibase_drivers/ before running — the container mounts that directory as /liquibase/lib/.

Building and Testing

# Build
dotnet build pengdows.hangfire.slnx

# Run all tests
dotnet test pengdows.hangfire.slnx

# Unit tests only (no database required — uses pengdows.crud.fakeDb)
dotnet test pengdows.hangfire.tests/pengdows.hangfire.tests.csproj

# Integration tests (uses in-memory SQLite)
dotnet test pengdows.hangfire.integration.tests/pengdows.hangfire.integration.tests.csproj

# Pack for release
dotnet pack -c Release

Unit tests use pengdows.crud.fakeDb — an in-memory mock provider that exercises SQL generation, parameter binding, and return value handling without a real database. Integration tests use SQLite via an in-process fixture.

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 was computed.  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 was computed.  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
2.0.0 103 4/21/2026
1.0.0 117 3/27/2026