Documentation menu
Documentation

Durable execution

Choose queues, retries, fibers, or workflows based on the recovery guarantee the work needs.

Start from a fresh project

This guide is self-contained. Create an empty Ayjnt project, then replace the starter agent with the files shown below. Run commands from the new project directory unless a step says otherwise.

terminal
bunx ayjnt new try-durable-execution --empty
cd try-durable-execution
bun install

# remove agents/alive once you are ready to add the guide's files

Four execution tools

keepAliveWhile() protects in-memory work from idle eviction but does not make it recoverable. Fibers add a durable run ledger and stash() checkpoints. Workflows add durable steps and a full instance lifecycle.

PrimitiveUse it for
retry()A fallible operation that can be tried again now
queue()FIFO work owned by one agent
runFiber() / startFiber()Long work with checkpoints and recovery hooks
AgentWorkflowDurable multi-step processes, approvals, and external control
agents/importer/agent.ts
import { Agent, callable } from "ayjnt";

export default class ImporterAgent extends Agent {
  @callable()
  async importFile(fileId: string) {
    return this.runFiber(
      `import:${fileId}`,
      async (fiber) => {
        const rows = await this.retry(
          () => downloadRows(fileId),
          { maxAttempts: 3 },
        );

        fiber.stash({ downloadedRows: rows.length });
        return this.queue("indexRows", { fileId, rows });
      },
    );
  }

  async indexRows(payload: { fileId: string; rows: Row[] }) {
    await saveRows(payload.fileId, payload.rows);
  }
}

Make effects idempotent

A recovery path may execute again. Give external writes idempotency keys, record completion before notifying clients, and keep non-durable progress messages separate from durable state transitions.

example
await chargeCustomer({
  customerId,
  amount,
  // A retry receives the same key instead of creating a second charge.
  idempotencyKey: `order:${orderId}:charge`,
});

Run it and verify the result

Interrupt one run after its first checkpoint, restart development, and verify that the selected durable primitive resumes or retries safely.

terminal
bun run build
bun run dev

# the local app is now available at http://localhost:8787