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.0.tar.gz (9.0 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.0-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: outbox_pattern-0.1.0.tar.gz
  • Upload date:
  • Size: 9.0 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.0.tar.gz
Algorithm Hash digest
SHA256 6d9fb753b30830ae05935d27fc2951fc5d4817ea027768dff0e8a8edf7c39a3b
MD5 98b8d06f77129724ee9726d5470c905d
BLAKE2b-256 4684c7d1b9b6e80f137c30ffb1c3238c639d70ffd8f022106ae573b67ea49395

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: outbox_pattern-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a718f93fb875e5ee97ba2d03ccbca21a089c376ae31d2a543464f27d61d0e762
MD5 32a83d981cc113ca483a6106bd4ff1d6
BLAKE2b-256 5ebfa09868cdc7523c2172f16839d2eb79b6a7dc3ffd7071651c17cdaf4774d2

See more details on using hashes here.

Provenance

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