# Singleton

> Prevent overlapping runs for the same key from duplicating work or overwriting results.

Keep two syncs for the same user from running at once. Singleton applies to the entire function run for that key; choose whether a new event skips its run or replaces the active run.

Singleton controls the **whole function run**. A concurrency limit of `1` controls executing steps, so it does not provide the same run-level rule.

## Configure a singleton function

This TypeScript v4 function keeps one active sync run per user. If another `data-sync.start` event arrives for that user while the run is active, `mode: "skip"` skips the new run and lets the current run continue. Replace the step body with your synchronization code.

```typescript
import { Inngest } from "inngest";

const inngest = new Inngest({ id: "customer-sync" });

export const syncUser = inngest.createFunction(
  {
    id: "sync-user",
    triggers: { event: "data-sync.start" },
    singleton: {
      key: "event.data.user_id",
      mode: "skip",
    },
  },
  async ({ event, step }) => {
    await step.run("sync-user-data", async () => {
      console.log(`syncing user ${event.data.user_id}`);
    });
  }
);
```

The required `key` is a Common Expression Language (CEL) expression string evaluated against each triggering event. Different key values can have separate active runs. In this example, user A and user B can each have one active sync run.

## Choose what a new event does

| Mode     | When another event arrives with the same key                       | Use it when                                 |
| -------- | ------------------------------------------------------------------ | ------------------------------------------- |
| `skip`   | Inngest skips the new run and preserves the active one.            | The work already in progress should finish. |
| `cancel` | Inngest cancels the active run and starts a run for the new event. | The newest event should replace older work. |

**Cancel mode:** Set `mode: "cancel"` in the example to use the second behavior. Cancellation does not undo steps that already completed. A currently executing `step.run` finishes before cancellation takes effect. During a rapid burst of matching events, cancel mode can skip some new runs rather than cancel each previous run.

A run that fails and is retrying still occupies its singleton slot. In skip mode, new matching runs stay skipped while the original run retries.

## Use it with other flow controls

- [Step concurrency](/docs-markdown/durable-execution/flow-control/concurrency) controls active step execution. Singleton already implies one active run per key. Treat an additional concurrency setting with care because it can change scheduling without replacing the run-level rule.
- [Rate limiting](/docs-markdown/durable-execution/flow-control/rate-limiting) limits run starts over a specified period. Singleton uses the duration of an active run.
- [Throttling](/docs-markdown/durable-execution/flow-control/throttling) queues excess runs. Singleton skip mode discards overlapping runs; cancel mode replaces the active one.
- [Debounce](/docs-markdown/durable-execution/flow-control/debounce) can be combined with singleton when you want to wait for a quiet period before applying the run-level rule.
- [Batching](/docs-markdown/durable-execution/flow-control/batching) cannot be combined with singleton. Function registration fails when both are set.