Add replicated citizenlab asnmeta updaters - #190
Conversation
This avoids potential inconsistency with offline replicas, and uses a temporary table to store the new data before swapping the data cluster wide.
this harness verifies that a clustered clickhouse deployment applies the citizenlab updates correctly
this follows the same pattern as the replicated citizenlab table
this requires all replicas to be updated together rather than allow an offline replica to catch up asynchronously from the replication log.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #190 +/- ##
==========================================
- Coverage 83.07% 81.99% -1.08%
==========================================
Files 91 93 +2
Lines 6411 6560 +149
==========================================
+ Hits 5326 5379 +53
- Misses 1085 1181 +96
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
{{database}} ensures that the test databases don't use the same zk path
{{shard}} is re-added to zk paths as it's the canonical schema.sql
definition and exists in zk even if on data1 the table was recreated
locally by the updater.
alter_sync = 2 fails query if all replicas are not online
fixes tests so expected commands match these changes
Migration: citizenlab and asnmeta to one replicated table on all nodesTarget state: on data1, data2 and data3:
These are the paths in the merged updaters. There is no The new paths are the parents of the old State on 2026-09-29:
Every 0. Pause the updaters
Keep them paused until step 10. The old updaters would undo steps 2–8: the next weekly run is 2026-10-05 00:00 UTC, and citizenlab runs every 30 minutes. 1. Preflight checks
SELECT hostName(), macro, substitution
FROM clusterAllReplicas('oonidata_cluster', system.macros)
ORDER BY 1, 2;
SELECT hostName() AS host, name, engine, total_rows
FROM clusterAllReplicas('oonidata_cluster', system.tables)
WHERE database = 'ooni'
AND name IN ('citizenlab', 'citizenlab_flip', 'asnmeta', 'asnmeta_tmp')
ORDER BY name, host;
SELECT hostName(), `table`, zookeeper_path
FROM clusterAllReplicas('oonidata_cluster', system.replicas)
WHERE database = 'ooni' AND `table` IN ('citizenlab', 'citizenlab_flip', 'asnmeta')
ORDER BY `table`, 1;
2. citizenlab: replace each node's replicated table with a local copy (on each node, not ON CLUSTER)
CREATE TABLE ooni.citizenlab_local (
`domain` String, `url` String, `cc` FixedString(32), `category_code` String
) ENGINE = ReplacingMergeTree
ORDER BY (domain, url, cc, category_code)
SETTINGS index_granularity = 4;
INSERT INTO ooni.citizenlab_local SELECT * FROM ooni.citizenlab;
EXCHANGE TABLES ooni.citizenlab AND ooni.citizenlab_local;After this, 3. citizenlab: check the copies and drop the old replicated tables
SELECT hostName(), count(), groupBitXor(cityHash64(domain, url, cc, category_code)) AS h
FROM clusterAllReplicas('oonidata_cluster', ooni.citizenlab)
GROUP BY 1;
SELECT hostName(), engine
FROM clusterAllReplicas('oonidata_cluster', system.tables)
WHERE database = 'ooni' AND name = 'citizenlab';
DROP TABLE ooni.citizenlab_local ON CLUSTER oonidata_cluster SYNC;
DROP TABLE IF EXISTS ooni.citizenlab_flip ON CLUSTER oonidata_cluster SYNC;4. asnmeta: clear data3's empty replicated copy (on data3 only)
DROP TABLE ooni.asnmeta SYNC;
DROP TABLE IF EXISTS ooni.asnmeta_tmp SYNC;
CREATE TABLE ooni.asnmeta (
asn UInt32, org_name String, cc String, changed Date, aut_name String, source String
) ENGINE = MergeTree ORDER BY (asn, changed);
5. Clean up the old Keeper paths
SELECT hostName(), `table`, zookeeper_path
FROM clusterAllReplicas('oonidata_cluster', system.replicas)
WHERE database = 'ooni';(If other replicated tables exist in
SELECT name, numChildren
FROM system.zookeeper
WHERE path = '/clickhouse/oonidata_cluster/tables/ooni'
AND name IN ('citizenlab', 'citizenlab_flip', 'asnmeta');Dropping the last replica removes
6. Create the replicated tables at the new pathsCREATE TABLE ooni.citizenlab_v2 ON CLUSTER oonidata_cluster (
`domain` String, `url` String, `cc` FixedString(32), `category_code` String
) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/{cluster}/tables/{database}/citizenlab', '{replica}')
ORDER BY (domain, url, cc, category_code)
SETTINGS index_granularity = 4;
CREATE TABLE ooni.asnmeta_v2 ON CLUSTER oonidata_cluster (
asn UInt32, org_name String, cc String, changed Date, aut_name String, source String
) ENGINE = ReplicatedMergeTree('/clickhouse/{cluster}/tables/{database}/asnmeta', '{replica}')
ORDER BY (asn, changed);The Keeper path contains the literal 7. Copy the data (on data1)
INSERT INTO ooni.citizenlab_v2 SELECT * FROM ooni.citizenlab;
INSERT INTO ooni.asnmeta_v2 SELECT * FROM ooni.asnmeta;8. Verify replication, swap in the new tables and clean up
SELECT hostName(), count(), groupBitXor(cityHash64(domain, url, cc, category_code)) AS h
FROM clusterAllReplicas('oonidata_cluster', ooni.citizenlab_v2)
GROUP BY 1;
SELECT hostName(), count()
FROM clusterAllReplicas('oonidata_cluster', ooni.asnmeta_v2)
GROUP BY 1;
EXCHANGE TABLES ooni.citizenlab AND ooni.citizenlab_v2 ON CLUSTER oonidata_cluster;
EXCHANGE TABLES ooni.asnmeta AND ooni.asnmeta_v2 ON CLUSTER oonidata_cluster;
DROP TABLE ooni.citizenlab_v2 ON CLUSTER oonidata_cluster SYNC;
DROP TABLE ooni.asnmeta_v2 ON CLUSTER oonidata_cluster SYNC;
DROP TABLE IF EXISTS ooni.asnmeta_tmp ON CLUSTER oonidata_cluster SYNC;9. Verify both tables
SELECT hostName(), `table`, zookeeper_path
FROM clusterAllReplicas('oonidata_cluster', system.replicas)
WHERE database = 'ooni' AND `table` IN ('citizenlab', 'citizenlab_flip', 'asnmeta')
ORDER BY `table`, 1;
SELECT hostName(), count(), max(changed)
FROM clusterAllReplicas('oonidata_cluster', ooni.asnmeta)
GROUP BY 1;10. Deploy the new updaters
11. Load balancer
|
|
drop the {shard} key for citizenlab and asnmeta; reduce the verbosity of comments to the salient points |
Combined #185 and #186 updaters (citizenlab and asnmeta tables) and shares the same testing framework used by both; applies the requested changes by @hellais from #186