Skip to main content

Coming from DBOS

Are you coming from DBOS?

If so, this guide is meant to help you understand the differences and similarities between Resonate and DBOS.

Durability

A core value proposition of DBOS is its durable workflows. A durable workflow or Durable Execution is one that can recover in another process after a hard failure and resume from where it left off.

Resonate also provides durable workflows (Durable Executions).

In Resonate, if your process crashes, your function can recover and resume from where it left off in another process.

Programming model

If you have experience with DBOS, then you are familiar with thinking in terms of Workflows and Steps.

Resonate does not have proprietary function types such as Workflows and Steps – you just write functions.

Resonate promotes a procedural programming model that enables you to compose functions indefinitely and recursively.

Consider this factorial example. How might you implement it in DBOS?

See it in action: example-recursive-factorial-py

@resonate.register
def factorial(ctx: Context, n: int) -> int:
if n <= 1:
return 1
r = yield ctx.run(factorial, n - 1).options(id=f"factorial-{n-1}")
return n * r


def main():
result = factorial.run("factorial-5", n=5)
print(result)

Zero-dependency development

With DBOS, your Worker or application must always connect to a Postgres database, whether it’s a local development or a production instance.

Resonate offers a zero-dependency development experience, where a Worker (single node application) can run without connecting to a Resonate Server.

from resonate import Resonate

resonate = Resonate.local()

#...

Human-in-the-loop

With DBOS, you can use a '@recv' function to block the Workflow and await input from a human.

With Resonate, you can achieve the same effect by creating a Durable Promise that is not attached to any function execution, and then await on it. Then, you can resolve it from anywhere, optionally sending data into the function while doing so.

See it in action: example-human-in-the-loop-py

# worker.py
@resonate.register()
def foo(ctx: Context) -> None:
blocking_promise = yield ctx.promise()
# ...
# wait for the promise to be resolved
data = yield blocking_promise
# ...
# client.py
def main():
# ...
resonate.promises.resolve(
id=promise_id,
data=json.dumps(data)
)

Distributed execution

A major difference between Resonate and DBOS is that Resonate can orchestrate executions across multiple Workers while DBOS cannot. With Resonate, you can run multiple Workers in a group, and Resonate will automatically load-balance invocations across them.

Consider the following Resonate example. How would you accomplish this using DBOS?

See it in action: example-load-balancing-py

# worker.py
resonate = Resonate.remote(
group="workers"
# ...
)
# client.py
resonate = Resonate.remote(
group="client"
)

def main():
# ...
result = resonate.options(target="poll://any@workers").rpc(promise_id, "function_name", params)

DBOS is designed to ensure durability for a single instance of an application. When a DBOS application process crashes, an operator must restart the application for any in-progress functions/workflows to recover and resume.

Resonate will automatically recover and resume in-progress functions/workflows in another Worker.

In essence, DBOS focuses on single-instance durability without addressing scalability, while Resonate holistically addresses the needs of modern distributed systems.