05 Benchmark e virada
Reconstrua os dashboards no ClickHouse, compare as sete consultas nos dois mecanismos, faça a virada do produtor, verifique a paridade e desmonte os ambientes.
Ponto de partida
Módulo 04 concluído: a camada de analytics está preenchida e testada.
analytics.fact_trips contém cerca de 50 milhões de linhas;
analytics.dim_taxi_zones, analytics.dim_payment_type, analytics.dim_vendor e
analytics.dim_date estão totalmente carregadas; dbt test passa de ponta a ponta; e
analytics.taxi_zones_dict está ativo e retorna boroughs por meio de dictGet().
analytics.agg_hourly_zone_trips continua vazia — por decisão de projeto, não por
defeito — e permanece assim até a virada deste módulo. O produtor do Snowflake continua
em execução, e a lacuna entre o Snowflake e o ClickHouse permanece aberta. Reserve cerca
de 45 minutos.
Por quê
Todos os módulos até aqui foram uma preparação. O módulo 03 provou que o ClickHouse consegue armazenar 50 milhões de linhas; o módulo 04 provou que o pipeline dbt funciona nele. Isoladamente, nenhum dos dois bastaria para um parceiro aprovar a migração: ainda são necessários um número e uma virada.
O número vem do benchmark da Etapa 2: as mesmas sete consultas do seu plano de migração, executadas em sequência no Snowflake e no ClickHouse, com a mediana de três execuções em cada um. É isso que transforma “o ClickHouse deve ser mais rápido” em um ganho de velocidade específico e defensável, que o parceiro pode apresentar às próprias partes interessadas.
A virada da Etapa 3 é a outra metade. Até agora, todos os módulos mantiveram os dois sistemas em execução lado a lado, com o Snowflake como sistema de registro e o ClickHouse recuperando o atraso. Uma migração que nunca transfere de fato o caminho de gravação é uma cópia, não uma migração. A Etapa 3 interrompe o produtor do Snowflake, fecha a lacuna que suas gravações contínuas mantêm aberta desde o módulo 01 e passa a gravar as novas corridas no ClickHouse — o momento em que o ClickHouse se torna o sistema de registro.
Este também é o último módulo do participante antes da avaliação escrita do módulo 06, que permite a consulta de tudo o que você produzir aqui. Os dashboards, o CSV do benchmark e a verificação de paridade precisam ser reais antes de você desmontar os ambientes na Etapa 5.
Conceitos — por baixo dos panos
A camada de BI, na prática. Na Etapa 1,
bash superset/add_clickhouse_connection.sh chama diretamente a API REST do Superset —
não é necessário percorrer manualmente a interface. O script registra a conexão com o
ClickHouse e importa a exportação de dashboard incluída no repositório, adicionando
quatro dashboards do ClickHouse aos três dashboards do Snowflake criados no módulo 01
(sete no total):
| Dashboard | Espelha | O que demonstra |
|---|---|---|
| CH — Operations Command Center | Snowflake Dashboard 1 | Dados ativos de fact_trips (após a virada); mesmos KPIs, consultas mais rápidas |
| CH — Executive Weekly Report | Snowflake Dashboard 2 | QUALIFY reescrito como uma subconsulta com ROW_NUMBER() |
| CH — Driver & Quality Analytics | Snowflake Dashboard 3 | JSONExtractString no lugar de LATERAL FLATTEN do Snowflake |
| CH — Capabilities Showcase | (novo — sem equivalente no Snowflake) | Funções aproximadas, junções com dicionário e a cláusula SAMPLE |
O que as sete consultas de benchmark exercitam. A Etapa 2 executa as mesmas sete consultas do seu plano de migração nos dois mecanismos e compara o tempo decorrido. Cada uma aborda uma diferença específica de dialeto ou um recurso do mecanismo registrado no plano do módulo 02:
| Consulta | O que exercita |
|---|---|
| Q1 | Receita por hora e borough |
| Q2 | Média móvel da distância em 7 dias |
| Q3 | 10 principais corridas — QUALIFY do Snowflake versus uma subconsulta com ROW_NUMBER() no ClickHouse |
| Q4 | Avaliações de motoristas — LATERAL FLATTEN do Snowflake versus JSONExtractString do ClickHouse |
| Q5 | Preço dinâmico — VARIANT do Snowflake versus String + JSONExtract* do ClickHouse |
| Q6 | Agregação por hora — MERGE do Snowflake versus ReplacingMergeTree do ClickHouse |
| Q7 | CDC / atualização dos dados ativos |
A lacuna da virada. O produtor do Snowflake grava cerca de 60 corridas por minuto
desde o módulo 01 e nunca foi interrompido. O script de migração do módulo 03 capturou
TRIPS_RAW no estado em que ela se encontrava quando o script foi executado, e o módulo
04 criou o pipeline dbt sobre esse snapshot. Todas as corridas gravadas no Snowflake
desde o último lote do script de migração existem apenas no Snowflake — o trecho final
está ausente no ClickHouse. A passagem de recuperação com --resume da Etapa 3 fecha
exatamente essa lacuna: ela lê o max(pickup_at) já presente no ClickHouse e recupera
apenas as linhas gravadas depois desse ponto. Assim, uma passagem que fecha uma lacuna
aberta desde o módulo 01 leva de segundos a minutos, não os 40 a 50 minutos da migração
em massa original. Se você ignorá-la e fizer a virada mesmo assim, o ClickHouse perderá
permanentemente todas as corridas inseridas durante a lacuna — uma falha silenciosa de
paridade que a Etapa 4 foi feita para detectar, mas somente se a Etapa 3 for executada na
ordem correta.
agg_hourly_zone_trips é preenchida aqui, e somente aqui. Ela está vazia desde o
módulo 03 por decisão de projeto: seu filtro incremental é
WHERE pickup_at >= now() - INTERVAL 2 HOUR, que corresponde apenas às linhas gravadas
por um produtor ativo, e até este módulo o Snowflake era o único destino das gravações do
produtor. Quando a Etapa 3 inicia o produtor do ClickHouse, as novas linhas finalmente
entram nessa janela de duas horas, e a tabela — junto com todos os gráficos de dashboard
baseados nela — deixa de ficar em branco pela primeira vez no laboratório.
Etapa 1 — Adicionar os dashboards do ClickHouse
Para realizar a criação manual completa — criando passo a passo cada um dos 7 conjuntos de dados, 18 gráficos e 4 dashboards na interface do Superset —, siga Superset no ClickHouse. Para pular as etapas manuais e importar tudo de uma só vez:
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.shO host do ClickHouse no arquivo
superset/dashboards/dashboard_export_*.zip incluído no repositório foi substituído por
your-instance.clickhouse.cloud. O atalho acima corrige a URI a partir de .env antes
da importação, portanto isso é transparente — você nem perceberá a substituição. Porém,
se você importar o ZIP manualmente pela interface do Superset, a conexão de banco de
dados criada por ele não funcionará — será necessário editá-la depois para que aponte
para o CLICKHOUSE_HOST e as credenciais corretos. Consulte
Superset no ClickHouse para
ver o procedimento exato.
Verificação:
Abra http://localhost:8088 (admin / admin). Em Dashboards, devem aparecer 7 itens no
total — 3 dashboards do Snowflake e 4 com o prefixo CH —.
Etapa 2 — Executar o benchmark
Execute as sete consultas em sequência no Snowflake e no ClickHouse e compare o tempo decorrido:
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
./scripts/run_benchmark.shSaída esperada:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Esses números são de uma execução representativa, não uma garantia — seus resultados variarão conforme o tamanho do warehouse, o nível do ClickHouse Cloud e quaisquer outras cargas em execução nos dois serviços naquele momento.
O script grava cada execução em
workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv.
Mantenha o arquivo no disco — ele é um dos dois arquivos necessários para a avaliação do
módulo 06; portanto, não o exclua durante a desmontagem da Etapa 5.
Etapa 3 — Fazer a virada para o ClickHouse
Lacuna da migração. O produtor do Snowflake permaneceu em execução durante todo o
laboratório, gravando cerca de 60 corridas por minuto. O script de migração do módulo 03
capturou TRIPS_RAW como ela se encontrava quando esse script foi executado — as linhas
gravadas depois disso existem apenas no Snowflake. Feche essa lacuna antes de transferir
o caminho de gravação.
Execute as quatro etapas abaixo, na ordem indicada — é essa ordem que mantém a consistência entre os dois sistemas:
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.shNão pule a Etapa 2. --resume lê o max(pickup_at) já presente no ClickHouse e
adiciona um filtro WHERE PICKUP_DATETIME > <watermark> à consulta do Snowflake. Assim,
ele transfere somente as linhas gravadas durante e depois da execução da migração
original no módulo 03 — aquelas que o ClickHouse nunca viu. Fazer a virada sem isso
levaria o ClickHouse a perder permanentemente todas as corridas inseridas nessa janela.
A verificação de paridade da Etapa 4 foi feita para detectar exatamente esse problema,
mas somente se esta etapa for executada primeiro.
./scripts/03_cutover.sh exibe a solicitação Type "cutover" to confirm e, em seguida,
repete as etapas de 1 a 3 como uma proteção adicional: interrompe novamente o produtor
do Snowflake (sem efeito se isso já foi feito), executa mais um dbt run e depois cria e
inicia o produtor do ClickHouse (nyc_taxi_ch_producer). Trinta segundos após o início
do produtor, o script confirma que novas linhas estão chegando a default.trips_raw e
executa o dbt mais uma vez — é essa execução que finalmente fornece as primeiras linhas
a agg_hourly_zone_trips, fechando a lacuna que o módulo 04 deixou aberta
intencionalmente.
Verificação:
-- 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 runningComo manter a camada de analytics atualizada. fact_trips e
agg_hourly_zone_trips são modelos incrementais do dbt — eles não se atualizam
automaticamente. 03_cutover.sh executa dbt run uma vez depois de confirmar que o
produtor está ativo, mas os dashboards ficam defasados à medida que novas corridas se
acumulam; execute novamente dbt run em dbt/nyc_taxi_dbt_ch sempre que quiser números
atuais (em produção, você agendaria essa execução — com cron, Airflow ou dbt Cloud —, mas
sob demanda é suficiente no laboratório). Por outro lado,
analytics.mv_live_trip_feed é uma view materializada atualizável — o dbt run do
módulo 04 já a criou com engine = 'ReplacingMergeTree(refreshed_at)' —, mas o laboratório nunca ativa seu intervalo de
atualização: a instrução MODIFY REFRESH EVERY 30 SECOND, que faria a view executar
novamente por conta própria, existe apenas como comentário no arquivo do modelo. Para
ativá-la, você executaria por conta própria uma única instrução ALTER TABLE; sem isso,
mv_live_trip_feed é atualizada somente uma vez, quando o dbt a cria.
Virada reversa, caso seja necessário desfazer esta etapa e voltar ao produtor do 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 producerEtapa 4 — Verificar a paridade
Agora que a passagem de recuperação com --resume foi executada e o produtor do
ClickHouse está ativo, os dois sistemas devem estar em paridade. Confirme:
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.shSaída esperada:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Sua contagem de linhas será diferente; o que importa é a linha de paridade. Nesse ponto,
o produtor do Snowflake já foi interrompido, portanto nenhuma linha nova está sendo
inserida ali — as contagens devem coincidir exatamente ou diferir por poucas linhas, caso
um lote ainda estivesse em andamento durante a passagem com --resume, bem abaixo do
limite de 0,01% verificado pelo script.
Se a verificação de paridade falhar (diferença superior a 0,01%), a lacuna não foi totalmente fechada — execute novamente a passagem de recuperação e repita a verificação:
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.shEtapa 5 — Desmontar os ambientes
Antes de desmontar qualquer coisa, confirme que a migração está em um estado final correto:
| Verificação | Comando | Resultado esperado |
|---|---|---|
| Paridade da contagem de linhas | bash scripts/01_verify_migration.sh | Correspondência da contagem de linhas ≥ 99,9% |
| Testes do dbt | dbt test (em dbt/nyc_taxi_dbt_ch) | Todos os testes passam |
| Dashboards do Superset | Abra http://localhost:8088 | 7 dashboards visíveis (3 SF + 4 CH) |
| Resultados do benchmark | cat scripts/benchmark_results_<timestamp>.csv | As 7 consultas têm um valor de ganho de velocidade |
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 testQuando as quatro verificações passarem, dois arquivos são tudo o que o módulo 06 exige,
e ambos sobrevivem à desmontagem:
workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md e
workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv.
O módulo 06 é uma avaliação escrita com consulta, leva cerca de 60 minutos e não
precisa de mais nada — não há motivo para manter um serviço pago do ClickHouse Cloud em
execução durante uma prova escrita. Copie ou anote o conteúdo dos dois arquivos em um
local acessível e, em seguida, desmonte tudo:
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && ./teardown.shEsse comando destrói o serviço do ClickHouse Cloud (por meio de terraform destroy) e o
contêiner do produtor de corridas do ClickHouse, caso a virada tenha sido realizada.
Os recursos do Snowflake da Parte 1 não são desmontados por esse script. Desmonte a parte do Snowflake separadamente:
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"
source .env && ./teardown.shComo verificar se você terminou
Neste ponto do módulo, você deve ter confirmado, na seguinte ordem:
- A verificação de paridade passou — o
01_verify_migration.shda Etapa 4 relatou PASS com uma diferença na contagem de linhas inferior a 0,01%. - O CSV do benchmark está no disco — a Etapa 2 gravou
benchmark_results_<timestamp>.csvcom um valor de ganho de velocidade para cada uma das 7 consultas, e você o guardou antes da desmontagem na Etapa 5. - Os 7 dashboards estão presentes — a verificação do Superset da Etapa 1 mostrou
lado a lado 3 dashboards do Snowflake e 4 dashboards
CH —. - O produtor do ClickHouse estava gravando — o bloco de verificação da Etapa 3
mostrou
default.trips_rawrecebendo novas linhas enyc_taxi_ch_producerem execução, antes que a Etapa 5 o interrompesse para a desmontagem.
Se alguma dessas condições não era verdadeira naquele momento, volte à etapa correspondente, em vez de tentar executar novamente as verificações agora — a Etapa 5 já destruiu o serviço do ClickHouse Cloud e, caso a virada tenha ocorrido, também o contêiner do produtor.
Estado final
A migração está concluída e medida: 50 milhões de linhas foram transferidas do Snowflake para o ClickHouse e tiveram a paridade verificada; sete consultas foram comparadas diretamente, com o ClickHouse mais rápido em todas elas; a camada de BI foi reconstruída com 4 dashboards do ClickHouse ao lado dos 3 dashboards originais do Snowflake; e o caminho de gravação foi transferido definitivamente do Snowflake para o ClickHouse. Os dois ambientes de nuvem foram desmontados — sem serviço do ClickHouse Cloud, sem contêiner do produtor do ClickHouse e, depois que a desmontagem da Parte 1 também for executada, sem warehouse do Snowflake.
Dois arquivos sobrevivem à desmontagem e são tudo o que o módulo 06 exige:
workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md e
workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv.
O módulo 06 é uma avaliação escrita com consulta — leve apenas esses dois arquivos.
04 Reconstrução do pipeline dbt
Reconstrua o pipeline Medallion no ClickHouse com o dbt-clickhouse — modelos incrementais delete_insert, ReplacingMergeTree e views materializadas atualizáveis — e crie o dicionário de zonas.
06 Avaliação
Conclua a avaliação com 20 questões de múltipla escolha e 4 questões abertas, com consulta permitida, para receber o ClickHouse Migration Proficiency Badge.