ITB.HangfirePostgreSql.ValkeyQueue 0.1.2-alpha

This is a prerelease version of ITB.HangfirePostgreSql.ValkeyQueue.
There is a newer version of this package available.
See the version list below for details.
dotnet add package ITB.HangfirePostgreSql.ValkeyQueue --version 0.1.2-alpha
                    
NuGet\Install-Package ITB.HangfirePostgreSql.ValkeyQueue -Version 0.1.2-alpha
                    
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="ITB.HangfirePostgreSql.ValkeyQueue" Version="0.1.2-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ITB.HangfirePostgreSql.ValkeyQueue" Version="0.1.2-alpha" />
                    
Directory.Packages.props
<PackageReference Include="ITB.HangfirePostgreSql.ValkeyQueue" />
                    
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 ITB.HangfirePostgreSql.ValkeyQueue --version 0.1.2-alpha
                    
#r "nuget: ITB.HangfirePostgreSql.ValkeyQueue, 0.1.2-alpha"
                    
#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 ITB.HangfirePostgreSql.ValkeyQueue@0.1.2-alpha
                    
#: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=ITB.HangfirePostgreSql.ValkeyQueue&version=0.1.2-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=ITB.HangfirePostgreSql.ValkeyQueue&version=0.1.2-alpha&prerelease
                    
Install as a Cake Tool

ITB.HangfirePostgreSql.ValkeyQueue

NuGet License: MIT

A Valkey/Redis-backed queue provider for Hangfire.PostgreSql. PostgreSQL stays the durable source of truth (job data, state history, scheduled/recurring jobs, dashboard); only the hot queue path (job IDs) moves to Valkey — LPUSH to enqueue, blocking BLMOVE to dequeue — so idle worker polling load on PostgreSQL drops toward zero.

In a measured run, an idle Hangfire server's PostgreSQL load fell by 97.6 % (1.37 → 0.03 queries/s). Per-job cost fell 12.5 %, and throughput was unchanged. Full methodology, environment and caveats: docs/LOAD-TEST.md.

Prerelease. On Hangfire.PostgreSql 1.21.1 the baseline already wakes idle workers via PostgreSQL LISTEN/NOTIFY, so the pickup-latency win is marginal and the reduction under sustained load is modest. The win is idle database load. Validate against your own workload before relying on it, and keep pure-PostgreSQL as the fallback.

Installation

dotnet add package ITB.HangfirePostgreSql.ValkeyQueue

Targets .NET 10. Requires Hangfire.PostgreSql 1.21.x and a Valkey (or Redis) 7+ server.

Usage

using Hangfire;
using Hangfire.PostgreSql;
using Hangfire.PostgreSql.Factories;
using Hangfire.PostgreSql.ValkeyQueue;
using Hangfire.Server;
using StackExchange.Redis;

var storageOptions = new PostgreSqlStorageOptions { UseSlidingInvisibilityTimeout = true };
var storage = new PostgreSqlStorage(
    new NpgsqlConnectionFactory(pgConnectionString, storageOptions), storageOptions);

var redisOptions = ConfigurationOptions.Parse(valkeyConnectionString);
var mux = ConnectionMultiplexer.Connect(redisOptions);

var valkey = new ValkeyQueueOptions
{
    InvisibilityTimeout = TimeSpan.FromMinutes(30),   // MUST exceed your longest job
    BlockingConnectionConfig = redisOptions,          // required on managed Valkey (TLS + AUTH)
};

storage.UseValkeyQueues(mux, valkey);                 // point the "default" queue at Valkey

services.AddHangfire(c => c.UseStorage(storage));
services.AddHangfireServer();

// Safety nets — recover from Valkey data loss / worker death. Register as a singleton
// IBackgroundProcess so AddHangfireServer picks it up.
services.AddSingleton<IBackgroundProcess>(
    new ValkeyQueueMaintenance(mux, valkey, pgConnectionString));

A complete, runnable worked example — including the ValkeyQueueMaintenance registration that is easy to forget — is in docs/EXAMPLE.md.

Configuration

ValkeyQueueOptions:

Option Default Notes
KeyPrefix hangfire: Prefix for every Valkey key this provider owns.
BlockTimeout 2 s How long a single BLMOVE blocks before the worker loops.
InvisibilityTimeout 30 min A job in a processing list longer than this is treated as orphaned. Must exceed your longest job.
MaintenanceInterval 15 s How often the reconciler and orphan sweep run.
ReconcileGrace 30 s The reconciler ignores jobs enqueued more recently than this, so it never races an in-flight LPUSH.
Schema hangfire The schema Hangfire.PostgreSql created its tables in.
Queues ["default"] Queues served by Valkey. Anything not listed stays on PostgreSQL.
BlockingConnectionConfig null ConfigurationOptions for the dedicated blocking connections. Set this whenever AUTH/TLS is in play — see below.

Correctness

The enqueue is no longer transactional with job creation (it pushes to Valkey, not PostgreSQL), so two safety nets close every failure window:

  • Reconciler re-enqueues jobs PostgreSQL reports as Enqueued but missing from Valkey (Valkey data loss / failover).
  • Orphan sweep requeues jobs stuck in a processing list past InvisibilityTimeout (worker died mid-job).

Both live in ValkeyQueueMaintenance. Without it registered, those failures are silent job loss — this is the one registration step you cannot skip.

Duplicates are execution-safe: only one worker wins the Enqueued → Processing state transition in PostgreSQL; the loser is discarded.

Notes

  • One dedicated blocking connection per worker thread (BLMOVE monopolises its connection) — fine for tens of workers, revisit for hundreds.
  • On managed Valkey (ElastiCache with TLS + AUTH) set BlockingConnectionConfig; otherwise the blocking connections are derived from the shared multiplexer's connection string, which masks the password and every blocking connection fails to authenticate.
  • InvisibilityTimeout must exceed your longest-running job or the sweep will re-flag it.
  • Keys are wrapped in a {hash tag} so all three keys for a queue land in the same cluster hash slot; without it, cluster mode rejects the BLMOVE with CROSSSLOT.

Development

# Postgres + Valkey for the tests (pg_stat_statements is only needed for benchmarks)
docker run -d --name hfvq-pg -e POSTGRES_PASSWORD='Asd123!1' \
  -e POSTGRES_DB=hangfire_bench -p 55432:5432 postgres:17-alpine
docker run -d --name hfvq-valkey -p 56379:6379 valkey/valkey:8-alpine

dotnet test

The suite covers the queue hand-off, both recovery paths, and the cluster hash-slot invariant. Endpoints are overridable via POSTGRES_TEST_CONNECTION, REDIS_TEST_HOST and REDIS_TEST_PORT.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET 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.1.0 142 9/28/2026
1.0.2 185 9/22/2026
1.0.1 100 9/21/2026
1.0.0 86 9/21/2026
0.1.2-alpha 85 9/21/2026