Set up a SQL Server CDC source with the SQL Server Agent
The MS SQL Server CDC agent reads the change tables that SQL Server fills from its transaction log. SQL Server Agent runs the capture job that fills them, and the cleanup job that empties them. This guide turns on the Agent, enables CDC on a database and on its tables, sets the retention, and creates a Gluesync user with the fewest privileges that work. It then runs a snapshot and live changes into a MySQL target, handles a column added to a captured table, and shows what happens when the cleanup deletes changes that Gluesync has not read yet.
In this guide
You will check that SQL Server Agent runs, enable CDC on the database and on the tables, and read the two jobs that SQL Server creates. You will choose a retention for the cleanup job. You will create a Gluesync login with db_datareader only, and learn when db_owner or sysadmin is needed instead. You will create the connection and the entity, run a snapshot followed by live changes, add a column to a captured table, and recover from a retention gap.
Quick information
Goal |
Replicate a SQL Server table with a snapshot followed by change data capture (CDC), and keep the capture jobs, the retention and the schema changes under control |
Audience |
Database administrators who own a SQL Server instance, and data engineers who set up the pipeline |
Level |
Intermediate |
Time required |
About 60 minutes |
Area |
SQL Server → CDC, SQL Server Agent; Control Plane → Pipelines → MS SQL Server CDC agent → Base settings and Advanced settings; entity → start dialog |
Applies to |
Gluesync 2.3 |
Before you begin
Make sure that:
-
You have a SQL Server instance with SQL Server Agent available, for example Developer, Standard or Enterprise edition. Express has no SQL Server Agent. This guide was tested with SQL Server 2022 Developer on Linux;
-
You can sign in with the
sysadminserver role. Enabling CDC on a database needs it; -
You can restart the SQL Server service. On Linux, turning on the Agent needs a restart;
-
Core Hub can open a connection to the instance on the SQL Server port,
1433by default; -
You have a target to write to. This guide uses a MySQL database: any target works, and the steps for the source are the same;
-
You can sign in to the Control Plane with a role that can create pipelines;
-
You have a source table. This guide uses
dbo.ordersin the databaseshop, with 100,000 rows (id bigintprimary key,customer varchar(50),status varchar(20),amount decimal(10,2),created_at datetime2). A table without a primary key works too, see Choose keys for tables without a primary key.
|
The messages quoted in this guide were captured in the Control Plane, in the Core Hub log and in |
Guide steps
CLI + SQL
CDC on SQL Server needs the Agent: its capture job reads the transaction log and writes the change tables. On Windows, start the SQL Server Agent service from SQL Server Configuration Manager or services.msc. On Linux, or in a container, turn it on and restart the service:
/opt/mssql/bin/mssql-conf set sqlagent.enabled true
systemctl restart mssql-server
In a container, set the environment variable MSSQL_AGENT_ENABLED=True instead, or run mssql-conf as root inside the container and restart it with docker restart <container>.
Then connect as sysadmin and read the state of the Agent:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'Agent XPs';
SELECT servicename, status_desc FROM sys.dm_server_services;
|
Without the Agent you can still run |
SQL
CDC is enabled twice: once for the database, once for each table. Connect as sysadmin:
USE shop;
EXEC sys.sp_cdc_enable_db;
EXEC sys.sp_cdc_enable_table
@source_schema = N'dbo',
@source_name = N'orders',
@role_name = NULL,
@supports_net_changes = 0;
Repeat sys.sp_cdc_enable_table for every table you replicate. Two arguments matter:
-
@role_name = NULLis the value Gluesync uses when it enables a table itself. It means that SQL Server does not gate the changes with a database role; -
@supports_net_changes = 0is enough. Gluesync reads every change, not the net result per key, so you do not need the extra index that net changes need.
Do not pass @capture_instance. Gluesync reads the capture instance that has the name <schema>_<table>, here dbo_orders. A capture instance with another name is ignored.
Check the result:
SELECT name, is_cdc_enabled FROM sys.databases WHERE name = 'shop';
SELECT name, is_tracked_by_cdc FROM sys.tables;
SELECT capture_instance FROM cdc.change_tables;
SELECT j.name, j.enabled FROM msdb.dbo.sysjobs AS j;
The change tables live in the schema cdc of the source database, for example cdc.dbo_orders_CT. The Object browser of the Control Plane lists them, with dbo.systranschemas. Do not add them as entities.
|
SQL
List the settings of the two jobs:
USE shop;
EXEC sys.sp_cdc_help_jobs;
The retention is the one setting you must decide. The cleanup job deletes the changes that are older than the retention, whether or not Gluesync has read them. Choose a value longer than the longest time an entity can be paused, in error or stopped, for example a weekend plus a margin. The change tables grow with it, so size the disk of the database too.
-- 7 days, in minutes
EXEC sys.sp_cdc_change_job @job_type = N'cleanup', @retention = 10080;
Changes to the capture job, for example @pollinginterval or @maxtrans, apply when you stop and start the job:
EXEC sys.sp_cdc_change_job @job_type = N'capture', @pollinginterval = 2;
EXEC sys.sp_cdc_stop_job N'capture';
EXEC sys.sp_cdc_start_job N'capture';
| The Source change retention in hours property that the agent lists applies to the Change Tracking agent only. For CDC, the retention is the cleanup job. |
SQL
Gluesync reads the table for the snapshot and the change tables for CDC. In the test, the least privilege that worked is db_datareader on the source database. Replace the password:
CREATE LOGIN gluesync WITH PASSWORD = '<a strong password>';
USE shop;
CREATE USER gluesync FOR LOGIN gluesync;
ALTER ROLE db_datareader ADD MEMBER gluesync;
With this user, the connection test passed, the snapshot copied 100,000 rows, and an insert, an update and a delete reached the target. The tables had @role_name = NULL. You enabled CDC yourself in step 2, so Gluesync needs no more.
Give more only when you want Gluesync to enable CDC for you:
| Privilege | What it allows | When you need it |
|---|---|---|
|
Read the tables and the change tables, run the snapshot and CDC |
Always. This is the least privilege, when a DBA enabled CDC on the database and on the tables |
|
Run |
Only if you turn on automatic activation, step 9, and want Gluesync to enable CDC on the tables. Also needed if you use recursion protection: the agent creates a small side table and enables CDC on it |
|
Run |
Only if you want Gluesync to enable CDC on the database. The statement is refused to a |
To see the last rule yourself, connect as a db_owner that is not a sysadmin and run EXEC sys.sp_cdc_enable_db on a database without CDC.
|
When a table is gated with |
UI
-
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;
-
In 1. Setup source, open the MS SQL Server tile and choose MS SQL Server CDC agent. The other choice, MS SQL Server Change tracking agent, is a different agent;
-
Fill Base settings:
-
Host name / Address: the DNS name or address of the instance, as Core Hub sees it;
-
Port (leave 0 to use dynamic port discovery):
1433, or the port of your instance; -
Database name: the database with CDC enabled, here
shop; -
Username and Password: the user of step 4;
-
-
Turn on Enable TLS and Trust server certificate only if your instance requires encryption, see Enable HTTPS and TLS to databases;
-
Select Test and continue. The source appears with
<user>@<host>:<port>. Select Next.
-
In 2. Setup target(s), choose your target agent and fill its connection. For the MySQL target of this guide, enter the host,
3306, the database, the user and the password. Turn on Allow public key retrieval and Trust server certificate if your lab server uses the default self-signed certificate. With Trust server certificate off, the save failed withTrust store not found, please upload a trust store when using verify server certificate. Select Test and continue, then Next; -
Enter a Pipeline name and select Finish.
|
When you edit a source and the sign-in fails, the Control Plane still saves the new values. It shows |
UI
-
In the pipeline, select Entities. The Add entities page opens with the Object browser. Expand the schema
dboand tick the tableorders. The Field mapping dialog opens; -
Under Target, choose the Target Schema, then type
ordersin Target Table and choose Create table "orders". The dialog shows New table orders will be created on confirm.; -
Select Confirm. The Create table orders dialog shows the read-only SQL statement. Read it, then select Create table orders to run it on the target;
-
Back on the Add entities page, select Confirm and continue.
UI + SQL
-
On the row of the entity, select the play button. The Sync changes (CDC) dialog opens;
-
Under Start from keep Full copy, Recommended for the first start. It copies all the rows, then follows the changes. Where it stopped is not available before the first run, and Now skips what changed before the start;
-
Under Data already in the target keep Keep and merge, and under Speed keep Standard;
-
Select Start entity.
While the copy runs, read the row count on the target:
-- on the target
SELECT count(*) FROM demo.orders;
SQL
On the source, insert, update and delete one row each:
INSERT INTO dbo.orders VALUES (100001, 'customer-new', 'new', 19.90, SYSDATETIME());
UPDATE dbo.orders SET status = 'paid', amount = 25.00 WHERE id = 1;
DELETE FROM dbo.orders WHERE id = 2;
After a few seconds, read the three keys on the target:
SELECT id, customer, status, amount FROM demo.orders WHERE id IN (1, 2, 100001) ORDER BY id;
SELECT count(*) FROM demo.orders;
To see what the capture job does, look at the log scan sessions of SQL Server. error_count stays 0 when the capture works:
SELECT TOP 3 session_id, start_time, end_time, tran_count, latency, error_count
FROM sys.dm_cdc_log_scan_sessions ORDER BY session_id DESC;
SELECT * FROM sys.dm_cdc_errors;
If the capture job is stopped, changes wait in the transaction log and nothing reaches the target. In the test, a row written while the job was stopped (EXEC sys.sp_cdc_stop_job N’capture') was not in the change table and not on the target. After EXEC sys.sp_cdc_start_job N’capture' the row appeared on the target within seconds. Start the job again if you see no changes and the entity is healthy.
UI + SQL
Gluesync can run sys.sp_cdc_enable_table for you when you add an entity. The option is off by default, so an upgrade never makes a source with a small user refuse to connect.
First, see what happens when it is off. Create a second table and add it as an entity the same way as in step 6:
CREATE TABLE dbo.customers (id int NOT NULL PRIMARY KEY, name varchar(50), country char(2));
INSERT INTO dbo.customers VALUES (1, 'Acme', 'IT'), (2, 'Globex', 'US'), (3, 'Initech', 'DE');
Start the new entity with Full copy.
To let Gluesync do it, turn the option on:
-
Open the pipeline, select the gear icon of the pipeline and Edit source;
-
Fill Password again, open Advanced settings and select Add property;
-
Under Key choose Enable automatic Change Data Capture / Change Tracking activation and switch Value on;
-
Select Test and continue.
Gluesync enables CDC on the tables of an entity when you add the entity, and when you save the source again with the option on. It never disables CDC, because another entity or another tool may read the same table. With the option on, but a user that has only db_datareader, saving the source gives an error notification:
With the user in db_owner, a new entity for dbo.customers enabled CDC on the table, and is_tracked_by_cdc became 1. In the test, an entity that existed before the grant did not retry the activation when it was started again: delete the entity and add it again, or enable the table yourself. For the database, the user needs sysadmin, as shown in step 4. Most teams keep the option off and let the DBA run step 2.
SQL + UI
A capture instance has a fixed list of columns, set when CDC is enabled on the table. A column that you add later is not captured. In the test, after ALTER TABLE dbo.orders ADD note varchar(20) NULL, a new row with a note reached the target without error, with the other columns right and no note. CDC does not warn you.
To capture the new column, recreate the capture instance while nothing writes to the table, and map the column. The order matters:
-
Stop the writers of the table, or schedule the change in a quiet window. Wait until the entity has read every change, the Last rows and the Last trans. stop moving;
-
Pause the entity with its pause button and Pause entity;
-
Add the column to the target table, for example on MySQL
ALTER TABLE orders ADD COLUMN note varchar(20) NULL; -
On the source, drop and enable the capture instance again. Changes written between the two statements are lost, which is why you stopped the writers:
USE shop; EXEC sys.sp_cdc_disable_table @source_schema = N'dbo', @source_name = N'orders', @capture_instance = N'dbo_orders'; EXEC sys.sp_cdc_enable_table @source_schema = N'dbo', @source_name = N'orders', @role_name = NULL, @supports_net_changes = 0; SELECT column_name FROM cdc.captured_columns; -
On the row of the entity, select the three-dot menu and Edit mapping. The new column is listed on both sides, unticked. Tick
noteon the source and on the target, select Confirm, then Confirm and continue; -
Start the entity with Where it stopped.
note is listed, 5 of 6 columns are selected.For other changes, such as a new type, a key change or a dropped column, see Handle schema changes on a live replication.
UI + SQL
Pause the entity, change the source, and let the cleanup job delete the changes before you resume. In the test the cleanup job removed changes that were older than the retention, and Gluesync did not know:
-
Pause the entity;
-
Insert a row, wait, insert another row, and run the cleanup. The test used the procedure that the cleanup job calls, with a low-water mark that is later than the first row:
USE shop; INSERT INTO dbo.orders VALUES (100030, 'gone-x', 'new', 1, SYSDATETIME()); INSERT INTO dbo.orders VALUES (100031, 'kept-y', 'new', 1, SYSDATETIME()); DECLARE @lw binary(10) = (SELECT MAX(start_lsn) FROM cdc.lsn_time_mapping); EXEC sys.sp_cdc_cleanup_change_table @capture_instance = N'dbo_orders', @low_water_mark = @lw, @threshold = 5000; SELECT id FROM cdc.dbo_orders_CT; -
Start the entity with Where it stopped.
When a saved position is older than the oldest change that remains, Gluesync starts again from the oldest change. The rows in between are lost silently. To realign the target, start the entity again with Full copy and Keep and merge. In the test the missing row appeared and both counts were 100011. Prevent the gap with a retention that is longer than any pause, and watch the age of the oldest change:
SELECT MIN(tran_end_time) AS oldest_change FROM cdc.lsn_time_mapping;
UI
-
In the start dialog, under Speed select Faster or Custom to raise the number of readers and of writers. More lanes copy faster and put more load on both databases;
-
The agent reads the snapshot with
4parallel queries by default. The property Query parallelism of the source changes it, and Max Snapshot Concurrency (0 means no limitation) limits the snapshot, see SQL Server source advanced settings; -
For live changes, SQL Server decides the latency: the capture job polls the log every
pollingintervalseconds,5by default. A smaller value lowers the delay and adds a little load, see step 3; -
If the target supports bulk operations, select Custom and switch Writing to Bulk load. See Tune replication throughput and Bulk load.
Troubleshooting
| Symptom or message | Cause | Fix |
|---|---|---|
|
SQL Server Agent is off. CDC is enabled but no job runs, so the change tables stay empty. |
Turn on the Agent as in step 1, restart SQL Server, and check |
|
|
A DBA with |
|
Automatic activation is on and the user is not |
Run the statement from the notification as a DBA, or add the user to |
|
The table has no capture instance named |
Run |
|
The password is wrong, or the login is disabled or locked. |
Fix the password in Edit source and save again. The wrong value stays saved until you do. |
|
The login exists but has no user in the database, or the database name is wrong. |
Run |
Inserts, updates and deletes stop reaching the target, the entity is Active and shows no error |
The capture job is stopped, or the Agent is stopped. |
|
A new column does not reach the target |
The capture instance was created before the column existed. |
Recreate the capture instance and update the mapping, as in step 10. |
Rows written during a pause are missing after the resume, and no error appears |
The cleanup job deleted the changes before Gluesync read them. Gluesync resumed from the oldest change that was left. |
Start the entity with Full copy and Keep and merge. Raise the retention of the cleanup job, step 3. |
|
The server name stored in SQL Server does not match the real name, for example after a clone. |
|
|
SQL Server refuses to truncate a table that has CDC enabled. |
Delete the rows instead, or disable CDC on the table, truncate it, enable it again and start the entity with Full copy. |