Snapshot Load

One-time full table copy when a table is first added to replication.

📷 Initial sync phase

Before CDC can track changes, Openflow performs a complete snapshot of each table. This ensures the destination starts with an exact copy of the source data.

What happens

1

Discover tables

Scans source database for configured tables.

2

Create schema

Creates matching schema in Snowflake.

3

Create table

Creates destination table with CDC columns.

4

Fetch all rows

Reads entire table contents from SQL Server.

5

Upload via Snowpipe

Streams data using the Snowpipe Streaming API.

6

Mark complete

Signals that incremental load can begin.

Key processors

ProcessorFunction
MultiDatabaseFetchSourceTableSchemaGets table DDL from SQL Server
UpdateSnowflakeSchema / UpdateSnowflakeTableCreates the destination schema and table in Snowflake
MultiDatabaseFetchTableSnapshotBulk reads all rows from the source table
PutSnowpipeStreamingHigh-throughput data loading into Snowflake

Result: After snapshot completes, the Snowflake table contains an exact copy of all source data, and the pipeline transitions to incremental CDC mode.

Snapshot Performance & Scaling

The snapshot copies every existing row of each table into Snowflake before incremental replication starts. It is usually the first place a pipeline slows down, and a stalled snapshot blocks the table from ever reaching the incremental phase. This section covers why large tables are the bottleneck, the levers you can tune, and the signals that tell you to scale.

Why large tables are the first bottleneck

During snapshot load the connector reads each source table in primary key order, in keyset-paged chunks, through the Fetch Table Rows [MultiDatabaseFetchTableSnapshot] processor. For a large table this is the slowest phase, and a stalled snapshot blocks the table from ever reaching incremental replication. Throughput comes down to how many tables you read in parallel, the batch sizes, and the runtime's memory and disk headroom.

Levers you can tune

LeverWhere it livesEffectCaution
Concurrent Select Queries For Snapshot SQLServer Ingestion Parameters (drives Concurrent Select Queries on Fetch Table Rows) More concurrent source queries, speeds snapshot of many tables Higher load on the source SQL Server database. Keep it low on a Small runtime.
Runtime size Chosen when the runtime is created (Small / Medium / Large) A larger runtime gives more disk headroom for upload backpressure and more memory Size is fixed at creation. Changing it means migrating the connector to a new runtime.
Oversized Value Strategy SQLServer Ingestion Parameters Controls how a value larger than the connector limit is handled (pass through, set null, or fail the table) Large values raise runtime and warehouse memory load. Exclude multi-GB LOB columns from replication instead.
Fetch Size / Max Batch Size Fetch Table Rows processor properties Rows fetched per round trip and per FlowFile during snapshot Larger batches use more runtime memory. Reduce for very wide rows to avoid out-of-memory.

Runtime disk and backpressure

During snapshot, the queue in front of Upload Rows via Snowpipe Streaming can fill with FlowFiles and trigger back pressure, which consumes runtime disk. For larger tables, use a Large runtime for the extra storage headroom. Because runtime size is fixed at creation, pick the size up front based on the largest tables you will snapshot.

How data reaches Snowflake

The connector loads snapshot rows into Snowflake with Snowpipe Streaming, through the Upload Rows via Snowpipe Streaming processor, grouped per destination table by a concurrency group. Snowpipe Streaming is serverless, so the snapshot upload itself does not run on your warehouse. Account-level Snowpipe Streaming limits still apply as the number of replicated tables grows.

Avoid source deadlocks: SNAPSHOT isolation

Snapshot reads run under SQL Server's default READ COMMITTED isolation and take shared locks. If other clients hold conflicting locks on the same tables, this can deadlock and SQL Server terminates one session. The mitigation is SNAPSHOT isolation, which reads from row versions instead of taking shared locks:

ALTER DATABASE <database> SET ALLOW_SNAPSHOT_ISOLATION ON;

Then enable SNAPSHOT isolation on the connector's source-read processors and restart them. Because the connector reads the source during both snapshot and incremental, this applies to both MultiDatabaseFetchTableSnapshot and MultiDatabaseCaptureChangeSqlServer.

