Snowflake MigrationClickHouse Workshops

05 Benchmark dan cutover

Bangun ulang dashboard di ClickHouse, benchmark ketujuh query terhadap kedua engine, cutover producer, verifikasi paritas, dan bongkar semuanya.

Titik awal

Modul 04 selesai: lapisan analytics sudah terisi dan teruji. analytics.fact_trips menyimpan kurang lebih 50 juta baris; analytics.dim_taxi_zones, analytics.dim_payment_type, analytics.dim_vendor, dan analytics.dim_date terisi penuh; dbt test lulus dari ujung ke ujung; dan analytics.taxi_zones_dict hidup serta mengembalikan borough melalui dictGet(). analytics.agg_hourly_zone_trips masih kosong — sesuai desain, bukan karena cacat — dan tetap demikian sampai cutover di modul ini. Producer Snowflake masih berjalan, dan jeda antara Snowflake dan ClickHouse masih terbuka. Siapkan sekitar 45 menit.

Mengapa

Setiap modul sampai titik ini adalah persiapan. Modul 03 membuktikan ClickHouse bisa menampung 50 juta baris; modul 04 membuktikan pipeline dbt berjalan di atasnya. Tak satu pun dari keduanya, sendirian, adalah sesuatu yang akan disetujui seorang partner sebagai "termigrasi" — untuk itu butuh dua hal lagi: sebuah angka, dan sebuah cutover.

Angkanya adalah benchmark di Langkah 2: ketujuh query yang sama dari rencana migrasi Anda, dijalankan beruntun terhadap Snowflake dan terhadap ClickHouse, dengan median dari tiga eksekusi masing-masing. Itulah yang mengubah "ClickHouse seharusnya lebih cepat" menjadi percepatan spesifik yang bisa dipertahankan dan bisa diletakkan seorang partner di depan pemangku kepentingannya sendiri.

Cutover di Langkah 3 adalah separuh lainnya. Setiap modul sejauh ini menjaga kedua sistem berjalan berdampingan, Snowflake sebagai sistem pencatatan dan ClickHouse menyusul di belakangnya. Migrasi yang tidak pernah benar-benar memindahkan jalur tulis adalah penyalinan, bukan migrasi. Langkah 3 menghentikan producer Snowflake, menutup jeda yang dijaga terbuka oleh penulisan terus-menerusnya sejak modul 01, dan mulai menulis perjalanan baru ke ClickHouse — momen ClickHouse menjadi sistem pencatatan.

Ini juga modul learner terakhir sebelum asesmen tertulis di modul 06, yang bersifat open-book terhadap apa pun yang Anda hasilkan di sini. Dashboard, CSV benchmark, dan pemeriksaan paritas semuanya harus nyata sebelum Anda membongkar apa pun di Langkah 5.

Konsep — di balik layar

Lapisan BI, secara mekanis. bash superset/add_clickhouse_connection.sh memanggil REST API Superset secara langsung di Langkah 1 — tidak ada klik manual di UI Superset. Ia mendaftarkan koneksi ClickHouse, lalu mengimpor ekspor dashboard yang di-commit, menambahkan empat dashboard ClickHouse di samping tiga dashboard Snowflake yang dibangun modul 01 (total tujuh):

DashboardMencerminkanYang didemonstrasikan
CH — Operations Command CenterSnowflake Dashboard 1Data fact_trips live (pasca-cutover); KPI yang sama, query lebih cepat
CH — Executive Weekly ReportSnowflake Dashboard 2QUALIFY ditulis ulang sebagai subquery ROW_NUMBER()
CH — Driver & Quality AnalyticsSnowflake Dashboard 3JSONExtractString menggantikan LATERAL FLATTEN milik Snowflake
CH — Capabilities Showcase(baru — tanpa padanan Snowflake)Fungsi aproksimasi, join dictionary, klausa SAMPLE

