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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

without_durability-0.0.2.tar.gz (34.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

without_durability-0.0.2-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

File details

Details for the file without_durability-0.0.2.tar.gz.

File metadata

  • Download URL: without_durability-0.0.2.tar.gz
  • Upload date:
  • Size: 34.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","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}

File hashes

Hashes for without_durability-0.0.2.tar.gz
Algorithm Hash digest
SHA256 3b3efa88591a4c468ce3da765a1eb00838042c4f9866464466008181f1bdd9b0
MD5 c16664b8dcf550bb25ecd497db5a5b3e
BLAKE2b-256 2fd0185bd8577155a27a6737ae85e15566a171efad97a01a72a5c0f3183edf32

See more details on using hashes here.

File details

Details for the file without_durability-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: without_durability-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 40.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","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}

File hashes

Hashes for without_durability-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 311c5e022e575d3a3c1cdfec9adde25645e01606a4eda103b000d4f569ea4e71
MD5 bb169001af5421fd1043a0383b7d2a2b
BLAKE2b-256 d2d12fbaf319a33be3e5804f7e0351383839497f5e8559d8e21612043202d765

See more details on using hashes here.

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