Change feed (CDC) #
GET /_changes serves a collection’s change log tail from the shard
engines’ WALs, so external systems can replicate Pizza data elsewhere —
off-site disaster recovery, secondary indexing, audit pipelines — with a
standard poll loop:
GET /demo:orders/_changes?since=abc:3,def:1&limit=1000
{
"results": [
{"shard":"abc…","rolling_id":0,"epoch_id":4,"seq":0,
"id":"0,7","key":"u1","op":"create","doc":{"qty":3}},
{"shard":"abc…","rolling_id":0,"epoch_id":4,"seq":1,
"id":"0,8","op":"delete"}
],
"cursors": {"abc…": 4, "def…": 1},
"purged_below": {"abc…": 3},
"active": {"abc…": 5},
"pending": true
}
Request #
GET /_changes
GET /<targets>/_changes
Path parameters #
targets
(Optional, string) Comma-separated collection names (wildcard supported); the target-less form feeds every collection.
Query parameters #
since
(Optional, string) Per-shard epoch cursors from a previous response,shard_hex:epoch,shard_hex:epoch. Omit to start from the beginning of the retained tail.limit
(Optional, integer) Maximum rows to return. Defaults to1000.
Consuming the feed #
- Poll with the
cursorsof the previous response as the nextsince(persist the cursors — they are the checkpoint). pending: truemeans more rows exist beyondlimit; poll again immediately, otherwise poll at your cadence.- Compare each cursor against
purged_below: a cursor below that floor means the WAL retention window (wal.local.retention_epoch/retention_bytes) has already purged part of the tail for that shard. The tail is incomplete — run a full resync (search + re-bulk into the DR cluster) and resume from the currentcursors.
Every write path (index/replace/update/delete) appends a WAL entry on the primary and every replica, so the feed reads from whichever copy of the shard group is allocated to the node serving the request (primary preferred).