Snowflake to BigQuery Migration — ELT Platform on Google Cloud

By Amar Kumar

A proposed Snowflake to BigQuery migration for an ELT-heavy analytics platform would rebuild streaming and batch ingestion on Google Cloud, port Snowflake-native SQL (Streams, Tasks, dynamic tables, stored procedures) into dbt incremental models, replace cron with Cloud Composer, run a historical backfill and parallel reconciliation, then cut over BI, reverse-ETL, and AI pipelines — with the legacy Snowflake stack and managed ELT tool decommissioned.

Proposed outcome: A reconciled BigQuery platform with Kafka Sink and CDC ingestion, dbt transforms, Composer orchestration, residency-aware IAM controls, GCP cost visibility, and a freeze / final-delta / cutover executed with minimal downtime.

Scenario

This brief describes a proposed solution — not a delivered engagement. It maps a recurring warehouse migration pattern: an analytics platform that grew on Snowflake must standardize on BigQuery for cost, GCP-native integrations, and residency controls, while production continues until cutover.

Problem

Moving warehouses is not a dump-and-load. Snowflake-native constructs, cron orchestration, and a managed ELT dependency create a multi-track migration where ingestion, transforms, security, and cutover each need an explicit design — not a one-shot dialect rewrite.

Requirements

Functional

Non-functional

Architecture

Four tracks run in parallel until cutover: ingestion (Kafka Sink + CDC → BigQuery raw), transform (dbt incremental → curated/marts), orchestration (Composer DAGs), and migration control (historical GCS loads + reconciliation against Snowflake).

flowchart TB classDef src fill:#f8fafc,stroke:#475569,color:#334155 classDef ingest fill:#ede9fe,stroke:#7c3aed,color:#5b21b6 classDef bq fill:#dbeafe,stroke:#2563eb,color:#1e3a8a classDef orch fill:#fef3c7,stroke:#d97706,color:#92400e classDef legacy fill:#fee2e2,stroke:#dc2626,color:#991b1b classDef cons fill:#ccfbf1,stroke:#0d9488,color:#115e59 KAFKA["Kafka topics\nstream events"]:::src OLTP["OLTP / SaaS\nsources"]:::src SF["Snowflake\nlegacy warehouse"]:::legacy SINK["Kafka BigQuery Sink\nStorage Write API"]:::ingest CDC["Airbyte / Datastream\nCDC · batch ELT"]:::ingest GCS["GCS staging\nunload · backfill"]:::ingest RAW["BigQuery raw\npartitioned · clustered"]:::bq DBT["dbt incremental\nstaging · core · marts"]:::bq CUR["BigQuery curated\nIAM · RLS · policy tags"]:::bq COMP["Cloud Composer\nAirflow DAGs"]:::orch CRUN["Cloud Run\nnon-SQL jobs"]:::orch BI["BI tools\nnative BQ connector"]:::cons RETL["Reverse ETL"]:::cons AI["AI insights\npipelines"]:::cons KAFKA --> SINK OLTP --> CDC SINK --> RAW CDC --> RAW SF --> GCS GCS --> RAW RAW --> DBT DBT --> CUR COMP --> DBT COMP --> CDC COMP --> CRUN CUR --> BI CUR --> RETL CUR --> AI SF -.->|"parallel reconcile"| CUR

Target architecture — rebuilt ingestion into BigQuery raw, dbt transforms to curated marts, Composer orchestration, and parallel reconciliation against Snowflake until cutover

sequenceDiagram autonumber participant Src as Sources participant SF as Snowflake participant BQ as BigQuery participant Rec as Reconcile suite participant Cons as BI · reverse-ETL · AI Src->>SF: production writes continue Src->>BQ: dual-write / rebuilt ingestion SF->>BQ: historical unload via GCS loop nightly parallel run BQ->>Rec: row counts · key aggregates SF->>Rec: same metrics Rec-->>Rec: diff report · alert on breach end Note over Src,Cons: freeze window Src--xSF: pause non-critical writers SF->>BQ: final delta sync Rec->>Rec: sign-off checklist Cons->>BQ: repoint connections Note over SF: decommission ELT + Snowflake

