Remix.run Logo
▲ Show HN: Waymark – A workflow engine that compiles jobs instead of replaying(github.com)
27 points by icyfox a day ago | 5 comments

Hey everyone! @mozgiii and I have been working on Waymark for the better part of the last year. Waymark’s an open source durable execution library with similar functionality to Temporal or DBOS.

I fell in love with durable execution a few years ago, after working at a company with a heavy pubsub architecture. In comparison to job orchestrators that force a certain task design or special syntax, many of these durable libs lets you write business logic in regular code: `asyncio.gather` to parallelize tasks, `for` loops, conditionals, and let the runtime figure out how to run them efficiently across a cluster of nodes. But most of the current solutions require a heavy broker, runtime hacks to try to ensure deterministic workflow logic, and feel like you have to learn a whole new ecosystem just to get started.

A few unique design decisions in waymark:

— Write jobs in normal code (right now Python but JS coming soon). Instead of running control flow as-is, which requires booting up an interpreter to replay past state, we’ll just compile your code directly. We walk your AST and map your logic to our stackless VM bytecode. We support variables, conditionals, await, sleep, helper functions, loops... basically what you’d expect in workflow logic.

— Since we’re compiling a language subset, we can proactively flag errors in your workflows locally instead of when they run in production.

— It only requires Postgres as the task broker. We’ve spent a lot of time tuning our design to play nicely with its strengths and limitations; our current benchmarks show around 1500 workflows/second but we still have some more optimization work to do here.

— Actions plug-and-play with FastAPI’s dependency injection syntax. So if you already have an ecosystem based on typehinting/DI, it should fit like a glove.

It allows for parallelization of workflows across one machine or a whole cluster. The bulk of the work per node is done by our central Rust core - which distributes workflows to properly saturate all cores. The idea is to be as batteries included as possible with the broker, webapp, and runtime.

What to use it for? It works great as the runner loop controlling agents, but it also works well for basically any task that you need to kick off in the background. It’s largely replaced our way of writing api code. Most frontend actions at this point just push to a simple REST endpoint that kick off a background job.

I spent an embarrassing amount of time making our https://waymark.sh site interactive - it’s probably the best place to learn more. We’re starting with Python support and looking for community input on what language to ship next. Would love to hear your thoughts!

▲msteffen 2 hours ago | parent | next [-]

This is so cool. I spent years at Pachyderm (YC...something) banging our head against:

- we're "Bazel for data"

- but...we have no Starlark. We're trying to port Airflow but it's not playing well with the way we cache partial results.

Amazing to me that I'm only now seeing the architecture pattern of "pure coordination language connecting sandboxed imperative components", which is used by Nix and Bazel, applied to distributed workflows. If somebody applied this to batch data processing you might have a Pachyderm that actually works better than ours did.

▲icyfox an hour ago | parent [-]

Cool thought! We have an internal version of map reduce implemented in waymark that worked pretty well (though uses Redis for its intermediate values so it's technically not an end-to-end integrated solution).

In principle I would love to have something like this working for arbitrarily large ETL flows; ie. write the logic you want and it tries to figure out a good distributed algorithm to get the outputs. At the moment flowing data through the cluster is the biggest bottleneck. We generally assume that action inputs & outputs themselves are pretty small at the boundary even if their containerized context is quite large.

Where did you hit the most snags with caching?

▲dangistan202611 27 minutes ago | parent | prev [-]

It is too async heavy as per its readme -- this is very off-putting. Not everyone cares for async. There is no usage shown with sync code.

▲icyfox 23 minutes ago | parent [-]

MIT licensed, just clarified in the repo.

As for async - background jobs are better modeled as async because of their natural parallelism across nodes imo. Logic in actions themselves are free to be as synchronous as you want. Why the preference?

▲dangistan202611 14 minutes ago | parent [-]

I dispute the "better" assertion. Anyhow, I don't know how familiar or not you are with sync code, but it fully supports background jobs using queues with threads/interpreters/processes. It has been around for decades. If you can willingly disregard support for it, I can willingly disregard your repo.