Search docs.molo17.com
    Configuration guide Connectors

    Set up a CockroachDB target with TLS and upserts

    CockroachDB speaks the PostgreSQL wire protocol, and the Gluesync CockroachDB agent writes to it through a JDBC driver. CockroachDB is a target only: the agent has no change data capture from it. This guide creates a database user with only the grants Gluesync needs, shows which TLS settings work against a secure cluster and against an insecure one, creates the target, loads a snapshot and live changes from PostgreSQL, explains how the agent writes the rows, and shows how to handle the permission, TLS and transaction retry errors that a new setup is most likely to meet.

    In this guide

    You will create a CockroachDB user and grant it the database and table privileges that the agent needs, including the one that "Empty the table first" needs on tables you created yourself. You will choose the Enable TLS and Trust server certificate settings that match your cluster, and place the CA certificate where the driver looks for it when you want the server certificate verified. You will create the target connection, add an entity so that Gluesync creates the CockroachDB table, run a snapshot, and follow inserts, updates and deletes. You will read how the agent writes rows, set the batch size and the number of writers, and recover from a transaction retry error.

    Quick information

    Goal

    Replicate a PostgreSQL table to CockroachDB with a snapshot and CDC, over a secure or an insecure connection

    Audience

    Data engineers and database administrators who own a CockroachDB cluster

    Level

    Intermediate

    Time required

    About 40 minutes

    Area

    CockroachDB → SQL; Control Plane → Pipelines → CockroachDB target → Base settings, Security and Advanced settings; entity → Fine tuning and start options

    Applies to

    Gluesync 2.3

    Before you begin

    Make sure that:

    1. You have a CockroachDB cluster and you can run SQL on it as an administrator such as root. The agent supports CockroachDB 20.0 and later. This guide was tested with CockroachDB 24.3, one secure single-node cluster and one insecure single-node cluster;

    2. Core Hub can open a connection to the SQL port of the cluster, 26257 by default;

    3. For a secure cluster, you have the CA certificate of the cluster (ca.crt) if you want Gluesync to verify the server;

    4. You can sign in to the Control Plane with a role that can create pipelines;

    5. You have a PostgreSQL source with a table that has a primary key. This guide uses public.orders, with 200,000 rows and these columns: id bigint (primary key), kind varchar(20), val integer, amount numeric(12,2), created_at timestamptz, note text, flag boolean, payload jsonb and uid uuid.

    The statements in this guide are plain CockroachDB SQL. The messages quoted here were captured in the Control Plane and in the Core Hub log of a running Gluesync 2.3 against CockroachDB 24.3.

    Guide steps

    Create the CockroachDB user and grant what the target needs

    SQL

    Gluesync does not need the admin role. Connect to the cluster as root and run the statements below. Replace the database name and the password.

    1. Create the database, the user, and let the user connect and create tables:

      CREATE DATABASE demo;
      CREATE USER gluesync WITH PASSWORD '<a strong password>';
      GRANT CONNECT, CREATE ON DATABASE demo TO gluesync;

      CREATE on the database lets the user create the tables that Gluesync proposes in the Field mapping dialog. The user becomes the owner of the tables it creates, so it needs nothing else for them. On an insecure cluster, CockroachDB refuses a password (ERROR: setting or updating a password is not supported in insecure mode), so use CREATE USER gluesync;. The agent still asks for a password in the form: enter any value;

    2. For tables that already exist and that you created as another user, grant the table privileges instead:

      GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE demo.public.orders TO gluesync;
    3. Add DROP if you will use Empty the table first before a snapshot. The agent empties the table with TRUNCATE, and CockroachDB requires DROP for it:

      GRANT DROP ON TABLE demo.public.orders TO gluesync;
    4. Check what the user holds:

      SHOW GRANTS ON DATABASE demo;
      SHOW GRANTS ON TABLE demo.public.orders;
    Expected result

    SHOW GRANTS ON DATABASE lists CONNECT and CREATE for gluesync. For a table owned by another user, SHOW GRANTS ON TABLE lists SELECT, INSERT, UPDATE and DELETE (and DROP if you added it).

    If a grant is missing, the error says which privilege and which table. Step 8 lists the messages.

    Choose the connection security

    CLI

    The target form has two switches in the Security section: Enable TLS and Trust server certificate. They behave differently from what their names suggest, so check them against your cluster before you create the target. In the tests, Trust server certificate decides whether the driver requires SSL and verifies the certificate (the SSL mode of the driver is prefer when the switch is on and verify-ca when it is off).

    Cluster Settings Result

    Secure

    Enable TLS off, Trust server certificate on (the defaults)

    Connects. The certificate of the server is not verified

    Secure

    Enable TLS on, Trust server certificate on

    Connects. The certificate of the server is not verified

    Secure

    Enable TLS on, Trust server certificate off, no CA certificate in place

    Fails with Could not open SSL root certificate file /home/gluesync/.postgresql/root.crt.

    Secure

    Enable TLS on, Trust server certificate off, CA certificate in place

    Connects. The certificate of the server is verified against your CA

    Insecure

    Enable TLS on, Trust server certificate on

    Connects, without TLS

    Insecure

    Enable TLS off, Trust server certificate off

    Fails with The server does not support SSL.

    To have the server certificate verified, give the driver the CA certificate of your cluster. The driver reads it from /home/gluesync/.postgresql/root.crt inside the Core Hub container. With Docker Compose, mount your ca.crt there:

    services:
      core-hub:
        volumes:
          - ./certs/ca.crt:/home/gluesync/.postgresql/root.crt:ro

    Restart Core Hub after you add the mount. The driver mode is verify-ca: it checks that the CA signed the certificate of the server, and it does not check the host name.

    Expected result

    The container holds the file /home/gluesync/.postgresql/root.crt. You can check it with docker exec <core hub container> ls -l /home/gluesync/.postgresql/. If you keep Trust server certificate on, you need no file.

    The two Upload files blocks of the Security section (trust store and key store) are not needed for the password sign-in of this guide. They were not used in the tests.

    Create the pipeline and the CockroachDB target connection

    UI

    1. In the Control Plane, select Create your first pipeline, or the + button of the pipeline list if you already have pipelines. The Agents setup wizard opens;

    2. In 1. Setup source, choose the PostgreSQL CDC agent tile and fill Base settings: Host name / Address, Port, Database name, Username and Password of your source database. Select Test and continue. The source appears with a green check mark. Select Next;

    3. In 2. Setup target(s), choose the CockroachDB tile and fill Base settings:

      • Host name / Address: the host of your cluster;

      • Port: 26257, or the SQL port of your cluster;

      • Database name: demo;

      • Username and Password: the user of step 1;

    4. In Security, set Enable TLS and Trust server certificate as step 2 describes. Leave the Connection string, Min connections count and Max connections count as they are unless you must change them;

    5. Select Test and continue. The target appears with a green check mark. Select Next, enter a pipeline name and select Finish.

    Edit target page of the CockroachDB agent with host name
    Figure 1. The CockroachDB target connection: host, port, database, user, password, and the Security switches.
    Expected result

    The pipeline appears in the HEALTHY group of the pipeline list, with the PostgreSQL and the CockroachDB icons. If the target does not save, the page shows Failed to save with the reason. A wrong password gives Failed to initialize pool: ERROR: password authentication failed for user gluesync.

    If you prefer the REST API, the same settings go in hostCredentials. The key names are in the CockroachDB target reference.

    Add the entity and let Gluesync create the table

    UI

    1. In the pipeline, select Entities. The Add entities page opens with the Object browser. Expand the source schema public and select the table orders. The Field mapping dialog opens;

    2. Under Target, the Target Schema is public. In Target Table type orders and choose Create table "orders". The dialog shows New table orders will be created on confirm. and the proposed target types;

    3. Look at the Length column. For the text, jsonb and uuid columns it shows 0 in red, with the tooltip Required, and the banner Missing required field disables Confirm. Enter 1 in each of the three cells. The number is not used: the statement in the next step has no length for these types;

    4. Select Confirm. The Create table orders dialog shows the Read-only SQL statement. Read it, then select Create table orders to run it on CockroachDB;

    5. Back on the Add entities page, select Confirm and continue.

    Field mapping dialog with the banner Missing required field and the Length cells of the columns note
    Figure 2. The Length cells of text, jsonb and uuid start at 0, flagged as Required.

    The statement has this form. It is read-only: to use another key or other types, create the table in CockroachDB yourself first and choose it in Target Table.

    CREATE TABLE public.orders (
       id bigint,
       kind character varying,
       val integer,
       amount numeric(12, 2),
       created_at timestamptz,
       note text,
       flag boolean,
       payload jsonb,
       uid uuid,
       PRIMARY KEY (id)
    )
    Create table orders dialog with a read-only CREATE TABLE statement for public.orders with a primary key on id
    Figure 3. The create table statement that Core Hub proposes.

    Check what CockroachDB created:

    SHOW CREATE TABLE public.orders;
    Expected result

    The entity appears in the pipeline on Hold as public.orders. SHOW CREATE TABLE returns id INT8 NOT NULL, kind VARCHAR NULL, val INT8 NULL, amount DECIMAL(12,2) NULL, created_at TIMESTAMPTZ NULL, note STRING NULL, flag BOOL NULL, payload JSONB NULL, uid UUID NULL and the primary key on id. Two things differ from the source: the varchar(20) length is not kept, and integer is INT8, because an INT in CockroachDB is a 64-bit integer.

    See Target table creation for the dialog.

    Run the snapshot and check the rows

    UI + SQL

    The CockroachDB agent has no bulk load: the Writing column of the start dialog says This target has no bulk load. It writes batches over the JDBC connection.

    1. In the pipeline, find the entity. On Hold, select the start options button, the one next to the play button. The Snapshot (copy-only) dialog opens;

    2. Keep Keep and merge under Data already in the target. It overwrites the rows that have the same key and keeps the others;

    3. Under Speed select Custom. In the Writing column, set Writers during the copy to 1 and keep If a row with the same key is already there on Upsert. Step 7 explains why one writer is the safe choice for a first load;

    4. Select Start snapshot.

    Snapshot copy-only dialog with Speed Custom
    Figure 4. The Writing column of the Custom speed: the standard write method, the writers during the copy, and the duplicate key setting.

    Check the rows in CockroachDB and compare them with the source:

    SELECT count(*) FROM public.orders;
    SELECT count(*), sum(val), sum(amount) FROM public.orders;
    Expected result

    The count equals the source count, 200000, and the sums equal the sums of the source. In the test, one writer copied 200,000 rows in about 15 seconds on a single-node cluster on a laptop. The entity returns to Hold when the copy ends. The Core Hub log shows Snapshot write completed for entity public.orders.

    In the tests the agent did not send INSERT …​ ON CONFLICT. With Keep and merge, it writes each batch as two statements, which you can read in the statement statistics of CockroachDB:

    SELECT key, count FROM crdb_internal.node_statement_statistics
    WHERE application_name = 'Gluesync' ORDER BY count DESC LIMIT 5;

    The statements have this form: an UPDATE …​ FROM over the batch for the keys that exist, then an INSERT …​ SELECT …​ WHERE NOT EXISTS for the keys that do not.

    WITH src (id, kind, ...) AS (SELECT id, kind, ... FROM (VALUES (...), (...)) AS t (id, kind, ...))
    UPDATE public.orders AS dest SET kind = src.kind, ... FROM src WHERE src.id = dest.id
    
    WITH src (id, kind, ...) AS (...)
    INSERT INTO public.orders(id, kind, ...) SELECT src.id, src.kind, ... FROM src
    WHERE NOT EXISTS (SELECT _ FROM public.orders AS dest WHERE src.id = dest.id)

    To prove that Keep and merge overwrites, change three rows in CockroachDB, run the snapshot again, and read them: they return to the values of the source. On duplicate key decides what happens when an insert meets a row that is already there, see On duplicate key.

    Capture live changes and read the results

    UI + SQL

    The copy-only snapshot leaves the entity on Hold. Start it to follow the changes, then change the source and read the target.

    1. Select the play button of the entity. The Sync changes (CDC) dialog opens. Under Start from choose Now, because the rows are already in the target. Keep If a row with the same key is already there on Upsert and select Start entity. The entity becomes Active;

    2. On the source, insert, update and delete one row each:

      INSERT INTO orders VALUES (300001, 'new', 7, 12.50, now(), 'cdc row', true, '{"k":1}', gen_random_uuid());
      UPDATE orders SET val = -1, kind = 'cdc-upd' WHERE id = 5;
      DELETE FROM orders WHERE id = 6;
    3. After a few seconds, read the three keys on CockroachDB:

      SELECT id, kind, val FROM public.orders WHERE id IN (5, 6, 300001) ORDER BY id;
      SELECT count(*) FROM public.orders;
    Expected result

    The first query returns the row 5 with val -1 and kind cdc-upd, and the row 300001, and no row 6. The count is 200000 again: one row added, one deleted. In the statement statistics, the insert is a plain INSERT INTO public.orders(…​) VALUES (…​), the update is the UPDATE …​ FROM statement of step 5, and the delete is DELETE FROM public.orders WHERE id = _.

    CockroachDB is a target only. You cannot start a pipeline that reads changes from it.

    Set the batch size, the writers and the entity options

    UI

    Three settings control how fast the agent writes. They apply to each entity.

    1. Max Write Batch Size and Max Delete Batch Size: both default to 1000 rows. In the Add entities page, select the gear of the entity, then Entity. The two properties are at the top of the tab.

    2. Snapshot writing concurrency in Fine tuning: the number of writers during the snapshot, from 1 to 10. The start dialog shows the same setting as Writers during the copy;

    3. Use connection pool, Min connections count and Max connections count of the target. The pool is on by default. The agent logs a warning when you turn it off, because the performance is lower.

    Entity settings of public.orders with Max Write Batch Size 1000
    Figure 5. The Entity tab of the entity settings: Max Write Batch Size and Max Delete Batch Size, and the SQL commands that run before and after a snapshot or CDC.

    In the Advanced settings tab of the target, Add property offers three more keys: Enable Recursion Protection, State Preservation Table Name Prefix and Disable truncate on target table before snapshot inserts. The third one stops the agent from emptying the table at the start of a snapshot.

    More writers are not always faster. With Keep and merge into a table that does not hold the rows yet, two writers made CockroachDB abort transactions with a retry error, and the snapshot stopped and started again. One writer copied the same 200,000 rows in about 15 seconds without an error. Start with one writer, and raise it only after a test on your cluster. Step 8 shows the error.

    See Tune replication throughput for the settings that apply to every target.

    Fix the errors you are most likely to meet

    SQL + UI

    Each error below was reproduced against a running cluster.

    1. The user cannot empty a table that it did not create. The user holds SELECT, INSERT, UPDATE and DELETE on a table that another user owns, and you start a snapshot with Empty the table first. The entity goes to Error and the Core Hub log shows ERROR: user gluesync does not have DROP privilege on relation orders. Core Hub keeps retrying the write. Fix: run GRANT DROP ON TABLE demo.public.orders TO gluesync;, or choose Keep and merge. After the grant, the snapshot with Empty the table first ran without an error;

    2. A transaction retry error. With two writers and Keep and merge into a table without rows, the notification Serialization failure Error shows a message that starts with ERROR: restart transaction: TransactionRetryWithProtoRefreshError: TransactionRetryError: retry txn (RETRY_SERIALIZABLE - failed preemptive refresh due to conflicting locks. CockroachDB cancelled one of two transactions that wrote overlapping keys. The entity shows Error, and Core Hub starts the snapshot again by itself with a growing delay. In the test, the copy finished about two minutes after the first error, with all 200,000 rows. Fix: set Writers during the copy to 1, which copied the 200,000 rows in about 15 seconds without an error, or start the snapshot with Empty the table first, which ran with eight writers without an error;

    3. A TLS or password error. Step 2 lists the TLS messages, and step 3 shows the message for a wrong password.

    Expected result

    After each fix the entity writes again. The row count of CockroachDB matches the source.

    Data types

    The agent maps each Gluesync type to a CockroachDB type. The types that came out of the test table:

    Source type (PostgreSQL) CockroachDB type Note

    bigint

    INT8

    The same width

    integer

    INT8

    INT in CockroachDB is a 64-bit integer

    numeric(12,2)

    DECIMAL(12,2)

    Precision and scale kept

    varchar(20)

    VARCHAR

    The length is not kept in the proposed statement

    text

    STRING

    The Length cell shows a required 0, see step 4

    boolean

    BOOL

    true and false

    timestamptz

    TIMESTAMPTZ

    Precision 6

    jsonb

    JSONB

    The Length cell shows a required 0, see step 4

    uuid

    UUID

    The Length cell shows a required 0, see step 4

    The CockroachDB agent overview lists the supported versions and features.

    Verify the result

    You have set up the CockroachDB target if:

    • The user has CONNECT and CREATE on the database, and for tables it did not create, SELECT, INSERT, UPDATE, DELETE and DROP;

    • You know whether your cluster is secure or insecure, and the Trust server certificate switch matches it;

    • The target connection shows a green check mark;

    • The row count and the sums of the target equal the source after the snapshot;

    • A CDC insert, update and delete reach the target;

    • The snapshot ran without a transaction retry error, or you reduced the writers to one.

    Troubleshooting

    Problem Possible cause What to do

    Failed to save with Failed to initialize pool: ERROR: password authentication failed for user gluesync

    The password or the user name is wrong, or the user does not exist on this cluster

    Check the user with SHOW USERS; and set the password again with ALTER USER gluesync WITH PASSWORD '…​';

    Failed to initialize pool: Could not open SSL root certificate file /home/gluesync/.postgresql/root.crt.

    Trust server certificate is off and the Core Hub container has no CA certificate at that path

    Mount your ca.crt at /home/gluesync/.postgresql/root.crt and restart Core Hub, or switch Trust server certificate on

    Failed to initialize pool: The server does not support SSL.

    Trust server certificate is off, and the cluster is insecure

    Switch Trust server certificate on

    Missing required field in the Field mapping dialog, and Confirm is disabled

    The Length cell of a text, jsonb or uuid column starts at 0

    Enter 1 in each of those cells. The value is not used in the statement

    ERROR: user gluesync does not have DROP privilege on relation orders

    Empty the table first runs TRUNCATE, and the user does not own the table

    GRANT DROP ON TABLE <table> TO gluesync;, or use Keep and merge

    Serialization failure Error with retry txn (RETRY_SERIALIZABLE …​

    Two or more writers upserted into a table that did not hold the rows yet, and CockroachDB cancelled one transaction

    Set Writers during the copy to 1. Core Hub starts the snapshot again by itself, but each failure costs time

    The entity is in Error and the log repeats Retrying to handle transaction message

    The target rejects every write, for example because of a missing privilege

    Read the first ERROR line of the Core Hub log, fix the cause on CockroachDB, and the entity writes again

    Contact MOLO17 Support if you need assistance with a CockroachDB target.