Performance Tuning (ACE)
ACE parallelizes work across multiple processes, hashing blocks of rows and only drilling into blocks that differ. ACE performance on your system will depend on a number of tunable factors:
Hardware and host capacity impacts performance: Having more cores and memory improves processing time; ensure ACE’s CPU cap is consistent with your resources.
Table architecture can speed hashing: Tables with very wide rows and large JSON/BLOB/embedded columns slow hashing; design tables for efficiency, and ensure that VACUUM/ANALYZE functions are performed regularly.
Network distribution impacts diff distribution: Keep ACE close to your databases. Many small, scattered mismatches force more data movement across the network than a few concentrated ones.
Note
We recommend you experiment with the suggestions on this page on a non-production system.
If you notice a pronounced change in a node's performance:
- Review your ACE commands; experiment with the table-diff tuning flags such as
--block-size,--max-connections,--concurrency-factor, and--compare-unit-size. - Confirm that your table has been
ANALYZEd recently. - Use
--table-filter/-Fand--nodesto target hot ranges. - If you're using ACE on huge tables, use Merkle trees.
- After repairing differences, run
table-rerunto validate the results.
Command Options that Impact Performance
When invoking ACE commands, review the available command options; many of the options are designed to improve performance. For example:
-
--block-sizeControls the number of rows hashed per block. Larger blocks reduce round-trips but can increase memory usage or hash time. Defaults to100000, and respects the guardrails defined inace.yamlunless--override-block-size/-Bis set. -
--concurrency-factorCPU ratio that controls how many comparisons of the blocks that differ ACE runs at the same time, relative to available CPUs (0.0–4.0). For example,0.5on a 16-CPU machine runs 8 comparisons. Higher values find the differing rows faster on machines with spare CPU, but can create contention on smaller or busier hosts. It does not change the speed of hashing; for that, see--max-connections. Default is0.5. -
--compare-unit-sizeSets the minimum block size used when ACE recursively drills into mismatched blocks. Smaller values provide more granular comparisons at the cost of additional round-trips. Default is10000. -
--max-connectionsThe number of connections that ACE opens to each node to cut and hash a table. The default is 8, or the value from themax_connectionssection oface.yaml, which can also give a different value for each node. More connections hash faster but keep more backends busy on the server. If a server has fewer free connection slots, ACE works with the connections it gets and writes a warning. A value that you set is a strict limit for the whole task. Without one, comparing the blocks that differ can use more connections, up to the number that--concurrency-factorgives. See Connections per node. -
--override-block-size(-B) Override the safety limits defined inace.yaml. Use cautiously: extremely large blocks can lead toarray_aggmemory pressure. -
--table-filter(-F) Use a table filter clause to narrow the comparison scope for large tables. -
--nodes
Compare a subset (e.g.,n1,n2) of your cluster's nodes to reduce cross-node IO.
Improving Performance with Merkle Trees
Using a Merkle tree can improve performance when your tables are very large by storing statistics so you can avoid a full rescan between diff activities. To improve performance when using Merkle trees:
- Keep your statistics fresh (
ANALYZE) for accurate range estimation. - For huge tables(billion-row/terabyte), parallelize builds.
- Use
mtree listen(CDC) or rely on the pre-diff update thatmtree table-diffperforms automatically to keep your data fresh automatically. - Tune
--max-cpu-ratioon Merkle commands (mtree build,mtree table-diff,mtree update) to control worker parallelism per host. The ratio is a share of the CPUs on the machine running ACE, not the database server, and each worker keeps one backend busy on every node. Use--max-connections(ormtree.max_connectionsinace.yaml) for an absolute cap when ACE runs on a host with far more cores than the database servers. For these commands it is an upper bound of the pool of each node, not the number of connections that ACE opens, and0takes the number of workers from--max-cpu-ratio. Since one connection holds the tree's transaction,Nconnections meansN - 1hash workers onmtree updateandmtree table-diff;mtree buildalso runs cutting workers out of the same pool, so it needs at least 3. ACE logs the worker count it settles on at the start of each phase. - ACE sends
max_parallel_workers_per_gather = 0on every connection it opens by default, for the diff and repair commands as well as the Merkle ones, so its queries do not also fan out into Postgres parallel workers. Changepostgres.max_parallel_workers_per_gatherinace.yamlto allow them.
Note
Merkle tree initialization adds triggers; measure the impact of this addition on write-heavy workloads.