Workflows
Workflows provide durable execution so you can write programs that are resilient to any failure.
Workflows are comprised of steps, which are ordinary TypeScript functions annotated with @DBOS.step()
.
If a workflow is interrupted for any reason (e.g., an executor restarts or crashes), when your program restarts the workflow automatically resumes execution from the last completed step.
Here's an example workflow from the programming guide. It signs an online guestbook then records the signature in the database. Using a workflow guarantees that every guestbook signature is recorded in the database, even if execution is interrupted.
class Guestbook {
@DBOS.workflow()
static async greetingEndpoint(name: string): Promise<string> {
await Guestbook.signGuestbook(name);
await Guestbook.insertGreeting(name);
return `Thank you for being awesome, ${name}!`;
}
}
Reliability Guarantees
Workflows provide the following reliability guarantees. These guarantees assume that the application and database may crash and go offline at any point in time, but are always restarted and return online.
- Workflows always run to completion. If a DBOS process crashes while executing a workflow and is restarted, it resumes the workflow from the last completed step.
- Steps are tried at least once but are never re-executed after they complete. If a failure occurs inside a step, the step may be retried, but once a step has completed (returned a value or thrown an exception to the calling workflow), it will never be re-executed.
- Transactions commit exactly once. Once a workflow commits a transaction, it will never retry that transaction.
Determinism
Workflows are in most respects normal TypeScript functions. They can have loops, branches, conditionals, and so on. However, workflow functions must be deterministic: if called multiple times with the same inputs, it should invoke the same steps with the same inputs in the same order. If you need to perform a non-deterministic operation like accessing the database, calling a third-party API, generating a random number, or getting the local time, you shouldn't do it directly in a workflow function. Instead, you should do all database operations in transactions and all other non-deterministic operations in steps.
For example, don't do this:
class Example {
@DBOS.workflow()
static async exampleWorkflow() {
// Don't make an HTTP request in a workflow function
const body = await fetch("https://example.com").then(r => r.text());
await Example.exampleTransaction(body);
}
}
Do this instead:
class Example {
@DBOS.step()
static async fetchBody() {
// Instead, make HTTP requests in steps
return await fetch("https://example.com").then(r => r.text());
}
@DBOS.workflow()
static async exampleWorkflow() {
const body = await Example.fetchBody();
await Example.exampleTransaction(body);
}
}
Workflow IDs
Every time you execute a workflow, that execution is assigned a unique ID, by default a UUID.
You can access this ID through the DBOS.workflowID
context variable.
Workflow IDs are useful for communicating with workflows and developing interactive workflows.
Starting Workflows Asynchronously
You can use DBOS.startWorkflow
to start a workflow in the background without waiting for it to complete.
This is useful for long-running or interactive workflows.
DBOS.startWorkflow
returns a workflow handle, from which you can access information about the workflow or wait for it to complete and retrieve its result.
The DBOS.startWorkflow
method resolves after the handle is durably created; at this point the workflow is guaranteed to run to completion even if the app is interrupted.
Here's an example:
class Example {
@DBOS.workflow()
static async exampleWorkflow(var1: str, var2: str) {
return var1 + var2;
}
}
async function main() {
// Start exampleWorkflow in the background
const handle = await DBOS.startWorkflow(Example).exampleWorkflow("one", "two");
// Wait for the workflow to complete and return its results
const result = await handle.getResult();
}
You can also use DBOS.retrieve_workflow
to retrieve a workflow's handle from its ID.
Workflow Events
Workflows can emit events, which are key-value pairs associated with the workflow's ID. They are useful for publishing information about the state of an active workflow, for example to transmit information to the workflow's caller.