DBOS Transact

repository·main·Indexed 23 days ago

https://github.com/dbos-inc/dbos-transact-py

A Python library for building lightweight, durable workflows and queues on top of a Postgres database. It provides fault tolerance and reliability through state checkpointing using @DBOS.step() and @DBOS.workflow() annotations. Key features include durable queues for background tasks, exactly-once event processing for webhooks and Kafka, cron scheduling, durable sleep, and programmatic workflow management via DBOSClient.

Tokens
13.8K
Snippets
19
Records
104
Agent score
81%

What's inside dbos-transact-py

  1. Use durable queues for background tasks

    main

    DBOS queues allow you to run tasks (single steps or entire workflows) in the background using Postgres as the backend. DBOS guarantees task completion and result retrieval even if the application is interrupted.

    Key capabilities include:

    • Flow control (limiting concurrency per queue or process).
    • Task timeouts and rate limiting.
    • Task deduplication and prioritization.

    To use a queue, instantiate a Queue object and use queue.enqueue(function, args) to add tasks. You can then use the returned handle to retrieve results via handle.get_result().

    from dbos import DBOS, Queue
    
    queue = Queue("example_queue")
    
    @DBOS.step()
    def process_task(task):
      ...
    
    @DBOS.workflow()
    def process_tasks(tasks):
      task_handles = []
      # Enqueue each task so all tasks are processed concurrently.
      for task in tasks:
        handle = queue.enqueue(process_task, task)
        task_handles.append(handle)
      # Wait for each task to complete and retrieve its result.
      # Return the results of all tasks.
      return [handle.get_result() for handle in task_handles]
  2. How durable workflows work in DBOS

    main

    DBOS workflows make your program durable by checkpointing its state in Postgres. If your program fails, it will automatically resume from the last completed step upon restart. You implement this by annotating ordinary Python functions as @DBOS.step() for individual units of work and @DBOS.workflow() for the orchestrating function that calls those steps.

    This pattern is ideal for:

    • Orchestrating business processes that require seamless recovery.
    • Building fault-tolerant data pipelines.
    • Operating AI agents or applications relying on non-deterministic APIs.
    from dbos import DBOS
    
    @DBOS.step()
    def step_one():
        ...
    
    @DBOS.step()
    def step_two():
        ...
    
    @DBOS.workflow()
    def workflow():
        step_one()
        step_two()
  3. Compare DBOS with other orchestration systems

    main

    DBOS is a lightweight, Postgres-backed library for durable execution. Use the following comparisons to decide if DBOS fits your requirements:

    DBOS vs. Temporal

    • DBOS: Best if you want to add durable workflows with minimal rearchitecting and already use Postgres. It is implemented as a library where you annotate workflows and steps.
    • Temporal: Best if you do not want to add Postgres to your stack or require a language not yet supported by DBOS. Temporal requires an externally orchestrated server and moving workflows/activities to a Temporal worker.

    DBOS vs. Airflow

    • DBOS: Best for general-purpose workflows written as code, especially when you need higher performance for streaming or real-time use cases. It requires only Postgres.
    • Airflow: Best if you need Airflow's specific ecosystem of out-of-the-box connectors. Airflow is designed for batch operations and requires workflows to be written as explicit DAGs orchestrated from an Airflow cluster.

    DBOS vs. Celery/BullMQ

    • DBOS: Best when you need the reliability of enqueueing tasks from durable workflows. DBOS queues are durable and Postgres-backed, meaning workflows and tasks are checkpointed to guarantee completion even during failures.
    • Celery/BullMQ: Best if you do not need durability or require extremely high throughput that exceeds what your Postgres server can handle. These are typically Redis-backed and do not provide workflow abstractions.
  4. Create a new release

    main

    To create a new release, use the make_release.py script. This command tags the latest commit with the specified version and creates a corresponding release branch. Version numbers must follow semver. If no version is provided, the script automatically increments the last released minor version.

    python3 make_release.py [--version_number <version>]
  5. Install preview or test versions

    main

    The project supports different versioning schemes for development and testing:

    • Preview Versions: PEP440-compliant alpha versions published from main. Version format: <next-release-version>a<number-of-git-commits-since-release>. Install via: pip install --pre dbos.
    • Test Versions: Built from feature branches. Version format: <next-release-version>a<number-of-git-commits-since-release>+<git-hash>.
    pip install --pre dbos
  6. Install `pdm` and set up the development environment

    main

    This project uses pdm for package and virtual environment management.

    1. Install pdm: Run the installation script via curl. Note that pdm is installed in ~/.local/bin, which may need to be added to your PATH.
    2. Install system dependencies: On Ubuntu, you may need to install the venv package for your specific Python version before installing pdm.
    3. Install project dependencies: Use pdm install to set up the environment. If a virtual environment already exists, pdm will use it; otherwise, it creates one in .venv.
  7. Run unit tests and type checks

    main

    To ensure code quality, you can run the test suite and type checker using pdm.

    Running Tests: Tests require a Postgres database running on localhost:5432. You can start a local instance using the provided Docker starter script. Note that tests involving Kafka will be skipped if Kafka is not available.

    Type Checking: Use mypy to verify type safety across the project.

  8. Implement exactly-once event processing

    main

    DBOS enables reliable event processing (e.g., webhooks, Kafka consumers) by ensuring workflows start exactly once in response to an event.

    For webhooks, use SetWorkflowID(event_id) as a context manager to provide an idempotency key. For Kafka, use the @DBOS.kafka_consumer(config, [topics]) decorator on a @DBOS.workflow() function to ensure each message triggers a workflow exactly once.

    # Webhook example
    def handle_message(request: Request) -> None:
      event_id = request.body["event_id"]
      # Use the event ID as an idempotency key to start the workflow exactly-once
      with SetWorkflowID(event_id):
        # Start the workflow in the background, then acknowledge the event
        DBOS.start_workflow(message_workflow, request.body["event"])
    
    # Kafka example
    @DBOS.kafka_consumer(config,["alerts-topic"])
    @DBOS.workflow()
    def process_kafka_alerts(msg):
        # This workflow runs exactly-once for each message sent to the topic
        alerts = msg.value.decode()
        for alert in alerts:
            respond_to_alert(alert)
  9. Deploy a DBOS app to DBOS Cloud

    main

    To deploy your application to DBOS Cloud, you need the DBOS Cloud CLI (which requires Node.js).

    1. Install the CLI globally:
      npm i -g @dbos-inc/dbos-cloud
    2. Deploy the application:
      dbos-cloud app deploy

    After deployment, the command will output a URL where your app is live. You can monitor the application's status and view logs in the DBOS Cloud Console.

    npm i -g @dbos-inc/dbos-cloud
    
    dbos-cloud app deploy
  10. Run a DBOS app locally with Docker and Postgres

    main

    To run a DBOS application locally, you must have a Postgres database available. If you use Docker, you can use the provided helper script to start a database instance. After the database is running, use the DBOS CLI to migrate the database schema and start the application server.

    1. Start Postgres via Docker:
      export PGPASSWORD=dbos
      python3 start_postgres_docker.py
    2. Run migrations and start the app:
      dbos migrate
      dbos start

    Once started, the application is typically accessible at http://localhost:8000.

    export PGPASSWORD=dbos
    python3 start_postgres_docker.py
    
    dbos migrate
    dbos start
  11. Use dbos-idempotency-key for idempotent workflows in Flask

    main
    The FlaskMiddleware looks for a specific header to establish workflow identity. If the header dbos-idempotency-key is present in the incoming HTTP request, the middleware uses its value to set the workflow_id for the duration of the request handler. This allows you to restart or retry requests with the same key to ensure the workflow is executed exactly once.