Apa yang diuji ketujuh query benchmark. Langkah 2 menjalankan ketujuh query yang sama dari rencana migrasi Anda terhadap kedua engine dan membandingkan waktu jam dinding. Masing-masing menyasar celah dialek atau fitur engine spesifik dari rencana modul 02:

QueryMenguji
Q1Pendapatan per jam menurut borough
Q2Rata-rata jarak bergulir 7 hari
Q310 perjalanan teratas — QUALIFY Snowflake vs subquery ROW_NUMBER() ClickHouse
Q4Rating pengemudi — LATERAL FLATTEN Snowflake vs JSONExtractString ClickHouse
Q5Harga lonjakan — VARIANT Snowflake vs String + JSONExtract* ClickHouse
Q6Agregasi per jam — MERGE Snowflake vs ReplacingMergeTree ClickHouse
Q7Kesegaran data CDC / live

Jeda cutover. Producer Snowflake sudah menulis kurang lebih 60 perjalanan per menit sejak modul 01, dan belum pernah berhenti. Skrip migrasi modul 03 menangkap TRIPS_RAW seperti keadaannya saat skrip itu berjalan, dan modul 04 membangun pipeline dbt di atas snapshot tersebut. Setiap perjalanan yang ditulis ke Snowflake setelah batch terakhir skrip migrasi hanya ada di Snowflake — ekor ClickHouse hilang. Tahap penyusulan --resume di Langkah 3 menutup persis jeda itu: ia membaca max(pickup_at) yang sudah ada di ClickHouse dan menarik hanya baris yang ditulis setelahnya, jadi satu tahap yang menutup jeda yang terbuka sejak modul 01 memakan hitungan detik sampai menit, bukan 40-50 menit seperti migrasi massal awalnya. Lewati langkah itu dan tetap melakukan cutover, dan ClickHouse akan permanen kehilangan berapa pun jumlah perjalanan yang mendarat di dalam jeda tersebut — kegagalan paritas senyap yang dibangun Langkah 4 untuk ditangkap, tetapi hanya jika Langkah 3 berjalan sesuai urutan.

agg_hourly_zone_trips terisi di sini, dan hanya di sini. Ia kosong sejak modul 03 sesuai desain: filter inkrementalnya adalah WHERE pickup_at >= now() - INTERVAL 2 HOUR, yang hanya cocok dengan baris yang ditulis producer live, dan sampai modul ini satu-satunya producer yang menulis adalah milik Snowflake. Begitu Langkah 3 menjalankan producer ClickHouse, baris baru akhirnya mendarat di dalam jendela dua jam itu, dan tabel tersebut — beserta setiap chart dashboard yang ditopangnya — berhenti kosong untuk pertama kali dalam lab ini.

Langkah 1 — Tambahkan dashboard ClickHouse

Untuk pembangunan manual lengkap — membuat masing-masing dari 7 dataset, 18 chart, dan 4 dashboard langkah demi langkah di UI Superset — ikuti Superset di ClickHouse. Untuk melewati langkah manual dan mengimpor semuanya sekali jalan:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash superset/add_clickhouse_connection.sh

superset/dashboards/dashboard_export_*.zip yang di-commit punya host ClickHouse yang disamarkan menjadi your-instance.clickhouse.cloud. Jalan pintas di atas menambal URI itu dari .env sebelum mengimpor, jadi hal ini transparan — Anda tidak akan menyadari penyamarannya. Tetapi jika Anda malah mengimpor ZIP secara manual melalui UI Superset, koneksi database yang dibuatnya tidak akan tersambung — Anda harus menyunting koneksi itu sesudahnya agar menunjuk ke CLICKHOUSE_HOST dan kredensial Anda yang sebenarnya. Lihat Superset di ClickHouse untuk cara persisnya.

Verifikasi:

Buka http://localhost:8088 (admin / admin). Di bawah Dashboards, Anda seharusnya melihat total 7 — 3 dashboard Snowflake dan 4 yang berawalan CH —.

