# Cursors and reorgs

Source: https://docs.graphops.xyz/substreams/concepts/cursors-and-reorgs

Decide how your consumer keeps stored data complete and correct when streams disconnect and the
chain reorganizes. Two rules cover both: save each cursor in the same transaction as the output it
follows, and revert writes after the last valid block when an undo signal arrives.

## Cursor model

Each `BlockScopedData` message carries one block's output and a cursor. The cursor is the resume
token for the stream position after that block. Reconnecting with it continues at the next block.

Commit in this order:

1. Apply the block's output to durable state.
2. Save the cursor in the same transaction when possible.
3. After a disconnect, resume from the latest committed cursor.

A cursor saved before its output commits can skip that block after a restart. A cursor kept only in
memory makes the consumer replay work from an earlier block, so writes must also be idempotent.

## Reorg and undo model

Near the chain head, a block can be replaced by a different block at the same height. Substreams
then sends a `BlockUndoSignal` that names the last valid block and its cursor. Your consumer must
revert everything it wrote after that block, then continue with the replacement blocks.

_Blocks N+1 and N+2 were replaced. The undo signal names N as the last valid block, so the consumer reverts what it wrote after N, stores cursor cN, and applies the replacement blocks._

- Before: N, N+1, N+2
- After the reorg: N, N+1′, N+2′
- `BlockUndoSignal`: last valid block N · cursor cN

| Message           | Meaning                                                | Required action                                                 |
| ----------------- | ------------------------------------------------------ | --------------------------------------------------------------- |
| `BlockScopedData` | Output for one block, with its cursor.                 | Apply the output and save the cursor after commit.              |
| `BlockUndoSignal` | Reorg correction with the last valid block and cursor. | Revert local state to the last valid block and save its cursor. |
| `ModulesProgress` | Progress for active jobs and stages.                   | Use for monitoring and backfill visibility.                     |

To revert, keep enough history to undo recent writes, for example by recording the block number on
each row. If your workload cannot revert writes, stream final blocks only; output then trails the
chain head.

## The full sequence

_Commit each block's output together with its cursor. On an undo signal, revert to the last valid block and keep its cursor, then resume from the stored cursor after a reconnect._

1. BlockScopedData (block N · cursor cN): Commit block N's output and cursor cN in one transaction.
2. BlockScopedData (block N+1 · cursor cN+1): Commit block N+1's output and cursor cN+1 in one transaction.
3. BlockUndoSignal (last valid block N · cursor cN): Revert everything after block N and store cursor cN.
4. Reconnect (cursor cN): Resume from the stored cursor; the stream continues after block N.

## Practical consequences

* Store cursors durably, not only in process memory.
* Make writes idempotent by block number, block hash, ordinal, or a package-specific key.
* Handle undo signals before claiming exactly-once materialization.
* Reconnect with backoff, and resume from the latest committed cursor.

## Next steps

* [Resume with cursors](/substreams/guides/resume-with-cursors): Apply the commit order in your consumer.
* [Run a sink](/substreams/guides/run-a-sink): Use a standard sink that stores cursors and handles undo for you.
* [API integration](/substreams/api): Check every message type a stream can send.