mode:, and it decides what “agree” means.
Row count mode
COUNT(*) per side. Use it when the target is known to transform values — so a checksum would never match — but must not lose or duplicate rows. A drill-down still works, narrowing which key ranges have the wrong count.
Because nothing here reads a column, the two sides do not have to have the same columns. A target carrying columns the source has never heard of — surrogate keys, load timestamps, whole extra dimensions bolted on by the warehouse — is compared as it stands, and you do not have to list columns to make the two sides look alike. Only the key columns have to exist on both sides, so the drill-down can range over them.
Row checksum mode
columns: and exclude_columns: are how you keep pipeline bookkeeping — load timestamps, batch ids, surrogate keys generated per side — from being reported as a difference for the rest of time.
Each row’s hash runs over the compared columns in order, position by position, so both sides must present the same number of columns. A target that is wider than the source is the usual reason a run stops with source has N columns but target has M columns; name the columns you actually want compared, on both sides:
exclude_columns: on the wider side does the same job when it is easier to name what to drop than what to keep. If you only care that no rows went missing, mode: row_count compares the two sides with no column list at all.
full is an accepted legacy spelling of row_checksum. Suites round-tripped through synq-recon suite yaml come back written as row_checksum.Aggregate mode
Row-level comparison is the wrong instrument when the target is a summary of the source, when row identity differs between the two systems, or when you care about the numbers rather than the rows. Aggregate mode compares measures, grouped, with a tolerance.Measures
A measure is a column and one or more functions.function: takes a single value or a list, so the block above expands to two measures: SUM(amount) and COUNT(amount). Available functions are SUM, COUNT, AVG, MIN and MAX.
The group hierarchy
group_columns: is a hierarchy, evaluated outermost-first. The comparison groups by the first column; only groups that diverge are then broken down by the second, and so on. Groups that agree are never queried again.
With group_columns: [sale_date, store_id], a run over a discrepancy on one store on one day looks like this:
STORE-1’s numbers that are wrong. Two levels, and the finding is specific enough to act on.
Each measure appears as _measure_<function>_<column> — the alias the generated SQL gives it, which is also what you will find in -o json and in the audit log. Each divergent node is labelled with why it is there:
If you omit
group_columns, the hierarchy falls back to [key_column].
Thresholds
Floating-point arithmetic, rounding, and currency conversion all produce differences that are not errors. A threshold is what separates them from the ones that are.absolute: 0 means exact agreement.
Overrides get more specific from there — per grouping column, and per measure:
1
per_column[column].per_measure[measure]
The threshold for this measure at this level of the hierarchy.
2
per_measure[measure]
The threshold for this measure, at every level.
3
per_column[column]
The threshold for every measure at this level.
4
thresholds
The suite-wide default.
"FUNC(column)" — "SUM(revenue)", "COUNT(amount)" — and the quotes are needed because of the parentheses.
A run whose only divergences are inside their thresholds reports
within_threshold rather than mismatched. That is a distinct status you can act on separately — see --fail-on.Hash algorithms
row_checksum needs a hash function that both databases compute identically. The tool negotiates the best one available on both sides, so you normally do not think about it:
Set
hash_algorithm: explicitly (auto, md5, farm_fingerprint, xxhash64) only when you have a reason to pin it — for example, to keep checksums stable across a platform migration.
Next
How comparison works
The checksum formula, bisection, segmentation strategies and privacy levels.
Time windows and cutoffs
Aggregate mode on a lagging target needs a cutoff. Here is why.
Investigating results
Reading a drill tree, and following a finding to the rows.
Configuration reference
Every field of
aggregate: and thresholds:, generated.