MERGE.
Compatible versions
Dynamic tables require a running warehouse for their refresh; size it for your change volume. See Cost and warehouse.
Key Features
- CDC Streaming: utilizes Snowpipe Streaming to deliver real-time changes from the source system.
- Full change history: every change is written as an insert into the changelog table, so nothing is ever overwritten.
- Current state in a dynamic table: a dynamic table merges the changelog into one row per key, with updates applied and deletes removed. See Dynamic Tables.
- Per-subscription control: each subscription chooses whether it gets a dynamic table and how fresh it must be (its target lag).
- Time Travel Support: retains historical data for time travel queries, facilitating data analysis and auditing.
- Key-Pair Authentication: relies on Snowflake’s key-pair authentication mechanism — no passwords.
- Schema evolution: new columns are added automatically as the source schema evolves, to the changelog and to the dynamic table.
Prerequisites
- A Snowflake account where you wish to sync your data.
- A warehouse for the dynamic table refreshes.
- A role with the privileges listed under Role privileges below.
-
A key pair (no passphrase) for authentication. To set it up:
- Generate the key pair:
- Register the public key on the Snowflake user:
- Copy the private key without the header and footer lines:
- Generate the key pair:
Role privileges
Run these as a role that can grant on the target database —ACCOUNTADMIN, or the
owner of the database and schema:
USAGE on the database and schema makes them visible to the role. USAGE on the
warehouse is what lets it create the dynamic table and run that table’s refreshes —
the only part of provisioning that needs a running warehouse; the rest is
cloud-services DDL. CREATE TABLE covers the append-only changelog table the
connector creates per subscription. CREATE PIPE covers the Snowpipe Streaming
pipe it creates alongside each of those tables (<table>_STREAMING), which is the
object streamed rows actually arrive through — without it, provisioning fails
before the first row lands. CREATE DYNAMIC TABLE covers the current-state table
built over the changelog.
No INSERT, UPDATE or DELETE grant is needed: the connector creates the tables
it writes to and therefore owns them, and rows reach them through the pipe rather
than through SQL DML. If instead you point the connector at a schema whose tables
already exist and are owned by another role, that owner has to grant write and
schema-evolution rights on each one, or new source columns will fail to land:
The role owns the dynamic tables it creates, so no extra grant is needed to alter, resume or rebuild them. To let other roles read them, grant
SELECT on the dynamic tables (or SELECT ON FUTURE DYNAMIC TABLES IN SCHEMA) as you would for any table.Configuration
How It Works
For each subscription writing table<T>, Popsink creates three objects in the connector’s database and schema:
Changes are streamed into
<T>_APPEND as inserts: an update or a delete adds a row, it never modifies one. The dynamic table <T> reads the new rows of <T>_APPEND and merges them, so queries on <T> see the source table as it is, while <T>_APPEND keeps everything that happened to it.
Table names must be simple identifiers (letters, digits, _ and $, not starting with a digit). They are created unquoted, so Snowflake stores them in upper case.
Changelog columns
Besides the source columns, every row of the changelog carries:
These columns are carried into the dynamic table too, except
before_data: each row of the dynamic table tells you which change produced it and when.
__partition and __offset only order the changes of a row; do not use them as a row identifier. A backfill row reads the source table directly rather than through Kafka, so its __offset is a counter derived from the range the loader read. On an IBM i source it can look like the record’s RRN, but it is not one: it drifts after deleted records, follows an index key on large files, and is replaced by a Kafka offset as soon as the row changes.
Dynamic Tables
A dynamic table is a Snowflake table whose content is defined by a query and kept up to date by Snowflake itself, on a schedule set by its target lag. Popsink uses one per subscription to turn the changelog into the current state of the source table.How the merge works
On each refresh, Snowflake reads only the rows added to<T>_APPEND since the previous refresh, and merges them into <T>:
- Keep the latest change of each key. The new rows are grouped by the subscription’s primary key, and the latest one wins: the highest
__source_ts_ms, then the highest__partitionand__offsetto break ties between two changes made in the same millisecond. - Apply it only if it is newer than the row already in
<T>, by the same ordering. A late or replayed event never overwrites a more recent state. - Apply the change:
- a delete removes the key from
<T>; - any other change updates the row, or inserts it if the key is not there yet.
- a delete removes the key from
42:
After the refresh, the dynamic table
ORDERS holds one row, order 42 with status shipped. Order 43 was deleted, so it is not there. Both histories stay in ORDERS_APPEND.
Deletes are hard deletes in the dynamic table. To find deleted rows, query the changelog for __deleted = TRUE.
Because only the new rows are read, a refresh costs in proportion to the changes since the last one, not to the size of the table. Popsink creates the dynamic table with:
The
COMMENT is how Popsink recognizes a dynamic table it manages: it records the key and the columns the table was built with. Do not change it, and do not alter the definition by hand: Popsink would recreate the table on its next check.Configuring a subscription
In the subscription drawer, the dynamic table is set per subscription:
The same options can be set on several subscriptions at once from the bulk configuration panel.
Choosing the target lag. A shorter lag gives fresher data but runs the warehouse more often. The lag is a target, not a guarantee: a refresh that takes longer than the lag, or a busy warehouse, delays the next one.
Changing the options. A new target lag or warehouse is applied in place with
ALTER DYNAMIC TABLE ... SET TARGET_LAG/WAREHOUSE: the table is not rebuilt. Turning the option off stops Popsink from managing the dynamic table, but does not drop it: drop it yourself if you no longer need it.
Several subscriptions can write the same table. If they disagree on whether to build the dynamic table or on its target lag, the table falls back to the defaults (on,
60 min) and a warning is logged.First refresh
The dynamic table is created withINITIALIZE = ON_SCHEDULE: Popsink does not wait for it to be filled, and the first refresh runs on Snowflake’s schedule, within the target lag. Until then, querying it returns an error saying the table has not been initialized yet. The changelog <T>_APPEND can be queried right away.
To fill it immediately, run:
Tables without a primary key
A dynamic table needs a key to merge on. When the subscription has no primary key, Popsink uses__hash, the hash of the row’s content, as the key, and logs a warning:
- an update writes a delete for the old content and an insert for the new content, so the old row is removed;
- two source rows with exactly the same content collapse into one row in the dynamic table.
Schema evolution
When a new column reaches the subscription, Popsink:- adds it to
<T>_APPEND; - recreates the dynamic table with the new column.
NULL in it.
Columns are never dropped or retyped.
Rebuilding a dynamic table
If a dynamic table is lost, dropped or out of sync, open the target, and in its subscription list choose Rebuild dynamic table in the subscription’s menu. Popsink recreates the dynamic table from its changelog. Only one rebuild runs at a time. A rebuild is also triggered automatically after a backfill created the changelog.Cost and warehouse
Each refresh runs on the connector’s Warehouse and is billed as warehouse compute. The changelog itself is written by Snowpipe Streaming and does not use the warehouse.- Use a warehouse with
AUTO_SUSPENDandAUTO_RESUMEso it only runs during refreshes. - Many dynamic tables with a short target lag keep the warehouse busy. Raise the target lag on tables that do not need to be fresh before sizing up the warehouse.
- A dedicated warehouse makes refresh costs easy to follow, and keeps them from competing with your queries.