Do not use RCSI. ALLOW_SNAPSHOT_ISOLATION only affects sessions that request it (the connector). READ_COMMITTED_SNAPSHOT (RCSI) redefines the default isolation level for every connection to the database and can change behavior for other applications.

When to scale

Runtime sizing bands and warehouse/merge tuning that span both phases are in the Incremental Performance tab.

Incremental Load

Ongoing capture of changes (INSERT, UPDATE, DELETE) after the initial snapshot.

🔄 Continuous CDC phase

After the snapshot completes, Openflow continuously polls SQL Server Change Tracking. Each polling interval captures the net effect of INSERT, UPDATE, and DELETE activity per row and replicates it to Snowflake in near real-time.

What happens

1

Wait for snapshot

Ensures initial load completed before starting CDC.

2

Read Change Tracking

Polls SQL Server CT tables for new changes.

3

Process schema changes

Handles DDL changes (ALTER TABLE, etc.).

4

Merge rows

Batches multiple changes for efficiency.

5

Upload to journal

Writes changes to the Snowflake journal table.

6

Merge to destination

Applies UPSERT/DELETE to the final table.

Data flow

SQL Server CT
Change Tracking
capture
Journal Table
Staging
merge
Destination
Final Table

Key processors

ProcessorFunction
MultiDatabaseCaptureChangeSqlServerReads SQL Server Change Tracking tables
MultiDatabaseEnrichCdcStreamProcesses schema changes and DDL events
Schedule Warehouse (MergeContent)Bins changes and gates the merge schedule
PutSnowpipeStreamingStreams changes into the journal table
MultiDatabaseMergeSnowflakeJournalTableApplies UPSERT/DELETE from journal to destination

Result: Changes appear in Snowflake within seconds of occurring in SQL Server. Change Tracking reports the net effect of changes between polls, and the journal-plus-MERGE pattern applies those changes idempotently to the destination (keyed on the primary key).

Incremental Performance & Scaling

In steady state, changes are captured through Change Tracking, streamed into per-table journal tables, then applied to destination tables by a MERGE. Loading the journals is cheap and serverless; the MERGE runs on your warehouse and is where nearly all steady-state cost and latency live. This section covers the MERGE, the retention boundary, journal growth, and how to size the runtime and warehouse.

Where the cost is: journal, then MERGE

SQL Server CT
Net change per polling interval
Journal table
Loaded via Snowpipe Streaming (serverless, cheap)
MERGE to destination
Runs on your warehouse (main cost center)

Because the load side is serverless and the Merge Journal to Destination step runs on a warehouse, the MERGE is the primary compute cost and the first place to look for both cost and latency problems. Change Tracking rolls multiple updates to the same row between polls into a single net change, which tends to make merges smaller than the CDC variant, which preserves every intermediate operation.

Control merge cost: Merge Task Schedule CRON

The Merge Task Schedule CRON ingestion parameter governs when the merge is allowed to run by throttling the FlowFiles that reach it. The connector evaluates it in UTC. The throttle is demand-driven: when no changes are pending, no merge runs and the warehouse can auto-suspend.

ScheduleCRONTradeoff
Continuous* * * * * ?Lowest latency, highest steady-state warehouse time
Every 15 minutes0 */15 * * * ?Balanced latency and cost
Hourly0 0 * * * ?Lowest warehouse cost, higher latency and larger per-merge batches

Watch the interaction. Throttling too aggressively lets journals accumulate between windows, producing larger single merges that can turn a concurrency problem into a memory problem. When reinstalling or draining queues, set the CRON back to * * * * * ? first, or queued changes will not drain until the next window.

Warehouse sizing for merges

Snowflake recommends starting at XSMALL and adjusting from there. There are two distinct axes:

