Replicate benchmark · SQL Server → Kafka · May 27, 2026

SQL Server → Kafka: 374K changes/sec CDC extraction

374,000 changes/sec sustained — plus an 811M-change, zero-violation one-hour run

SQL Server logo Kafka logo

Wirekite’s SQL Server change extractor captures committed changes from the transaction log and streams them into a Kafka/Redpanda queue. We benchmarked how fast that capture path runs on a single host. The headline: 374,000 changes per second sustained — backed by a separate one-hour endurance run that decoded 811 million changes with zero ordering violations and zero page splits.

The result

Two runs on the same rig, at different settings:

  • An uncapped run reached the extraction ceiling — a sustained 374,000 changes/sec.
  • A rate-capped one-hour run proved endurance — 811 million changes at a steady 226,000 changes/sec, holding from minute 10 to the end with no degradation and zero errors.
metricvalue
Peak sustained extraction374,000 changes/sec
One-hour endurance run811,000,000 changes @ 226,000 changes/sec
LSN ordering violations0
Page splits0
Queue publish errors0
Records lost or corrupted0

The workload

A pure-UPDATE workload is the hardest case for SQL Server CDC: every change is a logged modification, and CDC adds a before/after image to the log for every tracked row — there’s no cheap append work to coast on.

Five identical wide tables, 5 million rows each, every operation an UPDATE:

CREATE TABLE dbo.firenibble_N (
    id BIGINT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    int_col_1 INT,
    int_col_2 INT,
    int_col_3 INT,
    int_col_4 INT,
    varchar_col_1 VARCHAR(100),
    varchar_col_2 VARCHAR(100),
    varchar_col_3 VARCHAR(100),
    varchar_col_4 VARCHAR(100),
    date_col_1 DATE,
    date_col_2 DATE
) WITH (FILLFACTOR = 80);

FILLFACTOR = 80 leaves free space on every page so in-place integer UPDATEs never trigger page splits.

parametervalue
Operation mix100% UPDATE
Columns touched per update50% (integer columns)
Concurrent writers25 (endurance run) / 50 (ceiling run)
Rate cap250,000 changes/sec (endurance) / none (ceiling)
Durationone hour (endurance run)

The hardware

HostGCP c4-standard-96
vCPUs96
RAM354 GB
NUMA nodes8
OSUbuntu 22.04 LTS
SQL Server2022 RTM-CU24-GDR (16.0.4250.1), Developer Edition

Disk layout

The transaction log gets its own dedicated NVMe volume. SQL Server’s log writer serializes every commit on a synchronous flush, so log-volume latency directly sets the commit ceiling — sharing it with data-file I/O would cap throughput as the commit rate climbs.

volumedevicerole
dataNVMe, 256 GB8 × 16 GB data files (/mnt/mssql/data)
logNVMe, 256 GB (dedicated)single 50 GB pre-sized log file (/mnt/mssql/log)
tempdbshares the data volume8 × 1 GB files

SQL Server configuration

Instance (sp_configure)

settingvalue
max server memoryphysical minus ~12 GB OS reserve
min server memorymatched to max
max degree of parallelism8
cost threshold for parallelism50
optimize for ad hoc workloads1

Database (ALTER DATABASE)

settingvalue
Recovery modelFULL (required by CDC)
Read Committed SnapshotON
Change Data CaptureON
Query StoreOFF
Delayed DurabilityDISABLED (CDC blocks it)
Target recovery time60 s
vm.swappiness                = 1
vm.dirty_background_ratio    = 3
vm.dirty_ratio               = 80
vm.dirty_expire_centisecs    = 500
vm.dirty_writeback_centisecs = 100
vm.max_map_count             = 1600000
kernel.numa_balancing        = 0
Transparent Huge Pages       = always

The one-hour endurance run

811 million changes decoded over the hour at a steady 226,000 changes/sec. Throughput reached steady state by minute 10 and held it — no degradation across the full hour:

segmentdurationchangesrate
1323 s65.4 M202 K/s
2513 s123.6 M241 K/s
3512 s118.2 M231 K/s
4521 s115.7 M222 K/s
5514 s117.1 M228 K/s
6505 s111.9 M222 K/s
7508 s116.7 M230 K/s
overall56 min768.7 M226 K/s

Quality held perfectly the entire time:

metricvalue
Internal page splits0
LSN ordering violations0
Queue publish errors0
Records lost / corrupted0

And Wirekite keeps the transaction log bounded as it captures — across the whole hour the log oscillated between roughly 1.5 GB and 4 GB instead of growing unbounded, so the log volume never ran away.

What gates the throughput

The binding constraint is SQL Server’s own commit path — the synchronous log writer (the WRITELOG wait), made heavier because CDC forbids delayed durability. The Wirekite extractor was never the bottleneck: at every rate tested it kept pace with the source with no growing backlog.

It’s an honest number, too. SQL Server CDC adds supplemental before/after-image logging for every tracked UPDATE — roughly doubling the log bytes per change. Wirekite requires CDC, so that overhead is already baked into the rates above.

What this means

374,000 changes per second is far more than most SQL Server sources will ever emit — a busy OLTP database committing 50,000 changes/sec long-run still has roughly 7× headroom before capture would begin to lag. And the one-hour, 811-million-change run shows the capture path holds its rate with zero ordering violations and a transaction log that stays bounded — the two failure modes that actually break CDC pipelines in production.

Want a benchmark for your source × target?

Tell us the pair and the workload. We'll publish the run.