Langkah 2 — Jalankan benchmark

Jalankan ketujuh query beruntun terhadap Snowflake maupun ClickHouse dan bandingkan waktu jam dinding:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
./scripts/run_benchmark.sh

Output yang diharapkan:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  NYC Taxi Lab — Query Benchmark: Snowflake vs ClickHouse
  (median of 3 runs each)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Query                                   Snowflake     ClickHouse    Speedup
────────────────────────────────────────────────────────────────────────
Q1  Hourly revenue by borough           5.0s          0.7s          6x
Q2  Rolling 7-day avg distance          5.5s          0.8s          6x
Q3  Top 10 trips (QUALIFY→subquery)     5.0s          0.7s          6x
Q4  Driver ratings (JSON flatten)       5.4s          0.8s          6x
Q5  Surge pricing (VARIANT)             5.1s          0.7s          6x
Q6  Hourly aggregation (MERGE→RMT)      5.9s          0.8s          7x
Q7  CDC/live data freshness             7.9s          0.8s          9x
────────────────────────────────────────────────────────────────────────
Total                                   40.1s         5.6s          7x avg
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Angka-angka ini adalah satu eksekusi representatif, bukan jaminan — angka Anda sendiri akan bervariasi menurut ukuran warehouse, tier ClickHouse Cloud, dan apa pun yang berjalan terhadap kedua layanan pada saat itu.

Skrip menulis setiap eksekusi ke workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Simpan file itu di disk — ia adalah salah satu dari dua file yang dibutuhkan asesmen modul 06, jadi jangan hapus selama teardown di Langkah 5.

Langkah 3 — Cutover ke ClickHouse

Jeda migrasi. Producer Snowflake sudah berjalan sepanjang lab ini, menulis kurang lebih 60 perjalanan per menit. Skrip migrasi modul 03 menangkap TRIPS_RAW seperti keadaannya saat skrip itu berjalan — baris yang ditulis sejak itu hanya ada di Snowflake. Tutup jeda tersebut sebelum men-cutover jalur tulis.

Jalankan keempat langkah di bawah, secara berurutan — urutannya itulah yang menjaga kedua sistem tetap konsisten:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
source .venv/bin/activate

# Step 1: Stop the Snowflake producer (freeze the dataset)
docker stop nyc_taxi_producer

# Step 2: Catch up the delta — only migrates rows with pickup_at newer than
# what's already in ClickHouse. Runs in seconds to minutes, not the original
# 40-50 minutes, because only the gap rows move.
python scripts/02_migrate_trips.py --resume

# Step 3: Refresh the analytics tables with the newly migrated rows
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/dbt/nyc_taxi_dbt_ch"
dbt run
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"

# Step 4: Start the ClickHouse producer
source .env && source .clickhouse_state
./scripts/03_cutover.sh

Jangan lewati Langkah 2. --resume membaca max(pickup_at) yang sudah ada di ClickHouse dan menambahkan filter WHERE PICKUP_DATETIME > <watermark> ke query Snowflake, jadi ia hanya mentransfer baris yang ditulis selama dan setelah eksekusi migrasi awal modul 03 — baris yang belum pernah dilihat ClickHouse. Lakukan cutover tanpanya dan ClickHouse akan permanen kehilangan berapa pun jumlah perjalanan yang mendarat dalam jendela itu. Pemeriksaan paritas di Langkah 4 dibangun untuk menangkap persis hal itu, tetapi hanya jika langkah ini berjalan lebih dulu.

./scripts/03_cutover.sh meminta Type "cutover" to confirm, lalu mengulangi langkah 1-3 sebagai jaring pengamannya sendiri: ia menghentikan producer Snowflake lagi (tanpa efek jika Anda sudah melakukannya), menjalankan satu dbt run lagi, lalu membangun dan menjalankan producer ClickHouse (nyc_taxi_ch_producer). Tiga puluh detik setelah producer berjalan, ia memastikan baris baru mendarat di default.trips_raw dan menjalankan dbt sekali lagi — eksekusi yang akhirnya memberi agg_hourly_zone_trips baris pertamanya, menutup jeda yang sengaja dibiarkan terbuka oleh modul 04.

