GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Docs
  • Quickstart
  • Examples
  • AI Agents
  • DBOS Console
  • Why DBOS?
  • DBOS Architecture
  • Develop with Python
  • Add DBOS To Your App
  • AI-Assisted Development
  • Tutorials
  • Steps
  • Transactions & Datasources
  • Queues & Concurrency

This view

  • Original page
  • New address

GeezerVison is showing docs.dbos.dev — External Storage | DBOS Docs

info

External storage is a DBOS Transact Enterprise feature.

DBOS checkpoints workflow inputs, step outputs, and workflow outputs in your system database, which isn't a good fit for large objects such as files or datasets. With external storage, you keep large objects in a separate store such as Amazon S3 and checkpoint only a small reference to each one.

Setting Up External Storage​

Install DBOS Transact Enterprise and boto3:

pip install "dbos[enterprise]" boto3

Then, set external_stores in your DBOS configuration:

import boto3
from dbos import DBOS, DBOSConfig
from dbos_enterprise import S3ExternalStore

config: DBOSConfig = {
"name": "dbos-app",
"system_database_url": os.environ.get("DBOS_SYSTEM_DATABASE_URL"),
"conductor_key": os.environ.get("DBOS_CONDUCTOR_KEY"),
"external_stores": [S3ExternalStore(boto3.client("s3"), "my-bucket", prefix="dbos")],
}
DBOS(config=config)

DBOS provides two stores, and you can also write your own:

  • S3ExternalStore(client, bucket, prefix="", store_id=None) stores objects in an S3 bucket.
  • FilesystemExternalStore(root, store_id=None) stores objects as files under a directory, for development and testing.

Storing and Loading Objects​

Call DBOS.store_external_object to upload a value. It returns an ExternalObjectRef, a small reference you can return from a step or workflow or pass as a workflow input. When you need the value, call DBOS.load_external_object on the reference from a step:

from dbos import DBOS, ExternalObjectRef

@DBOS.step()
def generate() -> ExternalObjectRef[list[float]]:
readings = [float(i) for i in range(10_000_000)]
# Uploads to the store. Only this small reference is checkpointed.
return DBOS.store_external_object(readings)

@DBOS.step()
def summarize(ref: ExternalObjectRef[list[float]]) -> float:
readings = DBOS.load_external_object(ref)
return sum(readings) / len(readings)

@DBOS.workflow()
def pipeline() -> float:
ref = generate()
return summarize(ref)

You can store objects from a step or from workflow code, where DBOS runs the upload as a step. You can't load objects from workflow code. Load them in a step and use them there, returning only a small result, because a step's return value is checkpointed.

For the best performance with large objects, store bytes. A bytes value is uploaded and loaded as is, without being serialized.

Naming Objects​

By default, each call to store_external_object creates a new object with a random key. If you set a key, storing under that key again overwrites the object, so a step that retries doesn't leave extra copies behind:

ref = DBOS.store_external_object(report, key="reports/final.json")

Keys are scoped to the workflow that owns the object, so two workflows can use the same key. If you overwrite an object with a different value, references to the old value can no longer be loaded and raise DBOSExternalObjectIntegrityError.

Using Multiple Stores​

If you configure several stores, each needs a unique store ID and you must choose one with store_id when storing. Loading uses the store named in the reference:

ref = DBOS.store_external_object(data, store_id="s3://archive-bucket")

A store's ID defaults to its location, such as s3://my-bucket/dbos. Don't change the ID of a store while references to it exist. To be able to move a store's data to a new location, set store_id to a name that doesn't depend on the location.

Storing Objects Outside a Workflow​

Outside a workflow, you must name the workflow that owns the object. The workflow doesn't have to exist yet, so you can use DBOSClient to upload a large input and then enqueue a workflow to process it. The client must be configured with the same stores as your application:

import uuid
from dbos import DBOSClient
from dbos_enterprise import S3ExternalStore

store = S3ExternalStore(boto3.client("s3"), "my-bucket", prefix="dbos")
client = DBOSClient(
system_database_url=os.environ["DBOS_SYSTEM_DATABASE_URL"],
external_stores=[store],
)

workflow_id = str(uuid.uuid4())
ref = client.store_external_object(large_input, owner_workflow_id=workflow_id)
client.enqueue(
{"queue_name": "example_queue", "workflow_name": "process_input", "workflow_id": workflow_id},
ref,
)

Object Lifetime​

Every object is owned by a workflow: by default, the workflow that stored it. DBOS doesn't delete an object while its owner exists. Once the owner has been deleted, either by a retention policy or with DBOS.delete_workflow, the next retention run deletes the object from its store. Without a retention policy, objects are never deleted.

Only the owner keeps an object alive. If you fork a workflow and then delete the original, references held by the fork stop working and raise DBOSExternalObjectMissingError.

Custom Stores​

To use another storage system, implement the ExternalStore protocol and pass an instance in external_stores:

from typing import Sequence

class MyStore:
@property
def store_id(self) -> str:
return "my-store"

def put(self, key: str, data: bytes) -> None:
...

def get(self, key: str) -> bytes:
# Raise KeyError if no object has this key.
...

def delete(self, keys: Sequence[str]) -> None:
# Ignore keys with no object.
...

Each method should retry its own transient failures.

  • Setting Up External Storage
  • Storing and Loading Objects
    • Naming Objects
    • Using Multiple Stores
  • Storing Objects Outside a Workflow
  • Object Lifetime
  • Custom Stores
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.