Documentation menu
Documentation

Durable execution

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

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`,
});