Download icon
Upload icon
Query icon
Queries icon
SlackIncomingWebhook icon
snowflake icon
Schedule icon

Snowflake Incremental MERGE Upsert with a Transactional Load

Orchestrate idempotent Snowflake upserts with Kestra. Stage a delta file, COPY into staging, and MERGE into the target as one transactional batch.

Categories
Data

Apply a batch of changed rows to a Snowflake target table as an idempotent incremental upsert, without ever leaving the target in a half-applied state. This blueprint uploads a delta file to the table's internal stage, resets a transient staging table, COPYs the delta in, and MERGEs into the target with deduplication on the business key, all inside a single Snowflake transaction. If any statement fails, the whole batch rolls back. Reruns are safe because the MERGE is keyed and the dedup keeps only the latest row per key.

How it works

  1. io.kestra.plugin.core.http.Download (fetch_delta) pulls a sample CSV delta. Replace this with your real extract (S3, GCS, HTTP, SFTP, dbt artifact, CDC capture, etc.).
  2. io.kestra.plugin.jdbc.snowflake.Upload (stage_upload) uploads the file to the target table's internal stage @KESTRA.PUBLIC.%CUSTOMERS under a delta/ prefix with gzip compression.
  3. io.kestra.plugin.jdbc.snowflake.Query (ensure_staging) issues CREATE TRANSIENT TABLE IF NOT EXISTS ... LIKE CUSTOMERS as a one-off DDL outside the transaction.
  4. io.kestra.plugin.jdbc.snowflake.Queries (merge_upsert) runs with transaction: true and a per-execution queryTag. It DELETEs the staging rows, COPYs the staged delta with ON_ERROR = 'abort_statement', then MERGEs into CUSTOMERS using QUALIFY ROW_NUMBER() OVER (PARTITION BY CUSTOMER_ID ORDER BY UPDATED_AT DESC) to dedup late or duplicate rows.
  5. io.kestra.plugin.slack.notifications.SlackIncomingWebhook (notify) posts a confirmation; the alert_on_failure error handler posts a rollback notice if anything fails.
  6. A daily Schedule trigger at 0 4 * * * is included but disabled by default.

What you get

  • Atomic incremental load: COPY and MERGE in one Snowflake DML transaction, with automatic rollback on failure.
  • Idempotent reruns thanks to a keyed MERGE and a QUALIFY ROW_NUMBER dedup on CUSTOMER_ID / UPDATED_AT.
  • Per-execution queryTag so every batch is traceable in Snowflake's QUERY_HISTORY.
  • Slack confirmation on success and a separate Slack alert on rollback.
  • Reusable pattern across any SCD2-style or last-write-wins upsert table.

Who it's for

  • Analytics and data platform engineers loading CDC or batch deltas into Snowflake.
  • Teams replacing fragile shell scripts or stored procedures with declarative, auditable pipelines.
  • ELT practitioners who want pre-load orchestration that dbt does not cover.

Why orchestrate this with Kestra

Snowflake Tasks can schedule SQL, but they cannot fetch a delta file from an external system, upload it to an internal stage, and coordinate a multi-statement transaction with retries, Slack alerting, and lineage in one declarative unit. Kestra triggers this flow on a schedule or an event (webhook, file arrival, upstream extract finishing), captures every statement's result, retries transient failures, exposes outputs to downstream tasks, and keeps the full YAML in Git. Pair it with namespace secrets for credentials so nothing is hardcoded.

Prerequisites

  • A Snowflake account with a warehouse, the target table KESTRA.PUBLIC.CUSTOMERS (keyed by CUSTOMER_ID with an UPDATED_AT column), and grants to create transient tables and write to the table's internal stage.
  • A Slack incoming webhook for notifications.

Secrets

  • SNOWFLAKE_URL: JDBC URL, for example jdbc:snowflake://<account>.snowflakecomputing.com?warehouse=COMPUTE_WH.
  • SNOWFLAKE_USERNAME: Snowflake user.
  • SNOWFLAKE_PASSWORD: Snowflake password (swap for privateKey for key-pair auth).
  • SLACK_WEBHOOK_URL: Slack incoming webhook URL.

Quick start

  1. Add the secrets above to your Kestra namespace.
  2. Create the target table and confirm the user can write to @KESTRA.PUBLIC.%CUSTOMERS.
  3. Replace fetch_delta with your real extract and point stage_file at the resulting filename.
  4. Adjust the table names, merge key, and column list in merge_upsert to your schema.
  5. Run the flow manually, then enable the daily Schedule trigger when ready.

How to extend

  • Swap fetch_delta for io.kestra.plugin.aws.s3.Downloads, io.kestra.plugin.gcp.gcs.Downloads, or an SFTP task to pull deltas from your lake.
  • Replace the schedule with a Webhook or Flow trigger so the upsert fires the moment upstream extract finishes.
  • Add io.kestra.plugin.core.flow.ForEachItem to parallelize across multiple target tables.
  • Switch to key-pair auth by replacing password with privateKey (and privateKeyPassword if encrypted) in pluginDefaults.
  • Emit row counts via outputs.merge_upsert.outputs and assert thresholds with a follow-up Assert task.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.