TickerQTickerQ

Configuration

Redis connection options and multi-node heartbeat settings.

TickerQRedisOptionBuilder extends RedisCacheOptions from Microsoft.Extensions.Caching.StackExchangeRedis, so all standard Redis cache options are available.

Options

PropertyDefaultDescription
ConfigurationRedis connection string (e.g. "localhost:6379")
ConfigurationOptionsStackExchange.Redis ConfigurationOptions object
ConnectionMultiplexerPre-built IConnectionMultiplexer; takes priority over all other connection options
InstanceName"tickerq:"Key prefix for IDistributedCache entries
NodeHeartbeatInterval1 minuteHow often this node sends a heartbeat

Connection string

opt.AddStackExchangeRedis(redis =>
{
    redis.Configuration = "redis-server:6379,password=secret,ssl=true";
});

ConfigurationOptions

For advanced scenarios, use the StackExchange.Redis configuration object:

opt.AddStackExchangeRedis(redis =>
{
    redis.ConfigurationOptions = new ConfigurationOptions
    {
        EndPoints = { "redis-primary:6379", "redis-replica:6379" },
        Password = "secret",
        Ssl = true,
        AbortOnConnectFail = false,
        ConnectTimeout = 5000,
    };
});

Passing an existing multiplexer

If you already have an IConnectionMultiplexer instance, assign it directly so TickerQ reuses the same connection instead of opening a new one:

builder.Services.AddSingleton<IConnectionMultiplexer>(_ =>
    ConnectionMultiplexer.Connect("localhost:6379"));

builder.Services.AddTickerQ((sp, opt) =>
{
    opt.AddStackExchangeRedis(redis =>
    {
        redis.ConnectionMultiplexer = sp.GetRequiredService<IConnectionMultiplexer>();
    });
});

Using a factory

If your multiplexer is created asynchronously (e.g. via an async factory), use the ConnectionMultiplexerFactory property inherited from RedisCacheOptions. TickerQ invokes the factory once when the singleton is first resolved and caches the result:

builder.Services.AddTickerQ((sp, opt) =>
{
    opt.AddStackExchangeRedis(redis =>
    {
        redis.ConnectionMultiplexerFactory = async () => await ConnectionMultiplexer.ConnectAsync("localhost:6379");
    });
});

Connection precedence (first match wins): ConnectionMultiplexerConnectionMultiplexerFactoryConfigurationOptionsConfiguration.

DI isolation

TickerQ registers its own IConnectionMultiplexer and IDatabase under the keyed DI key "tickerq". This means:

  • The host application's unkeyed IConnectionMultiplexer registration is never modified or replaced.
  • TickerQ always resolves its own connection through the "tickerq" key regardless of what else is in the DI container.
  • Sharing is explicit opt-in: pass your multiplexer via redis.ConnectionMultiplexer and TickerQ will register that instance under its own key.
// Host app — unkeyed slot, owned by the host
services.AddSingleton<IConnectionMultiplexer>(myMultiplexer);

// TickerQ — keyed slot "tickerq", completely independent
services.AddTickerQ(opt =>
    opt.AddStackExchangeRedis(redis => redis.Configuration = "localhost:6379"));

// Both coexist without conflict
var hostMultiplexer  = sp.GetRequiredService<IConnectionMultiplexer>();            // myMultiplexer
var tickerMultiplexer = sp.GetRequiredKeyedService<IConnectionMultiplexer>("tickerq"); // TickerQ's own

Heartbeat interval

Each node writes a hb:<node> key with a TTL of interval + 20 seconds. Other nodes detect a node as dead when its heartbeat key expires.

opt.AddStackExchangeRedis(redis =>
{
    redis.Configuration = "localhost:6379";
    redis.NodeHeartbeatInterval = TimeSpan.FromSeconds(30); // more aggressive detection
});

Multi-node lifecycle

  1. Startup — node sends an initial heartbeat and registers in nodes:registry
  2. Every interval — node checks for dead nodes, recovers their locked jobs, then sends a fresh heartbeat
  3. Dead-node recovery — Lua script atomically clears LockHolder, sets status to Idle, and re-queues the job
  4. Shutdown — heartbeat key expires after TTL, other nodes detect and recover resources

On this page