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.5

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.5
File Size Uploaded
without_durability-0.0.5.tar.gz 34.9 kB Details

Built distribution (wheel)

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

Total release size: 75.6 kB

Release files / without_durability-0.0.5.tar.gz

Download URL without_durability-0.0.5.tar.gz
Size 34.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f659bd16086ec675f1283d5b4e3cb99e9486a3eac870e00ca8340acebd727c2a
BLAKE2b-256 checksum
How to use checksums
c78b772e3ac6d4ffdfb05179365d34fe8d0cf4ca8b723d78ce1e1d039d7e8668
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.5-py3-none-any.whl

Download URL without_durability-0.0.5-py3-none-any.whl
Size 40.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
edfbd452ca9ba03b0f0d55f6b201457577ab31463c7660b94dcccf5c1eb5f02b
BLAKE2b-256 checksum
How to use checksums
9646cc046155dafd105f207eb3e2a6b81d42eb84e905a275faee045be86fce49
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

This release

0.0.5 This release

2 release files

0.0.4

2 release files

0.0.3

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