Cutover sequence — dual-run with reconciliation, freeze, final delta, consumer repoint, then decommission of Snowflake and the managed ELT tool

Component map by migration track (major services per layer)

End-to-end flow

Six-month lifecycle — foundation and ingestion first, transforms and Composer next, historical backfill and parallel run, then freeze and cutover

Indicative share of Snowflake transform objects by migration path (illustrative)

Example reconciliation thresholds before cutover sign-off (illustrative)

Recommendation: standardize the warehouse on BigQuery with dbt for transforms, Kafka Connect BigQuery Sink for streaming, Airbyte OSS or Datastream for CDC/batch replacement of the managed ELT tool, and Cloud Composer for orchestration — using the BigQuery Migration Service for bulk SQL assist and Cloud Run only where SQL cannot express prior JS/procedure logic.

LayerTechnologyWhy
WarehouseBigQuery (Editions / autoscaling slots)Target platform; Standard SQL, partitioning, clustering, IAM, Storage Write API
Transformdbt Core or dbt Cloud on BigQueryIncremental models + macros replace Tasks / dynamic-table patterns with version control and tests
SQL assistBigQuery Migration ServiceBulk Snowflake → BigQuery dialect translation; human review for remainder
Streaming ingestKafka Connect BigQuery SinkFits existing Kubernetes footprint; Storage Write API for throughput
CDC / batch ELTAirbyte OSS or DatastreamOpen-source or managed CDC with native BigQuery destinations; exits managed ELT lock-in
OrchestrationCloud Composer (Airflow 2)DAG dependencies, retries, backfills, and alerting — stronger than cron or ad-hoc Scheduler chains
Alt orchestrationCloud Scheduler + WorkflowsLighter option if DAG graph is small; Composer preferred for ELT-heavy dependency trees
Non-SQL jobsCloud Run + PythonPort shell scripts and JS stored-procedure logic with Workload Identity
Historical loadGCS + load jobs / Data Transfer ServiceUnload from Snowflake, land with partition/cluster design
SecurityIAM, RLS policies, policy tags, authorized viewsRow/column controls and residency-aware dataset layout
IaC (preferred)Terraform + Helm / ArgoCDReproducible Sink, CDC, and Composer environment on GKE
Cost opsBilling export → BigQuery + Looker / Looker StudioSlot vs on-demand visibility; prove migration savings

Why dbt over BigQuery scheduled queries alone? An ELT-heavy estate needs incremental materializations, ref graphs, tests, and PR review. Scheduled queries work for a handful of jobs; they do not replace a transformation layer that grew as Streams, Tasks, and dynamic tables.

Why Composer over Scheduler + Workflows? Composer is heavier to operate but matches cron-replacement reality: multi-hop dependencies, backfills during parallel run, and SLA alerts. Scheduler + Workflows is a fallback when the DAG surface is intentionally small.

Why Airbyte or Datastream instead of keeping the managed ELT tool? Cutover success includes decommissioning that tool. Open-source or GCP-native CDC with BigQuery destinations removes a second vendor boundary and aligns residency and IAM with the rest of the GCP estate.

Illustrative dbt incremental pattern replacing a Snowflake Stream + Task CDC merge:

{{ config(
  materialized='incremental',
  incremental_strategy='merge',
  unique_key='event_id',
  partition_by={'field': 'event_date', 'data_type': 'date'},
  cluster_by=['account_id']
) }}

select
  event_id,
  account_id,
  event_ts,
  date(event_ts) as event_date,
  payload
from {{ ref('stg_events') }}
{% if is_incremental() %}
where event_ts > (
  select coalesce(max(event_ts), timestamp('1970-01-01'))
  from {{ this }}
)
{% endif %}

