Skip to main content

Coming from Temporal

Are you coming from Temporal?

This guide is meant to help you understand the differences and similarities between Temporal and Resonate.

Durability

One of Temporal’s core value propositions is Durable Execution. Essentially, Durable Execution is the ability for a function to recover in another process after a hard failure and resume from where it left off.

Resonate also provides Durable Execution.

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 Temporal, then you are familiar with thinking in terms of Workflows and Activities.

Resonate does not have proprietary function types such as Workflows and Activities; it just uses 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 Temporal?

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

In Temporal, you always need a Temporal Service for your Worker to connect to, whether that is a local development instance 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

In Temporal, you can use Signals to unblock a Workflow Execution with 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

In Temporal, you can run multiple Workers to handle the load of your application. You use Task Queue Names to identify which Workers should handle which Workflows and Activities.

In Resonate, you can register workers in groups and send invocations to the groups. Resonate will automatically load balance the invocations across the Workers in the group.

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)

Decentralized architecture

In Temporal, any given Workflow Execution is tied to a specific Temporal Service. This forces a star-like topology (centralized system) where all Workers in an application connect to a single Temporal Service.

Additionally, Temporal requires you to size your Temporal Service ahead of time to handle the load of all Workers in your application. If you size it incorrectly, you have to migrate to a new setup, which is a complex process.

Resonate applications, on the other hand, can use multiple Resonate Servers to durably store data (Durable Promises), which can be added dynamically as the application scales.

Temporal architecture

System complexity

The level of system complexity is perhaps the biggest difference between Resonate and Temporal.

Resonate at its core believes in simplicity.

This belief drives many of the design decisions in Resonate, and has resulted in a much smaller API surface than Temporal.

This smaller API surface and desire for simplicity have also resulted in a much smaller set of components.

For example, Temporal SDKs have tens of thousands of lines of code needed to manage the complexity of their implementation. Resonate SDKs, on the other hand, have but a few hundred lines of code.

A Temporal Service is a complex system consisting of multiple individual components, each consisting of tens of thousands of lines of code, that need to be configured, deployed, and scaled independently. A Resonate Server, on the other hand, is a single binary that is easy to deploy. And the Resonate system can scale by just adding more Resonate Servers, each with an underlying database.

Additionally, Temporal exposes many complex APIs to the developer which they are required to use for failure detection, load balancing, and more. Resonate's belief in simplicity means that much of this complexity is pushed into the platform and automatically handled for you.

For example, Temporal requires the developer to work with at least 4 different timeouts to detect and handle Activity Execution failures. It also requires the developer to manually develop heartbeats in the Activity, and tune the performance of Workers. Whereas, Resonate exposes a single timeout to the developer for the resolution of the Durable Promise, and it automatically handles failure detection, heartbeating, and load balancing across Workers.

Ultimately, as a developer, you will find many areas where Resonate has chosen to promote a specific model for the sake of simplicity, and to avoid indulging in what we believe to be unnecessary complexity.