Workload shapeRecommendationWhy
Getting startedXSMALL, single cluster, AUTO_SUSPEND onDocs recommend starting XSMALL; demand-driven merges let it auto-suspend when idle.
Many tables (concurrency-bound)Scale OUT: multi-cluster warehouseMany concurrent merges are a concurrency problem; multi-cluster absorbs it better than one bigger warehouse.
High-frequency merges of many large rowsScale UP: Large warehouseMany large rows can collapse into one big MERGE that runs a smaller warehouse out of memory.

Rule of thumb: scale out for table count, scale up for memory-bound single merges.

Change Tracking retention: a hard failure boundary

The most important operational constraint in steady state is the source retention window, set with CHANGE_RETENTION. SQL Server purges tracked changes once they are older than the window. If replication lag ever exceeds retention, the changes the connector needs expire, the affected table permanently fails, and it requires a full reload (a fresh snapshot) to resync.

ALTER DATABASE <database>
  SET CHANGE_TRACKING = ON
  (CHANGE_RETENTION = 5 DAYS, AUTO_CLEANUP = ON);

Set retention comfortably above your worst-case lag, not equal to it, so a paused connector, a network outage, or a failed cycle does not force a reload. Enable Change Tracking with TRACK_COLUMNS_UPDATED = OFF (the default): the connector does not use column-level change info, and turning it on adds source storage and per-DML overhead for no benefit.

Journal tables grow forever

Journal tables are retained indefinitely and never automatically cleaned up (customer data is never auto-deleted). They live in the same schema as the destination and are named <TABLE>_JOURNAL_<epoch>_<generation>, with a new generation on each schema change. To reclaim storage you can truncate journals, drop journals for tables removed from replication, or drop all but the latest generation for active tables.

Do not drop or alter active journal tables. The connector reads the latest-generation journal to drive the merge; removing or altering an active journal can cause data loss or replication failure. Only clean up journals for tables fully removed from replication.

Scaling the runtime

Runtimes are single-node and fixed-size (Small, Medium, Large), chosen at creation; the connector scales vertically, and changing size means migrating to a new runtime. Size to sustained (not peak) load. These documented bands are starting points, not service guarantees:

WorkloadEvents/sec (sustained)Active tablesRuntime
Lightbelow ~1,000fewer than ~100Small
Moderate~1,000 to 5,000hundredsMedium
Heavy~5,000 to 15,000hundreds to low thousandsLarge (or two Mediums for smaller blast radius)
Isolateabove ~15,000 from one sourceanyDedicated runtime

Also use a dedicated runtime when you need sub-1-minute end-to-end latency or cannot tolerate noisy neighbors. Changes load into Snowflake through Snowpipe Streaming (serverless), grouped per destination table; account-level Snowpipe Streaming limits apply as the number of replicated tables grows, so factor table count into capacity planning and raise limits through Support before you approach them.

When to scale

Stream Staleness Prevention

Prevents Snowflake streams from becoming stale during periods of inactivity.

⚠ Why this matters

Snowflake streams have a staleness window based on the table's data retention period. If a stream is not consumed within this window, it becomes STALE and unusable. You lose the ability to track changes and must recreate the stream.

How it works

1

Fetch journal streams

Query Snowflake for all journal stream metadata.

2

Get stream coordinates

Extract stream name and coordinates from results.

3

Check if streams present

Route based on whether any streams exist.

4

Advance STALE_AFTER

Execute SQL to refresh the staleness timestamp.

Stream lifecycle

🕑
ACTIVE
Within retention
time passes
AT RISK
Approaching stale
no activity
STALE
Must recreate

This processor prevents staleness by periodically advancing the stream offset before the retention window elapses.

Processors

ProcessorTypePurpose
Fetch Journal StreamsExecuteSQLRecordQueries Snowflake for all journal stream metadata
Get Stream CoordinatesEvaluateJsonPathExtracts stream name and coordinates from query results
Check If Journal Streams Are PresentRouteOnAttributeRoutes flow based on whether any streams were found
Advance Stream's STALE_AFTERExecuteSQLExecutes SQL to advance the stream's staleness timestamp

Scheduling: This processor group runs on a schedule (typically every few hours) to ensure streams stay fresh even during periods of inactivity on source tables.