Skip to main content

A reliable, lightweight, framework-agnostic Transactional Outbox pattern implementation for Python (FastAPI, Flask, etc.).

Project description

Outbox Pattern SDK (outbox-pattern)

PyPI version License: MIT

A lightweight, reliable, framework-agnostic implementation of the Transactional Outbox Pattern in Python. Built to work seamlessly with FastAPI (Async), Flask (Sync), or any custom Python application.


Key Features

  • 🔄 Transactional Outbox Pattern: Ensures event publication consistency by storing outbox events atomically before dispatching.
  • 🚀 Framework Agnostic: Native support for FastAPI (asyncio lifespan) and Flask (threading daemon).
  • Async & Sync Support: Native implementations for both asynchronous (asyncio) and synchronous (threading) workloads.
  • 🐇 RabbitMQ Integration: Built-in publishers for aio-pika (async) and pika (sync).
  • 🍃 MongoDB Integration: Outbox repositories powered by beanie (async) and pymongo (sync).
  • 📊 Telemetry & Observability: Integrated provider for Pydantic Logfire with structured logs and dead-letter event tracing.
  • 🛡️ Distributed Lock Safety: Prevents race conditions among multiple concurrent worker instances using optimistic lock timeouts.

Installation

Install the base package or specify optional extras depending on your stack:

# Install for FastAPI (Async: Beanie + aio-pika)
pip install "outbox-pattern[async]"

# Install for Flask (Sync: PyMongo + pika)
pip install "outbox-pattern[sync]"

# Install with Telemetry (Logfire)
pip install "outbox-pattern[telemetry]"

# Install all optional dependencies
pip install "outbox-pattern[all]"

Framework Integration Examples

1. FastAPI Integration (Async)

FastAPI applications manage background workers using the lifespan context manager:

from contextlib import asynccontextmanager
from fastapi import FastAPI
from pydantic import BaseModel

from outbox_pattern import OutboxEvent
from outbox_pattern.async_impl import (
    AsyncBeanieRepository,
    AsyncAioPikaPublisher,
    AsyncOutboxWorker,
)

# Repositories & Workers
repository = AsyncBeanieRepository()
publisher = AsyncAioPikaPublisher(broker_url="amqp://guest:guest@localhost:5672/")
worker = AsyncOutboxWorker(repository=repository, publisher=publisher, poll_interval=3)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: Start the Outbox worker task
    await worker.start()
    yield
    # Shutdown: Stop worker gracefully
    await worker.stop()

app = FastAPI(title="My FastAPI Microservice", lifespan=lifespan)

class CreateUserDTO(BaseModel):
    user_id: str
    email: str

@app.post("/users")
async def create_user(dto: CreateUserDTO):
    # 1. Save user inside main DB transaction...
    
    # 2. Save Outbox Event atomically
    event = OutboxEvent(
        tenant="tenant_default",
        topic="user.registered",
        payload=dto.model_dump(),
    )
    await repository.save_event(event)
    
    return {"status": "user_created", "user_id": dto.user_id}

2. Flask Integration (Sync)

Flask applications use daemon background threads for outbox polling:

from flask import Flask, request, jsonify
from pymongo import MongoClient

from outbox_pattern import OutboxEvent
from outbox_pattern.sync_impl import (
    SyncPyMongoRepository,
    SyncPikaPublisher,
    SyncOutboxWorker,
)

app = Flask(__name__)

# Initialize MongoDB & RabbitMQ
mongo_client = MongoClient("mongodb://localhost:27017/")
db = mongo_client["my_flask_app"]

repository = SyncPyMongoRepository(collection=db["outbox_events"])
publisher = SyncPikaPublisher(broker_url="amqp://guest:guest@localhost:5672/")

# Start Outbox worker in a background daemon thread
worker = SyncOutboxWorker(repository=repository, publisher=publisher, poll_interval=5)
worker.start()

@app.route("/orders", methods=["POST"])
def create_order():
    data = request.json
    
    # 1. Save order in MongoDB...
    
    # 2. Record Outbox Event
    event = OutboxEvent(
        tenant=data.get("tenant", "default"),
        topic="order.created",
        payload=data,
    )
    repository.save_event(event)
    
    return jsonify({"message": "Order created successfully"}), 201

Observability & Telemetry

Enable Pydantic Logfire telemetry to trace event publishing lifecycle and dead-letter queues:

from outbox_pattern.telemetry import LogfireTelemetryProvider

telemetry = LogfireTelemetryProvider()

worker = AsyncOutboxWorker(
    repository=repository,
    publisher=publisher,
    telemetry=telemetry,
)

Configuration

Environment variables can be set to override default behavior:

Environment Variable Default Description
OUTBOX_MAX_RETRIES 3 Maximum processing attempts before marking an event as failed.
OUTBOX_BATCH_SIZE 100 Number of events acquired per polling loop iteration.
OUTBOX_LOCK_TIMEOUT_SECONDS 300 Lock expiration time (in seconds) for stale workers.

License

This project is licensed under the terms of the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

outbox_pattern-0.1.2.tar.gz (8.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

outbox_pattern-0.1.2-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

File details

Details for the file outbox_pattern-0.1.2.tar.gz.

File metadata

  • Download URL: outbox_pattern-0.1.2.tar.gz
  • Upload date:
  • Size: 8.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for outbox_pattern-0.1.2.tar.gz
Algorithm Hash digest
SHA256 6ca4005d71be91cb8897a14dfabe2d0a5f62f81c2d4fbf8a36233d2cc40f94a9
MD5 6c109ce448c47045a6dc5bc9f7470ab5
BLAKE2b-256 29e9533047e28a383d8856b4c42cb9b6284afc5d73aa8eec386abf2fa2255c11

See more details on using hashes here.

Provenance

The following attestation bundles were made for outbox_pattern-0.1.2.tar.gz:

Publisher: publish.yml on JohnatanPalacios/outbox_pattern

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file outbox_pattern-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: outbox_pattern-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 15.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for outbox_pattern-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e929ffd0bc424c013f5eabf151b2aa96ed76215e32db6e1468d29d44d1d294cb
MD5 ca02ac67f9ad112152c49d8336dac962
BLAKE2b-256 6226fcdbf20e57ff659eae58b7ccf06fc56500b272719d70b5da9e9c9b2d5015

See more details on using hashes here.

Provenance

The following attestation bundles were made for outbox_pattern-0.1.2-py3-none-any.whl:

Publisher: publish.yml on JohnatanPalacios/outbox_pattern

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page