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.1.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.1-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: outbox_pattern-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 a39e861a323db6253d87d32f4b90f159a1b0e7d638407d79768a99b1d56dc545
MD5 0ee0d0317c31d775b74893d6848c8c52
BLAKE2b-256 7c3a2517bd3d22014c6038a92b705be7c0633d1aee7b807aec2b2b160d88283c

See more details on using hashes here.

Provenance

The following attestation bundles were made for outbox_pattern-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: outbox_pattern-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 15.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8a5ebcbd42c3b9c4dc991444348f4b48b59f774cfd174f11f34c81bde827178c
MD5 7ad52b98a377a88825eb85161f50ff97
BLAKE2b-256 65130b1f8566ca4d31c163df732be9346b0eb9776172f7df645e952793c1f75f

See more details on using hashes here.

Provenance

The following attestation bundles were made for outbox_pattern-0.1.1-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