Logical data replication is only supported in CockroachDB self-hosted clusters.
- Conflict resolution: As changes to a table replicate from the source to the destination cluster, there can be conflicts during some operations that the job will handle with conflict resolution. When LDR is started, the job creates a dead letter queue (DLQ) table with each replicating table. LDR will send any unresolved conflicts to the DLQ, which you should monitor as LDR continues to replicate changes between the source and destination clusters.
- Schema changes: The tables that are part of the LDR job may require schema changes, which need to be coordinated manually. There are some schema changes that are supported while LDR jobs are actively running.
- Jobs: and can operate on clusters running LDR . You may want to consider where you start and how you manage , , , and so on when you’re running LDR.
Conflict resolution
In LDR, conflicts are detected at both the and the level, which determines how LDR resolves the conflict.KV level conflicts
LDR uses last write wins (LWW) conflict resolution based on the of the replicating write. LDR will resolve conflicts by inserting the row with the latest MVCC timestamp. Conflicts at the KV level are detected when there is either:- An
UPDATEoperation replicated to the destination cluster. - A cross-cluster write occurs, which means both clusters are writing to the same key. For example, if the LDR stream attempts to apply a row to the destination cluster where the existing row on the destination was not written by the LDR stream.
SQL level conflicts
When a conflict cannot apply due to violating , for example, a schema constraint, LDR will send the row to the DLQ.Dead letter queue (DLQ)
When the LDR job starts, it creates a DLQ table with each replicating table so that unresolved conflicts can be tracked. The DLQ contains the writes that LDR cannot apply after the retry period of a minute, which could occur if there is a unique index on the destination table (for more details, refer to ). As an example, for an LDR stream created on themovr.public.promo_codes table:
movr.crdb_replication.dlq_113_public_promo_codes DLQ:
Manage entries in the DLQ
LDR does not pause when writes are sent to the DLQ. You must manage the DLQ manually by examining each entry in the DLQ and either reinserting the entry or deleting it from the DLQ. If you have multiple DLQ entries, manage them in order from most recent to least recent. To manage an entry in the DLQ:-
In the destination database’s DLQ table, examine the
incoming_rowcolumn to find the primary key and values for the entry.In this example result,incoming_rowcontains a primary key of207identified by the columnmy_id, as well as the values of the entry’s columnscreated_atandpayload. -
Determine whether the values for the entry in the DLQ match the values for the entry in the destination table and the source table respectively:
-
On the destination database, check the values for the entry and the replicated time:
-
On the source database, check the values for the entry as of the replicated time:
-
On the destination database, check the values for the entry and the replicated time:
-
Determine a course of action based on the results of the previous steps:
-
If the values for the entry are the same in both the destination table and the source table, delete the entry from the DLQ on the destination database:
-
If the entry’s values in the destination table are different from its values in the source table, but the entry’s values in the source table equal its values in the DLQ, update the entry in the destination table to have the same values as in the source table:
If this upsert fails due to a constraint violation, you must either delete the row that the upsert conflicts with or delete the DLQ entry. If the destination table has unique or foreign key constraints, the DLQ will likely continue to accumulate entries.
-
If the entry’s values in the destination table are different from its values in the source table, and the entry’s values in the source table do not equal its values in the DLQ, refresh the replicated time and retry the equality queries above. If the same results hold after a few retries with refreshed replicated times, there is likely a more recent entry for the same row in the DLQ.
-
To find the more recent entry, find all entries in the DLQ with the matching primary key:
- If there are more recent entries for the row, delete the less recent entries and repeat these steps to manage the most recent entry.
-
To find the more recent entry, find all entries in the DLQ with the matching primary key:
-
If the values for the entry are the same in both the destination table and the source table, delete the entry from the DLQ on the destination database:
Schema changes
When you start LDR on a table, the job will lock the schema, which will prevent any accidental that would cause issues for LDR. There are some supported schema changes that you can perform on a replicating table, otherwise it is necessary to stop LDR in order to coordinate the schema change.Supported schema changes
There are some supported schema changes, which you can perform during LDR without restarting the job:| Allowlist schema change | Exceptions |
|---|---|
| |
| N/A | |
| N/A | |
| N/A | |
| N/A | |
| N/A | |
| N/A | |
| changes | N/A |
| N/A | |
|
LDR will not replicate the allowlist schema changes to the destination table. Therefore, you must perform the schema change carefully on both the source and destination cluster.
Coordinate other schema changes
To perform any other schema change on the table, use the following approach to redirect application traffic to one cluster. You’ll need to drop the existing LDR jobs, perform the schema change, and start new LDR jobs, which will require a full initial scan. If you are running LDR in a unidirectional setup, follow Coordinate schema changes for unidirectional LDR.Redirect application traffic to one cluster
You have a bidirectional LDR setup with a stream between cluster A to cluster B, and a stream between cluster B to cluster A.- Redirect your application traffic to one cluster, for example, cluster A.
-
Wait for all traffic from cluster B to replicate to cluster A. Check this is complete with:
This is complete when
replicated_timeon cluster B surpasses the time at which you redirected application traffic, which indicates that all traffic from cluster B has been replicated to cluster A. -
Drop the LDR job on both clusters. Canceling the LDR streams will remove the history retention job, which will cause the data to be garbage collected according to the setting. Use :
- Perform the schema change on cluster A.
- Drop the existing table from cluster B.
- Recreate the table and its new schema on cluster B.
-
Create new LDR streams for the table on both clusters A and B. Run
CREATE LOGICAL REPLICATION STREAMfrom the destination cluster for each stream:If your table does not contain any user-defined types or dependencies, use the syntax to start the stream for a fast, offline initial scan and automatic destination table setup.
Coordinate schema changes for unidirectional LDR
If you have a unidirectional LDR setup, you should cancel the running LDR stream and redirect all application traffic to the source cluster.-
Drop the LDR job on the destination cluster. Canceling the LDR job will remove the history retention job, which will cause the data to be garbage collected according to the setting. Use :
-
Once the job has
canceled, perform the required schema change on both the source and destination tables. -
Start a new LDR job from the destination cluster:
If your table does not contain any user-defined types or dependencies, use the syntax to start the stream for a fast, offline initial scan and automatic destination table setup.
Jobs and LDR
You can run changefeed and backup on any cluster that is involved in an LDR job. Both source and destination clusters in LDR are active, which means they can both serve production reads and writes as well as run and .Changefeeds
will emit for the writes that occur to the watched table. If the watched table is also the destination to which LDR is streaming, the changefeed will additionally emit messages for the writes from the LDR job. For example:- You create a changefeed watching the
test_tableon cluster A. - You start LDR from cluster A
test_tablereplicating to cluster B’stest_table. There are writes totest_tablehappening on both clusters. At this point, the changefeed is only emitting messages for cluster A (the source of the LDR job). - You start another LDR job from cluster B to cluster A to create bidirectional LDR. This second LDR job sends writes occurring on cluster B
test_tableinto cluster Atest_table. The changefeed on cluster A will now start emitting messages for both the writes occuring from application traffic in cluster A and also the writes incoming from LDR running from cluster B to cluster A.
Backups
can run on either cluster in an LDR stream. If you’re backing up a table that is the destination table to which an LDR job is streaming, the backup will include writes that occur to the table from the LDR job.TTL
If you’re running jobs, you may not want to include these deletes in LDR. You can ignore row-level TTL deletes in LDR with the set on the table in the source cluster. If you would like to ignore TTL deletes in LDR, you can use thediscard = ttl-deletes option in the .

