Write crash resistant programs for financial applications, microservice orchestration, human in the loop applications and agentic workflows.
Deepslate is a durable execution interpreter for a minimal subset of Go and Python, it is an alternative to temporal and azure durable functions. What makes deepslate unique is that unlike existing durable execution engines, deepslate does not use event replay. It compiles the flow functions into custom bytecode and runs them on a custom control flow interpreter, deepslate then uses snapshots of the interpreter for crash recovery. This enables deepslate replay long running workflows very quickly and use less storage space. Deepslate also provides seamless developer experience by being a subset syntax, deeply integrating into the language build tools, linters and IDE.
This project is still being worked on and is not ready for public release.
-
Install the golang toolchain
-
Create an empty project folder
mkdir counter-example cd counter-example -
Create virtual env
python3 -m venv .venv source .venv/bin/activate -
Clone deepslate
git clone git@github.com:RainingComputers/deepslate.git ../deepslate -
Build and install deepslate
make -C ../deepslate pypiwheel pip install ../deepslate/dist/*.whl -
Install fastapi and uvicorn
pip install fastapi uvicorn -
Start postgres using docker
docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:18-alpine -
Save the example flow and server as
server.pyimport uvicorn from fastapi import FastAPI from deepslate import flow, sleep, new_postgres, spawn_workers, spawn_inspector DBURL = "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable" # durable flow that counts up forever, one tick per second, # can survive crashes and restarts @flow async def counter(): count: int = 0 while True: await sleep(1.0) count = count + 1 def create_app(spawn_fn, get_fn, trace_fn, shutdown_fn) -> FastAPI: app = FastAPI() app.state.spawn = spawn_fn app.state.get = get_fn app.state.trace = trace_fn app.state.shutdown = shutdown_fn # spawn a new durable counter flow with the given name @app.post("/counter/{name}") def spawn_counter(name: str): id_, exists = app.state.spawn(name, "counter", {}) return {"id": id_, "exists": exists} return app if __name__ == "__main__": # connect to postgres and start deepslate platform, _ = new_postgres(1, DBURL, "localhost:8765", 100, 100, 5) spawn_fn, get_fn, trace_fn, shutdown_fn = spawn_workers(4, platform, [counter()]) spawn_inspector(platform, "localhost:8080") # start the server app = create_app(spawn_fn, get_fn, trace_fn, shutdown_fn) uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)
-
Run the example server
python server.py -
Spawn a flow
curl -X POST http://localhost:8000/counter/my-counter -
Visit the dashboard at localhost:8080 and inspect the
my-counterisolate -
Kill the server with
Ctrl+C(leave postgres running)^C -
Restart the server
python server.py -
Visit the dashboard again at localhost:8080 and observe that
countcontinues from where it left off (survived the crash) and that the flow continues to run sleeping and looping
Deepslate lets you write durable workflows in a restricted subset language within @flow functions. The @flow function is meant to express only critical glue logic that requires durability. Flow functions can call other ordinary python functions where no restrictions exist.
You can think of a @flow function as a durable goroutine that can survive application restarts, deepslate provides golang-like concurrency constructs in python, i.e. spawning, channels (queue), wait group, mutex (lock) and select statement.
-
Flow definition: an
async deffunction marked@flow, with typed params and an optional return typefrom deepslate import flow @flow async def add(a: int, b: int) -> int: return a + b
-
Types:
int,bool,str,float,bytes,list[T],dict[K, V],@dataclasstypes, the result typeT | DSError, and sync primitivesdpslio.Queue[T],dpslio.Lock,dpslio.WaitGroup -
Variable declaration: every variable needs a type annotation, the value is optional, e.g.
count: int = 0orresult: bytes | DSError -
Assignment: rebind an already declared variable, e.g.
count = count + 1 -
Operators: arithmetic
+ - * / %, comparison== != < > <= >=, booleanand or not, and unary- not -
Literals:
int,float,str,TrueorFalse, list[1, 2, 3], and dict{"a": 1} -
if / elif / else: conditional branching, e.g.
if x > 0: ... elif x == 0: ... else: ... -
while: loop while a condition holds, including infinite loops, e.g.
while True: await sleep(1.0) -
for: iterate over a list, or use
enumeratefor index and value, e.g.for i, num in enumerate(numbers): ... -
match / case: match a value against cases, e.g.
match status: case 200: ... case _: ... -
return: return a value or exit the flow, e.g.
return resultor barereturn -
DSError and isinstance: deepslate does not support exceptions, durable operations return the result type
T | DSError, narrow it withisinstanceon the success type and propagate by returning the errorfrom deepslate import flow, http_request, DSError @flow async def handle(): resp: bytes | DSError resp = await http_request("GET", "http://localhost:8000/health", {}, b"") if not isinstance(resp, bytes): return resp
-
sleep: sleep for given number of seconds, deepslate flows can sleep for days or even months surviving restarts
from deepslate import flow, sleep @flow async def delay(): await sleep(1.0)
-
http_listen: suspend until an HTTP request hits the given route, returning its body
from deepslate import flow, http_listen @flow async def listener(): data: bytes data = await http_listen("POST", "/numbers")
-
http_listen_excl: like
http_listenbut exactly one flow serves the request and can reply to it,bodyreturns the request body andwriterreturns a write functionfrom deepslate import flow, http_listen_excl, body, writer from deepslate import HttpListenExclHandle @flow async def echo(): handle: HttpListenExclHandle while True: handle = await http_listen_excl("POST", "/echo") w = writer(handle) data = body(handle) w(201, data)
-
http_request: make a durable outbound HTTP call
from deepslate import flow, http_request, DSError @flow async def fetch(): resp: bytes | DSError resp = await http_request("GET", "http://localhost:8000/health", {}, b"")
-
json_marshal and json_unmarshal: encode to and decode from JSON bytes, decode takes the target type as a generic param
from deepslate import flow, json_marshal, json_unmarshal, DSError @flow async def codec(data: bytes): nums: list[int] | DSError nums = await json_unmarshal[list[int]](data) raw: bytes | DSError raw = await json_marshal([1, 2, 3])
-
pure function:
@puremarked deterministic functions that don't have any side effects, run inline without a checkpoint, e.g.status = get_status(resp)where:from deepslate import pure @pure def get_status(resp: HealthResponse) -> str: return resp.Status
-
impure function:
@impuremarked side-effect functions whose result is run once and checkpointed, e.g.await insert_event(name)where:import psycopg2 from deepslate import flow, impure @impure def insert_event(name: str) -> None: conn = psycopg2.connect(DBURL) cur = conn.cursor() cur.execute("INSERT INTO events (name) VALUES (%s)", (name,)) conn.commit() conn.close() @flow async def record(name: str): await insert_event(name)
-
Flow call: call another
@flowfunction directly, it runs durably and returns its valuefrom deepslate import flow @flow async def adder(a: int, b: int) -> int: return a + b @flow async def caller(): total: int = await adder(3, 5)
-
create_task: spawn a child flow that runs concurrently
from deepslate import flow, sleep, dpslio @flow async def child(n: int): await sleep(1.0) @flow async def parent(): dpslio.create_task(child(1))
-
Queue: a durable channel,
putandgetare awaitedfrom deepslate import flow, dpslio @flow async def consumer(ch: dpslio.Queue[int]): value: int = await ch.get() @flow async def producer(): ch: dpslio.Queue[int] = dpslio.Queue[int]() await ch.put(1) dpslio.create_task(consumer(ch))
-
WaitGroup: wait for N concurrent children to finish
from deepslate import flow, sleep, dpslio @flow async def task(wg: dpslio.WaitGroup): await sleep(1.0) await wg.done() @flow async def parent(): wg: dpslio.WaitGroup = dpslio.WaitGroup() wg.add(2) dpslio.create_task(task(wg)) dpslio.create_task(task(wg)) await wg.wait()
-
Lock: a durable mutex for exclusive sections
from deepslate import flow, dpslio @flow async def worker(mu: dpslio.Lock): await mu.acquire() count: int = 1 await mu.release()
-
select: proceed with whichever channel is ready first
from deepslate import flow, dpslio @flow async def race(ch1: dpslio.Queue[int], ch2: dpslio.Queue[int]): result: int = 0 match dpslio.select: case _ if v := await ch1.get(): result = v case _ if v := await ch2.get(): result = v