# Resume durable operations

Persist a remote identifier and resume read-only polling without silently submitting duplicate work.

Canonical: https://searchsdk.dev/docs/guides/durable-operations
Markdown: https://searchsdk.dev/docs/guides/durable-operations.md

<a id="lifecycle"></a>

## Persist intent, then the remote ID

> **TypeScript reference**
> The API names, code and policy behavior in this article describe the TypeScript SDK. See [your language guide](https://searchsdk.dev/docs/sdks) for its supported surface and limits.

`runDurableOperation` coordinates an application-supplied submit callback and read-only poll callback. It saves a submitting intent before dispatch, saves the returned remote ID, and resumes that same ID on later invocations. It is not a managed background worker or a provider-specific job adapter.

**durable.ts · fixture**

```typescript
import {
  createMemoryCheckpointStore, runDurableOperation,
} from '@belweave/search-sdk/durable';

const store = createMemoryCheckpointStore(); // Tests only; not disk persistence
const options = {
  key: 'example-job',
  operation: 'example.fixture',
  inputFingerprint: 'fixture-v1',
  store,
  submit: async () => ({ remoteId: 'fixture-job-1' }),
  poll: async () => ({ state: 'completed' as const, value: { ok: true } }),
};
const first = await runDurableOperation(options);
const resumed = await runDurableOperation(options);
console.log(first.value, resumed.resumed); // { ok: true }, true

```

<a id="store"></a>

## Choose a checkpoint store

| Store | Boundary |
| --- | --- |
| `createMemoryCheckpointStore()` | Process-local tests; does not survive exit. |
| `createFileCheckpointStore({ directory })` | Explicit Node-only subpath /durable-node; pre-existing absolute private local directory. |
| Custom CheckpointStore | Must implement atomic compareAndSet across every writer and defensive snapshots. |

The local file store uses exclusive per-key locks, atomic rename, and file/directory fsync. A crashed writer's lock is not stolen automatically. Stop writers and reconcile before removing stale locks. It is designed for trusted local filesystems, not distributed consensus.

<a id="reconcile"></a>

## Fail closed on ambiguous submission

> **No automatic resubmission**
> A crash or failure after submission intent may leave remote work running without a saved identifier. The coordinator returns ambiguous_submission and refuses to submit again. Cancellation or deadline expiry during submission is also ambiguous. Reconcile with the provider before continuing.

`operation` and `inputFingerprint` bind a checkpoint to the caller's intended work. Fingerprints and keys must be opaque and secret-free. Result checkpoints are JSON snapshots; they can contain sensitive provider data, so control storage access and retention.

After independently verifying the remote job belongs to the saved intent, call `attachDurableRemoteId({ store, key, operation, inputFingerprint, remoteId })`. It requires the matching submitting checkpoint and uses compare-and-set to attach the identifier. A later run can resume read-only polling. The helper cannot independently verify provider ownership.

<a id="bounds"></a>

## Bound each invocation

Defaults are a 30-second invocation deadline, one-second poll interval, and 100 polls. Cancellation or deadline expiry while polling and poll-limit failure retain the saved remote identifier for later resumption. Submission interruption instead requires ambiguity reconciliation. Polling is explicit, bounded, and separate from remote cancellation.

- [Durable API and errors](https://searchsdk.dev/docs/api/durable)