Component design

1 — Inventory & translation factory

2 — BigQuery foundation & security

3 — Streaming ingestion (Kafka Sink)

4 — CDC / batch ELT replacement

5 — dbt transformation layer

6 — Cloud Run re-architecture jobs

7 — Orchestration (Composer)

8 — Backfill & reconciliation

ComponentResponsibilityInputsOutputs
Unload orchestratorExport Snowflake history to GCS by partitionTable list, date ranges, warehouse sizeParquet/Avro in GCS; manifest checksums
Load controllerBigQuery load / Data Transfer with cluster keysGCS manifests, table DDLLoaded raw/history tables
Reconcile suiteCompare SF vs BQ nightlyPrimary keys, metric SQL catalogDiff report; alert on threshold breach
Cutover runbookFreeze, final delta, repoint, smoke testsSign-off checklistConsumer cutover ticket + rollback flag

Indicative build effort distribution by phase (% of total hours)

Implementation plan

Phase 1 — Foundation, security & inventory (weeks 1–4)

Stand up BigQuery projects/datasets by residency region; define partition/cluster standards; implement IAM, service accounts with Workload Identity, row-level policies, policy tags, and authorized views for PII. Complete Snowflake object inventory and tag each object with migration path (auto / manual / Cloud Run). Wire billing export for cost baselines. Scaffold empty dbt project and Composer environment (dev).

Risk: Incomplete inventory misses shadow Tasks or undocumented shell jobs. Rollback: no production traffic on BigQuery yet — foundation-only; Snowflake unchanged.

Phase 2 — Ingestion rebuild (weeks 3–8)

Deploy Kafka BigQuery Sink on the existing Kubernetes footprint (Helm/ArgoCD). Stand up Airbyte OSS or Datastream for sources previously on the managed ELT tool; dual-write or shadow-load into BigQuery raw while Snowflake remains source of truth. Add DLQ, schema-drift detection, and lag dashboards. Switch pilot BI/event connectors to native BigQuery destinations in a non-prod consumer.

Risk: Sink throughput or schema evolution breaks consumers. Rollback: disable Sink connectors; Snowflake ingestion continues; raw BigQuery tables retained for replay.

Phase 3 — Transformation migration (weeks 5–14)

Run BigQuery Migration Service on the SQL corpus; triage failures into manual dbt models. Port Streams + Tasks CDC paths to incremental merge models; replace dynamic tables with incremental or table materializations on Composer schedules. Re-architect JS procedures and clone-based workflows into Cloud Run or BigQuery SQL. Expand dbt tests and contracts; begin mart-level reconciliation against Snowflake.

Risk: Semi-structured VARIANT logic and QUALIFY-heavy queries under-translate. Rollback: keep Snowflake transforms authoritative; BigQuery marts stay in shadow until reconcile gates pass.

Phase 4 — Orchestration cutover from cron (weeks 10–16)

Model cron jobs as Composer DAGs with explicit dependencies, retries, and SLA miss alerts. Migrate backfill operators needed for parallel-run catch-up. Retire equivalent cron entries only after two weeks of green DAG runs. Document on-call playbooks for DAG failure and stuck sensors.

Risk: Hidden cron order assumptions surface as race conditions. Rollback: re-enable specific cron lines; Composer DAGs paused per domain.

Phase 5 — Historical backfill & parallel run (weeks 14–20)

Unload large tables to GCS; load into BigQuery with final partition/cluster design. Run nightly reconciliation: row counts, key aggregates, and sampled hash checks. Tune slots vs on-demand using observed scan patterns. Hold cutover gate until reconcile KPIs stay inside thresholds for an agreed streak (e.g. 10 consecutive days).

Risk: Multi-terabyte unload cost/time overruns — partition by date and parallelize. Rollback: stop loads; retain GCS manifests; Snowflake remains production.

