Skip to content

Broker Execution Transports

In addition to local workers and serverless Google Cloud transports, litestar-queues provides four message broker execution transports:

  • Google Cloud Pub/Sub (PubSubExecutionConfig, extra litestar-queues[pubsub])
  • Amazon SQS (SqsExecutionConfig, extra litestar-queues[sqs])
  • RabbitMQ 4.3+ (RabbitMQExecutionConfig, extra litestar-queues[rabbitmq])
  • Apache Kafka (KafkaExecutionConfig, extra litestar-queues[kafka])

Runnable Configuration Example

docs/examples/execution/broker_configs.py
"""Configuring Pub/Sub, Amazon SQS, RabbitMQ, and Apache Kafka execution transports."""

from litestar_queues import QueueConfig, WorkerConfig
from litestar_queues.backends.redis import RedisBackendConfig
from litestar_queues.execution.kafka import KafkaExecutionConfig
from litestar_queues.execution.pubsub import PubSubExecutionConfig
from litestar_queues.execution.rabbitmq import RabbitMQExecutionConfig
from litestar_queues.execution.sqs import SqsExecutionConfig


def build_broker_configs() -> dict[str, QueueConfig]:
    """Construct QueueConfig instances for all four message broker execution transports."""
    storage = RedisBackendConfig(url="redis://redis.internal:6379/0")
    return {
        "pubsub": QueueConfig(
            queue_backend=storage,
            execution_backend=PubSubExecutionConfig(
                project_id="my-gcp-project",
                topic_id="litestar-queue-tasks",
                subscription_id="litestar-queue-tasks-sub",
                ack_deadline=60,
                ack_extension_interval=30,
            ),
            worker=WorkerConfig(placement="external"),
        ),
        "sqs": QueueConfig(
            queue_backend=storage,
            execution_backend=SqsExecutionConfig(
                queue_url="https://sqs.us-east-1.amazonaws.com/123456789012/litestar-queues",
                region_name="us-east-1",
                wait_time_seconds=20,
                visibility_timeout=60,
                visibility_extension_interval=30,
            ),
            worker=WorkerConfig(placement="external"),
        ),
        "rabbitmq": QueueConfig(
            queue_backend=storage,
            execution_backend=RabbitMQExecutionConfig(
                amqp_url="amqps://app:secret@rabbitmq.internal:5671/prod",
                queue_name="litestar-queues.tasks",
                declare_queue=True,
                delayed_retry_type="returned",
            ),
            worker=WorkerConfig(placement="external"),
        ),
        "kafka": QueueConfig(
            queue_backend=storage,
            execution_backend=KafkaExecutionConfig(
                bootstrap_servers="kafka-1.internal:9092,kafka-2.internal:9092",
                topic="litestar-queues-tasks",
                consumer_group="litestar-queues-consumers",
            ),
            worker=WorkerConfig(placement="external"),
        ),
    }


async def main() -> None:
    """Verify broker execution configurations validate cleanly."""
    configs = build_broker_configs()
    assert set(configs) == {"pubsub", "sqs", "rabbitmq", "kafka"}
    pubsub_backend = configs["pubsub"].execution_backend
    assert isinstance(pubsub_backend, PubSubExecutionConfig)
    assert pubsub_backend.topic_path == "projects/my-gcp-project/topics/litestar-queue-tasks"

Shared Broker Architecture: Dispatcher + Consumer Fleet

Unlike Cloud Tasks (schedules_on_enqueue=True), general-purpose message brokers do not natively store 30-day delayed schedules, uniqueness reservations, or fenced retry state. litestar-queues therefore pairs your authoritative storage backend with a two-role broker topology:

  1. Worker Dispatcher (litestar queues run): Claims due pending and scheduled records from the storage backend, reserves a monotonic attempt generation (litestar_queues_attempt), and publishes an ID-only message ({"version": 1, "task_id": "<uuid>"}) to the broker topic or queue.
  2. Broker Consumer Fleet (litestar queues run-consumer --backend <name>): Pulls ID-only messages from the broker subscription, verifies the attempt generation fence against the storage backend, executes the task body while sending periodic worker heartbeats to the storage backend, and acknowledges the broker message once the attempt reaches a durable storage transition.

ID-Only Wire Envelope & Generation Fencing

Every broker message payload contains only {"version": 1, "task_id": "<uuid>"} plus transport metadata headers (litestar_queues_attempt, litestar_queues_queue, litestar_queues_task_name). Task arguments, keyword arguments, results, and metadata never travel across the broker wire.

Because message brokers provide at-least-once delivery, duplicate or delayed broker redeliveries are fenced against the storage record's retry_count and execution_ref:

  • If a duplicate broker message arrives while the attempt is already terminal or has rotated to a newer retry_count, the consumer acknowledges and drops the stale message without re-executing the task.
  • If a consumer crashes mid-attempt, QueueMaintenanceService (or the dispatcher's reconciliation pass) rotates the stale attempt after dispatch_stale_after seconds and republishes a fresh generation.
  • Broker-level redelivery timers (such as SQS message visibility, Pub/Sub ack deadlines, or RabbitMQ consumer timeouts) govern only transport message redelivery; authoritative task ownership is always fenced in the storage backend via expected_retry_count.