Verifikasi:

-- Most recent trip should be within the last 60 seconds
SELECT max(pickup_at) AS most_recent_trip FROM default.trips_raw;

-- Row count should be increasing — wait 60 seconds and run again
SELECT count() FROM default.trips_raw;

-- agg_hourly_zone_trips should now have rows for the first time in the lab
SELECT count() FROM analytics.agg_hourly_zone_trips;
docker ps | grep nyc_taxi_ch_producer   # should show running

Menjaga lapisan analytics tetap segar. fact_trips dan agg_hourly_zone_trips adalah model inkremental dbt — keduanya tidak menyegarkan diri sendiri. 03_cutover.sh menjalankan dbt run satu kali setelah memastikan producer hidup, tetapi dashboard akan menjadi basi seiring perjalanan baru menumpuk; jalankan ulang dbt run dari dbt/nyc_taxi_dbt_ch kapan pun Anda ingin angka terkini (di produksi Anda akan menjadwalkannya — cron, Airflow, dbt Cloud — tetapi on-demand sudah cukup untuk lab). analytics.mv_live_trip_feed, sebaliknya, adalah materialized view yang refreshable — dbt run modul 04 sudah membangunnya dengan engine = 'ReplacingMergeTree(refreshed_at)' — tetapi lab tidak pernah menyalakan interval refresh-nya: pernyataan MODIFY REFRESH EVERY 30 SECOND yang akan membuatnya menjalankan ulang dirinya sendiri hanya ada sebagai komentar di file model. Mengaktifkannya adalah satu pernyataan ALTER TABLE yang Anda jalankan sendiri; tanpa itu, mv_live_trip_feed hanya diperbarui satu kali saat dbt membangunnya.

Cutover balik, jika Anda perlu membatalkan langkah ini dan kembali ke producer Snowflake:

docker stop nyc_taxi_ch_producer
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake/superset"
docker-compose --env-file ../.env up -d producer

Langkah 4 — Verifikasi paritas

Sekarang setelah tahap penyusulan --resume berjalan dan producer ClickHouse aktif, kedua sistem seharusnya berada dalam paritas. Pastikan hal itu:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash scripts/01_verify_migration.sh

Output yang diharapkan:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Migration Parity Check
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✓ ClickHouse default.trips_raw: 50,008,250 rows

  ✓ Snowflake NYC_TAXI_DB.RAW.TRIPS_RAW: 50,008,250 rows
  ✓ Row count parity: PASS  (difference: 0 rows = 0.0000%)

  ✓ trip_metadata populated: 50,008,250 non-empty rows
  pickup_at range: 2022-03-30   2026-03-31

  ✓ ClickHouse has 50,008,250 rows — migration looks complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Jumlah baris Anda sendiri akan berbeda; yang penting adalah baris paritasnya. Producer Snowflake sudah dihentikan pada titik ini, jadi tidak ada baris baru yang mendarat di sana — jumlahnya seharusnya sama persis, atau berbeda beberapa baris saja jika ada batch yang masih dalam perjalanan selama tahap --resume, jauh di dalam ambang 0,01% yang diperiksa skrip.

Jika pemeriksaan paritas gagal (perbedaan lebih dari 0,01%), jeda belum tertutup sepenuhnya — jalankan tahap penyusulan lagi lalu periksa ulang:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
python scripts/02_migrate_trips.py --resume
bash scripts/01_verify_migration.sh

Langkah 5 — Bongkar semuanya

Sebelum membongkar apa pun, pastikan migrasi berada dalam kondisi akhir yang benar:

