Skip to main content

without-durability

Durable workflows built on the observation that once a workflow's state is a checkpoint any process can read, most of a workflow engine stops being necessary. A workflow is an ordinary async function whose effects are named; resuming means calling it again; each step it reaches hands back what is already recorded instead of running.

from without_durability import Completed, MemoryCheckpointer, Run, Sleeping, Waiting, claimed, resume


def as_text(recorded: object) -> str:
    if not isinstance(recorded, str):
        raise TypeError(f"{recorded!r} is not the reference this step recorded")
    return recorded


async def fulfil(run: Run) -> str:
    charge = await run.step("charged", lambda: gateway.charge(order), as_text)
    await run.sleep("settling", timedelta(days=3))  # survives a crash on day two
    approver = await run.awaiting("approved-by", as_text)  # another process writes this
    return await run.step("paid", lambda: gateway.pay(charge, approver), as_text)


checkpointer = MemoryCheckpointer()
match await resume(await claimed(checkpointer, "order-42"), checkpointer, fulfil):
    case Completed(value=reference):
        ...  # the workflow finished
    case Sleeping(due=due):
        ...  # schedule a wakeup for `due`
    case Waiting(key=key):
        ...  # nothing to schedule; someone must write `key`

A pass comes back as one of those three rather than raising two of them, so what to do next is a match a type checker can tell you is incomplete. Inside the workflow a suspension is still an exception, because that is how you stop in the middle of straight-line code; resume is the boundary where it becomes a value.

Each read names a parser because a step hands back what the store holds, not what its effect returned: the value has been through a codec, so a step returning a tuple is handed a list on the pass that ran it. A parser makes the return type something a function proved rather than something a cast asserted.

There is no server here and no engine. What there is instead is an interface, and the interface is where the interesting part lives.

A protocol of load and record is too weak to run a workflow safely at any scale, because it cannot say "only if nobody else is running this" or "only if I am still the one who may write". So Checkpointer states the requirements and an implementation says how it meets them: claim grants at most one live pass and issues strictly increasing fencing tokens, record refuses a write from a superseded pass and never overwrites a recorded step. That is the same problem Temporal answers with a server and DBOS answers by requiring Postgres; here it is stated as an interface, so a deployment brings whatever store it already runs. Scheduler is the other half of a workflow's state, its right to run, and Durable is the pair plus the transitions that have to cross both at once.

Stores live in their own packages, so this one depends on nothing but without and without-dag:

MemoryCheckpointer and MemoryScheduler ship here too, so a test injects a dict rather than starting a container.

The same interface carries a second mechanism: run_durably runs a without-dag CompiledGraph against it, recording each (node key, result) before pulling the next. A saga needs no third one, since a rollback is another graph run through the same call. work(durable, body) turns either into a running service, a pool of passes plus a timer, with backpressure that falls out of pulling one delivery per free slot.

See the without-durability guide (with the API reference) for the full surface: the two mechanisms, the worker, Run.transact and the exactly-once step it buys, what each store can and cannot promise, and the gaps that keep this a substrate rather than a replacement for Temporal or DBOS.

Metadata

Release files for without-durability 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for without-durability 0.0.3
File Size Uploaded
without_durability-0.0.3.tar.gz 34.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for without-durability 0.0.3
File Interpreter ABI Platform
without_durability-0.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 75.6 kB

Release files / without_durability-0.0.3.tar.gz

Download URL without_durability-0.0.3.tar.gz
Size 34.9 kB
Tags Source
SHA-256 checksum
How to use checksums
44de10718b75136f0866247b3ace0064f9829a666217c835fbb161435f640f95
BLAKE2b-256 checksum
How to use checksums
9558acd8cc605fcff25a8f42e6473cb2909c3c42bd1f53acf4f3475c16c6a8c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / without_durability-0.0.3-py3-none-any.whl

Download URL without_durability-0.0.3-py3-none-any.whl
Size 40.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
598b85477aaaf5711f9581c1f841112c8222d7f769a60c6b3ef7b0a4f1a558b8
BLAKE2b-256 checksum
How to use checksums
67c83f1f5058cd9febe79c1eb7d6d88a040f746e5be886d20fa71bbb579ac32c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

This release

0.0.3 This release

2 release files

0.0.2

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page