Skip to content

Relay ​

The Relay lets the processes of your system talk to each other through the datastore BullMQ already uses (Redis or PostgreSQL). Any process can publish a message on a topic, and every subscription that matches it receives the message, on whatever machine it runs.

typescript
import { Relay } from 'bullmq';

const relay = new Relay({ connection: { host: 'localhost', port: 6379 } });

// On any process:
await relay.subscribe('orders.*.status', message => {
  console.log(message.topic, message.data);
});

// On any other process:
await relay.publish('orders.42.status', { status: 'shipped' });

WARNING

The Relay is experimental. Its API may change in minor releases while it is being completed.

What it is for ​

Queues are for work: every job is processed by exactly one worker, with retries, rate limits and persistence. The Relay is for telling other processes that something happened, or what the current state of something is:

  • Showing live job progress in the API server a user is connected to, while the job runs on a worker somewhere else (example).
  • Pushing real-time notifications to the API server that holds a user's WebSocket connection (example).
  • Invalidating in-memory caches on every instance of a service (example).
  • Distributing live configuration and feature flags (example).
  • Controlling a fleet of workers: changing their concurrency, pausing them (example).
  • Cancelling jobs running on other workers (example).

How it compares ​

QueueQueueEventsRedis pub/subRelay
Each message goes toOne workerEvery listenerEvery subscriberEvery matching subscription
What you can publishJobsQueue events onlyAnythingAnything (JSON)
AddressingQueue nameQueue nameChannel or globTopic keypaths with * and > wildcards
Process briefly disconnectedJobs waitResumes from its last idMessages are lostMessages wait in its inbox
Latest value for new listeners–––Retained messages
DatastoresRedis, PostgreSQLRedis, PostgreSQLRedisRedis, PostgreSQL

For a comparison with event-streaming platforms, see Relay compared to Kafka.

How it works ​

Every process that creates a Relay is a node. A node registers itself in the datastore, keeps a lease alive with heartbeats, and has its own inbox.

When a message is published, the datastore itself finds every subscription whose pattern matches the topic, and appends one entry to the inbox of each node involved, listing which of that node's subscriptions it is for. Each node reads its inbox and calls the listeners.

  • Routing happens in the datastore. All the logic (topic matching, subscriptions, leases, inboxes) runs in atomic Lua scripts on Redis and SQL functions on PostgreSQL. The Relay class only reads its inbox and calls your listeners, so every BullMQ runtime behaves exactly the same.
  • Inboxes are durable. A node that briefly loses its connection reads the messages published in the meantime when it reconnects. A node that stops renewing its lease (it crashed, or was frozen for too long) is removed together with its subscriptions and inbox.
  • Only interested nodes are reached. A message is written only to the inboxes of nodes with a matching subscription.

Read more in Nodes and failures and Backends.

Contents ​

Released under the MIT License.