PemeriksaanPerintahDiharapkan
Paritas jumlah barisbash scripts/01_verify_migration.shKecocokan jumlah baris ≥ 99,9%
Tes dbtdbt test (dari dbt/nyc_taxi_dbt_ch)Semua tes lulus
Dashboard SupersetBuka http://localhost:80887 dashboard terlihat (3 SF + 4 CH)
Hasil benchmarkcat scripts/benchmark_results_<timestamp>.csvKetujuh query punya nilai percepatan
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash scripts/01_verify_migration.sh

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/dbt/nyc_taxi_dbt_ch"
dbt test

Begitu keempatnya lulus, dua file adalah segala yang dibutuhkan modul 06, dan keduanya bertahan melewati teardown: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md dan workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Modul 06 adalah asesmen tertulis open-book, kurang lebih 60 menit, dan tidak membutuhkan apa pun selain itu — tidak ada alasan menjaga layanan ClickHouse Cloud berbayar tetap berjalan selama ujian tertulis. Salin atau catat isi kedua file itu di tempat yang bisa Anda jangkau, lalu bongkar semuanya:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && ./teardown.sh

Ini memusnahkan layanan ClickHouse Cloud (melalui terraform destroy) dan kontainer producer perjalanan ClickHouse, jika cutover sudah dilakukan.

Sumber daya Snowflake dari Bagian 1 tidak dibongkar oleh skrip ini. Bongkar sisi Snowflake secara terpisah:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"
source .env && ./teardown.sh

Cara memverifikasi bahwa Anda sudah selesai

Sampai titik ini dalam modul, Anda seharusnya sudah memastikan, secara berurutan:

  • Pemeriksaan paritas lulus — 01_verify_migration.sh di Langkah 4 melaporkan PASS dengan perbedaan jumlah baris di bawah 0,01%.
  • CSV benchmark ada di disk — Langkah 2 menulis benchmark_results_<timestamp>.csv dengan ketujuh query membawa nilai percepatan, dan Anda menyimpannya sebelum teardown di Langkah 5.
  • 7 dashboard hadir — pemeriksaan Superset di Langkah 1 menampilkan 3 dashboard Snowflake dan 4 dashboard CH — berdampingan.
  • Producer ClickHouse menulis — blok verifikasi di Langkah 3 menunjukkan default.trips_raw bertambah baris dan nyc_taxi_ch_producer berjalan, sebelum Langkah 5 menghentikannya untuk teardown.

Jika salah satu dari hal itu tidak terpenuhi pada saatnya, kembalilah ke langkah yang bersangkutan alih-alih menjalankan ulang pemeriksaan ini sekarang — Langkah 5 sudah memusnahkan layanan ClickHouse Cloud dan, jika cutover terjadi, kontainer producer beserta layanan itu.

Kondisi akhir

Migrasi selesai dan terukur: 50 juta baris dipindahkan dari Snowflake ke ClickHouse dan diverifikasi berada dalam paritas, tujuh query di-benchmark berhadapan langsung dengan ClickHouse lebih cepat pada setiap query, lapisan BI dibangun ulang dengan 4 dashboard ClickHouse di samping 3 dashboard Snowflake yang asli, dan jalur tulis di-cutover dari Snowflake ke ClickHouse untuk selamanya. Kedua lingkungan cloud sudah dibongkar — tidak ada layanan ClickHouse Cloud, tidak ada kontainer producer ClickHouse, dan, setelah teardown Bagian 1 juga dijalankan, tidak ada warehouse Snowflake juga.

Dua file bertahan melewati teardown dan merupakan segala yang dibutuhkan modul 06: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md dan workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Modul 06 adalah asesmen tertulis open-book — bawa kedua file itu dan tidak ada yang lain.

Di halaman ini

Track your progress?

Optional. We email a link to confirm your address; progress records once you open it.

Please use your work email address, not a personal one.

Progress tracking also requires accepting the current Terms of Service in Privacy settings.

ID