Build1 distinct publisher3 min readPublished
One walkthrough runs both of Sling's modes against a single MySQL table, and the polling mode finishes clean while five cancelled orders stay behind in DuckDB. That gap is a property of SELECT, not of scheduling.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Follow any of these and your For You feed starts watching them — no settings page required.
build
DuckDB is growing a server, and someone on your team will have to run it1 distinct publisher
build
Object or file storage: the wrong pick stays quiet until inode exhaustion1 distinct publisher
build
A foreign-key cycle where every column is NOT NULL cannot take its first row1 distinct publisher
build
The ICO fines what you cannot prove: Article 32 makes encryption and erasure an engineering liability1 distinct publisher
The DuckDB merge is where the blind spot becomes concrete. DuckDB has no native `MERGE`, so Sling applies each incremental batch as a `DELETE FROM target WHERE EXISTS (SELECT 1 FROM temp src WHERE src.order_id = tgt.order_id)`, then an `INSERT` [12]. Every run issues DELETE statements. They are scoped to the keys in the batch, and the batch is whatever the query on `update_key` returned [2]. Order ids 1 through 5 were removed at the source [9], so they appear in neither the temp table nor the result set [3]. The deletion machinery is present and working, but it is never told which rows to remove.
Add it up: 50,000 rows loaded [8], minus the 3 keys the merge deletes before reinserting them, plus the 2,003-row batch, gives 52,000 in DuckDB against 51,995 in MySQL [1]. Five rows of drift, 0.0096% of the table [2], with both runs logging execution succeeded [10]. No component is in an error state, which is exactly why this surfaces as the opening scene of the guide: a report comes back wrong and a cancelled row is still sitting in the warehouse [11].
The checkpoint makes recovery awkward too, because the bookmark is the destination: Sling reads `max(update_key)` from the target table [2]. There is no separate ledger of which primary keys the sync has ever seen, so the target cannot be diffed against its own history.
The first log line is a claim about someone else's hardware: 50,000 rows, "2 secs", 19,910 r/s [7]. Divide, and 50,000 at 19,910 r/s is 2.51 seconds [3], so the seconds field is rounded and the rate is the honest figure. It describes a six-column table with a BIGINT key and a timestamp cursor [8], written through a temp table and an `INSERT` [12]. For that number to transfer you would need comparable row width and a target whose write path is also a direct insert rather than a staged upload.
Adoption cost is where the two modes stop being interchangeable. Query-based incremental needs no special database privileges and runs against anything Sling can query [4]. Log-based capture reads the transaction log directly, which is where deletes live alongside inserts and updates [5], and the price the guide names is more setup and a higher privilege level [4]. The mode that captures deletes correctly is also the one that costs more to run.
The guide's own framing is that query-based is enough for some workloads [1], and I agree, with one test. Whether the source application ever physically deletes a row is the test that matters. If it does, polling an update key is a correctness assumption made on behalf of every dashboard downstream, and the person who discovers it will not be the one who built it.
Ranked by verification strength, evidence, and original report placement.
The guide is published as "CDC with Sling: When Log-Based Works and When Query-Based Is Enough", framing query-based replication as sufficient for some workloads and log-based CDC as necessary for others.
Query-based incremental in Sling asks the source for rows where update_key is greater than the largest value already present; the target table is the bookmark, and Sling reads max(update_key) from the target before pulling newer rows.
A SELECT returns rows that exist, so a row deleted at the source is not in the result set and the target never hears about it; the blind spot is built into the design.
Query-based incremental needs no special database privileges and runs against anything Sling can query; log-based CDC costs more setup and a higher privilege level.
Log-based CDC does not query the table after the first load; it reads the transaction log (MySQL binary log, Postgres write-ahead log, SQL Server change tables), where deletes sit next to inserts and updates, so CDC applies them too.
In Sling's open-source core, change-capture sits next to incremental and full-refresh as a mode, and the mode and its option struct are readable in the public code (config.go, CDCOptions).
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 31, 2026
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Checkable arithmetic, a single witness
The central claim needs no witness at all: a SELECT cannot return a row that is no longer there, and the delete-then-insert Sling issues against DuckDB only matches keys in the arriving batch. Those two facts produce the five ghosts on their own, and the counts reconcile exactly — 50,000 plus 2,000 is the 52,000 in the target, five long of the source's 51,995. What holds the score down is provenance: one post, one synthetic table, log lines quoted rather than reproduced, and the text breaking off just as the change-capture run starts its snapshot.
No pipeline here but the author's
There is no adopter in this story. The orders table was created for the demonstration, populated to a round 50,000, and mutated on cue; no team, warehouse or production sync appears, and the throughput numbers describe one bench run rather than usage. The tier requirement tells us what Sling sells, not who has bought it.
Dramatised, but honestly
Five rows out of 51,995 is under a hundredth of a percent, and the write-up is scrupulous about blame — query-based incremental "did nothing wrong". What tilts this positive is the shape of the demonstration rather than any exaggerated sentence: the free mode is run all the way to a visible failure, while the mode that fixes it is configured and then left mid-snapshot. The assurance that CDC closes the gap rests on how transaction logs work, not on anything shown landing in DuckDB.
The free mode fails, the fix is paid
This is the vendor's own guide, and it does what open-core guides do: the mode that breaks is the one that costs nothing, and the mode that repairs it needs a CLI Pro Max token or an Advanced Platform plan. Two things partly offset that — the licensing line is stated flatly in the middle of the piece instead of buried at the end, and the pointer into config.go and CDCOptions is something a sceptic can go read. But the failure, the numbers, the log output and the judgement about which workloads "need" CDC all come from the party selling CDC.
Firm on mechanism, thin around it
Polling replication leaving deleted rows behind is the kind of claim you can talk yourself into from first principles in under a minute, and everything numeric in this walkthrough is self-consistent down to the rounded "2 secs". Against that: a single publisher, an author with something to sell, no reproduction by anyone else, and a truncated second half where the promised half of the comparison should be.