Phase 6 — Freeze, cutover & decommission (weeks 20–24)

Execute freeze window: pause non-critical writers, apply final delta, run smoke tests on BI / reverse-ETL / AI pipelines, repoint production connections to BigQuery. Decommission managed ELT connectors, then Snowflake warehouses and unused roles after a short hypercare period. Hand off cost dashboards and runbooks to the internal DE team.

Risk: Late-discovered consumer still hardcoded to Snowflake. Rollback: keep Snowflake readable through hypercare; reverse DNS/connection strings if smoke tests fail before freeze close.

Reporting & ops

SignalSourceCadence
Composer DAG success / duration / SLAAirflow metrics + Cloud MonitoringReal-time; page on SLA miss
Kafka Sink lag & DLQ depthConnector JMX / Kafka lag exportersReal-time; alert on lag breach
CDC sync freshnessAirbyte / Datastream job statusPer sync; alert on consecutive failure
SF vs BQ row-count / aggregate diffsReconcile suite → BigQuery audit tablesNightly during parallel run
Scanned bytes & slot utilizationINFORMATION_SCHEMA + Editions metricsDaily cost review
Policy-tag / IAM access anomaliesData Catalog + Cloud Audit LogsWeekly compliance export
dbt test failuresdbt Cloud / CI artifactsPer deploy; block merge on critical tests
GCP billing vs baselineBilling export dashboardWeekly during migration; monthly after

Ops cadence would include a daily parallel-run reconcile email (diffs, lag, failed DAGs), a weekly migration standup with the Architect / PM (inventory burn-down, cutover blockers), and hypercare war-room coverage for 1–2 weeks post-cutover. On-call pages for DAG SLA misses, Sink lag, and reconcile threshold breaches — not for every dbt warning.

Proposed deliverables

Following the phased plan, a build would ship these artifacts:

Effort estimate

Indicative effort for a six-month, hands-on IC engagement (assumes existing Kafka/Kubernetes footprint, Architect/PM partnership, and AI-assisted bulk SQL translation):

ScopeHours (range)
Phase 1 — Foundation, security & inventory80–120 hrs
Phase 2 — Ingestion rebuild120–180 hrs
Phase 3 — Transformation / dbt migration200–280 hrs
Phase 4 — Composer orchestration80–120 hrs
Phase 5 — Backfill & parallel run120–180 hrs
Phase 6 — Cutover & decommission60–100 hrs
Total (Phases 1–6)660–980 hrs
Ongoing maintenance (DAGs, models, cost tuning)20–40 hrs/month

At 30+ hrs/week, delivery would span roughly 22–26 weeks foundation through hypercare. Milestone gates — Sink healthy, dbt marts reconciling, Composer green, backfill complete, cutover signed — keep dual-run cost bounded and make rollback decisions explicit.

Glossary

TermMeaning
ELTExtract-Load-Transform — load raw data first, then transform inside the warehouse
CDCChange Data Capture — incremental replication of source inserts/updates/deletes
Snowflake Stream / TaskNative CDC change table + scheduled SQL runner often used for incremental merges
Dynamic tableSnowflake declarative incremental materialization; typically remapped to dbt incremental models
dbtData build tool — SQL models, tests, docs, and incremental strategies on BigQuery
BigQuery Migration ServiceGoogle tooling to assist bulk translation of Snowflake SQL to BigQuery Standard SQL
Storage Write APIHigh-throughput BigQuery write path used by the Kafka BigQuery Sink
Cloud ComposerManaged Apache Airflow on GCP for DAG-based orchestration
Authorized viewBigQuery view that grants query access to a subset of columns/rows without table-level grants
Policy tagData Catalog taxonomy node used for column-level access and masking
Parallel runPeriod when Snowflake and BigQuery both process production-like data for reconciliation
Freeze / final deltaShort write pause, last change sync, then consumer cutover to BigQuery