> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ilyama.golain.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Capture strategies

> Row journal vs telemetry batches — choosing how tables replicate.

Omega supports two capture strategies per table. Use **row journal** for relational/state tables and **telemetry batch** for time-series tables with a stable timestamp column.

## Row journal (`strategy: rows`)

**Best for:** mutable state, transactional tables, configuration, anything with INSERT/UPDATE/DELETE.

### How it works

1. Triggers on enrolled table write to `__omega_journal`.
2. Agent assigns monotonic **`commit_seq`** (not SQLite AUTOINCREMENT alone).
3. Flush loop publishes **`sync/rows/batch`** with operations:

| `op`     | `row` field   |
| -------- | ------------- |
| `insert` | Full row JSON |
| `update` | Full row JSON |
| `delete` | `null`        |

4. Cloud dedupes per row: `(device_id, source_table, journal_epoch, source_pk_hash, commit_seq)`.

### When to choose rows

* Table has a primary key and changes over time.
* You want relational/state materialization through `mirror-rows`.
* You need update/delete semantics or cloud-authoritative `state_write` flows.

### Example tables

* `device_state` — keyed settings
* `order_events` — append-only event log with PK
* `sensor_timeseries` — if you need telemetry hypertables, use `strategy: telemetry`

## Telemetry batch (`strategy: telemetry`)

**Best for:** high-volume time-ordered readings with a timestamp column.

### How it works (spec)

1. Cursor on max `event_ts` included in last accepted batch.
2. Publish **`sync/telemetry/batch`** every 5s or 512 KiB.
3. Rows include `ts`, optional `row_id`, and `fields` map.
4. Cloud dedupes batch on `(device_id, batch_id)`; enforces monotonic **`batch_seq`**.

### Current Omega status

Telemetry batches are emitted on `sync/telemetry/batch` and materialize into project- or fleet-scoped `edge_ts_*` hypertables after schema approval.

## Ignore (`strategy: ignore`)

Skip replication for scratch, cache, or local-only tables:

```yaml theme={null}
table_strategies:
  temp_import:
    strategy: ignore
```

## Comparison

|               | Row journal                    | Telemetry batch        |
| ------------- | ------------------------------ | ---------------------- |
| MQTT topic    | `sync/rows/batch`              | `sync/telemetry/batch` |
| Dedup         | Per row                        | Per batch + batch\_seq |
| Cloud storage | `edge_state_*` table (typical) | `edge_ts_*` hypertable |
| Omega status  | **Production**                 | **Production**         |
| PK required   | Yes                            | Timestamp column       |

## Journal epoch

If Omega truncates/rebuilds `__omega_journal`, it generates a new **`journal_epoch`** (UUIDv7). Required so `commit_seq=1` after reinit does not dedupe against pre-reinit rows.

## Pre-existing rows

With `snapshot_on_first_run: true`, Omega seeds existing SQLite rows into its local journal once before switching to normal incremental replication.

To backfill historical data:

* Run application migrations that touch rows after Omega start, or
* Use `snapshot_on_first_run: true` for the built-in one-time local bootstrap; use the secondary/object-upload flow only for larger future spill/bootstrap scenarios.

## Schema columns on first batch

First batch for a table may include **`schema_columns`** inline (full fingerprint structure) to aid server classification. Subsequent batches carry **`schema_hash`** only.

→ [Schema governance](/edge/data-sync/schema-governance)

## Related

* [Configuration](/edge/data-sync/configuration)
* [Payload formats](/edge/data-sync/payload-formats)
