Skip to main content

Worker Architecture

Current Design

Urblock uses BullMQ for async job processing. All 16 queues (token deploy, tx submit, tx confirm, webhook send, etc.) are currently processed in-process within the main API server.

This simplifies deployment but means CPU-intensive operations (e.g., large batch deploys) compete with HTTP request handling.

To separate workers from the API:

1. Set WORKER_MODE environment variable

# API-only (no queue processors)
WORKER_MODE=api

# Workers-only (no HTTP routes)
WORKER_MODE=workers

# Both (current behavior, suitable for dev/small deployments)
WORKER_MODE=both

2. Docker Compose setup

services:
api:
build: ./apps/api
environment:
- WORKER_MODE=api
ports:
- "3000:3000"

workers:
build: ./apps/api
environment:
- WORKER_MODE=workers
deploy:
replicas: 2 # Scale workers independently

3. Implementation

The QueueModule conditionally registers processors:

// In queue.module.ts
const isWorkerMode = process.env.WORKER_MODE !== 'api';
const processors = isWorkerMode
? [DeadLetterProcessor, TxSubmitProcessor, TxConfirmProcessor, ...]
: [];

This allows the same codebase to run in both modes without code splitting.

Benefits

  • Independent scaling: Run 5 workers while keeping 2 API replicas
  • Resource isolation: Worker OOMs don't crash the API
  • Deployment: Roll out worker changes without API downtime
  • Monitoring: Separate metrics/alerts for API latency vs queue throughput