Mirrors

Mirrors are the core of BunnyDB - they represent active CDC (Change Data Capture) replication streams from a source PostgreSQL database to a destination PostgreSQL database.

List Mirrors

Retrieve all configured replication mirrors with their current status.

Endpoint

GET /v1/mirrors

Permission: authed (any authenticated user)

Response

Returns an array of mirror objects with basic status information.

Example

curl http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Create Mirror

Create a new replication mirror. This operation starts the initial snapshot and begins CDC replication.

Endpoint

POST /v1/mirrors

Permission: adminOnly

Request Body

FieldTypeRequiredDescription
namestringYesUnique identifier for this mirror
source_peerstringYesName of the source peer
destination_peerstringYesName of the destination peer
table_mappingsarrayYesArray of table mapping objects
snapshot_num_rows_per_partitionnumberNoRows per partition during snapshot (default: 500000)
snapshot_max_parallel_workersnumberNoMax parallel workers for snapshot (default: 4)
snapshot_num_tables_in_parallelnumberNoTables to snapshot in parallel (default: 4)
cdc_sync_interval_secondsnumberNoCDC polling interval (default: 60)
cdc_batch_sizenumberNoChanges per batch (default: 10000)
do_initial_snapshotbooleanNoPerform initial snapshot (default: true)
publication_namestringNoCustom publication name (auto-generated if not provided)
replication_slot_namestringNoCustom replication slot name (auto-generated if not provided)

Table Mapping Object

FieldTypeRequiredDescription
source_schemastringYesSource schema name
source_tablestringYesSource table name
destination_schemastringYesDestination schema name
destination_tablestringYesDestination table name
exclude_columnsarrayNoColumn names to exclude from replication

Response

Returns the created mirror configuration.

Example

curl -X POST http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-to-analytics",
    "source_peer": "production-db",
    "destination_peer": "analytics-db",
    "table_mappings": [
      {
        "source_schema": "public",
        "source_table": "users",
        "destination_schema": "public",
        "destination_table": "users",
        "exclude_columns": ["password_hash", "ssn"]
      },
      {
        "source_schema": "public",
        "source_table": "orders",
        "destination_schema": "public",
        "destination_table": "orders"
      }
    ],
    "snapshot_num_rows_per_partition": 500000,
    "snapshot_max_parallel_workers": 4,
    "snapshot_num_tables_in_parallel": 2,
    "cdc_sync_interval_seconds": 30,
    "cdc_batch_size": 10000,
    "do_initial_snapshot": true
  }'

Initial Snapshot: By default, BunnyDB performs an initial snapshot of all tables before starting CDC. Set do_initial_snapshot: false if tables are already populated and you only want to capture new changes.

⚠️

Performance Tuning: Adjust snapshot_num_rows_per_partition, snapshot_max_parallel_workers, and cdc_batch_size based on your database size and network capacity. See Configuration for tuning recommendations.

Error Responses

Status CodeErrorDescription
400Bad RequestInvalid parameters or missing required fields
401UnauthorizedMissing or invalid token
403ForbiddenUser does not have admin role
409ConflictMirror name already exists

Get Mirror Status

Retrieve detailed status information for a specific mirror.

Endpoint

GET /v1/mirrors/{name}

Permission: authed (any authenticated user)

Path Parameters

ParameterTypeDescription
namestringUnique mirror name

Response

FieldTypeDescription
namestringMirror name
source_peerstringSource peer name
destination_peerstringDestination peer name
statusstringCurrent status (see statuses below)
last_lsnstringLast replicated LSN (Log Sequence Number)
last_sync_batch_idnumberLast applied batch ID
error_messagestringError message if status is error
error_countnumberNumber of consecutive errors
tablesarrayPer-table sync status
created_atstringISO 8601 timestamp
updated_atstringISO 8601 timestamp

Table Status Object

FieldTypeDescription
table_namestringFully qualified table name (schema.table)
statusstringTable sync status
rows_syncednumberTotal rows synced
rows_insertednumberRows inserted during CDC
rows_updatednumberRows updated during CDC
rows_deletednumberRows deleted during CDC
last_synced_atstringISO 8601 timestamp

Mirror Statuses

StatusDescription
initializingMirror is being set up
snapshottingInitial snapshot in progress
runningCDC replication active
pausedMirror manually paused
errorError occurred, waiting for retry
stoppedMirror stopped (terminal state)

Example

curl http://localhost:8112/v1/mirrors/prod-to-analytics \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Delete Mirror

Delete a mirror and clean up all associated resources (replication slot, publication, workflows).

Endpoint

DELETE /v1/mirrors/{name}

Permission: adminOnly

Path Parameters

ParameterTypeDescription
namestringUnique mirror name

Response

FieldTypeDescription
deletedstringName of the deleted mirror

Example

curl -X DELETE http://localhost:8112/v1/mirrors/prod-to-analytics \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
⚠️

Cleanup: Deleting a mirror terminates the Temporal workflow, drops the replication slot, and removes the publication from the source database. This operation cannot be undone. Data on the destination remains unchanged.

Error Responses

Status CodeErrorDescription
401UnauthorizedMissing or invalid token
403ForbiddenUser does not have admin role
404Not FoundMirror does not exist

Understanding LSN (Log Sequence Number)

The last_lsn field represents the PostgreSQL Write-Ahead Log (WAL) position that has been replicated. LSNs are in the format segment/offset (e.g., 0/1A2B3C4D).

What it means:

  • LSN advances as changes are replicated
  • If LSN stops advancing, replication may be stalled
  • Compare with source database’s current LSN to calculate replication lag

Check source LSN:

SELECT pg_current_wal_lsn();

Check replication slot lag:

SELECT slot_name,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) as lag
FROM pg_replication_slots;

See Monitoring for more details on tracking replication health using LSN and other metrics.

Common Workflows

Creating a Simple Mirror

Replicate all tables from one database to another:

curl -X POST http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "simple-mirror",
    "source_peer": "source-db",
    "destination_peer": "dest-db",
    "table_mappings": [
      {
        "source_schema": "public",
        "source_table": "users",
        "destination_schema": "public",
        "destination_table": "users"
      }
    ]
  }'

Excluding Sensitive Columns

Replicate tables while excluding PII or sensitive data:

curl -X POST http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "sanitized-mirror",
    "source_peer": "source-db",
    "destination_peer": "dest-db",
    "table_mappings": [
      {
        "source_schema": "public",
        "source_table": "users",
        "destination_schema": "public",
        "destination_table": "users",
        "exclude_columns": ["password_hash", "ssn", "credit_card"]
      }
    ]
  }'

Skip Initial Snapshot

Start CDC without snapshot (when destination already has data):

curl -X POST http://localhost:8112/v1/mirrors \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "cdc-only-mirror",
    "source_peer": "source-db",
    "destination_peer": "dest-db",
    "table_mappings": [...],
    "do_initial_snapshot": false
  }'

After creating a mirror, use the Mirror Control endpoints to pause, resume, resync, or manage the replication.