8.3 Snowpipe Streaming & Real-Time Ingestion
Key Takeaways
- Snowpipe Streaming ingests rows directly from applications into Snowflake tables (or Snowflake-managed Iceberg tables) without staging files, with ingest-to-queryable latency as low as about 5 seconds and up to 20 GB/s per table.
- The high-performance architecture routes rows through a PIPE object; Elastic Channels (at-least-once, Snowflake-managed) suit IoT and app events, while Named Channels with offset tokens give ordered, exactly-once ingestion for Kafka partitions or CDC.
- Clients use the Java, Python, or Node.js SDK (shared Rust core) or a REST API with NDJSON payloads; billing is throughput-based in credits per uncompressed GB ingested.
- The classic architecture uses the snowflake-ingest-java SDK (insertRows, getLatestCommittedOffsetToken) without a PIPE object and is planned for future deprecation, so new designs should use the high-performance architecture.
- Pairing Snowpipe Streaming for row landing with dynamic tables or streams and tasks for transformation builds low-latency pipelines without external stream processors.
8.3 Snowpipe Streaming & Real-Time Ingestion
Traditional bulk ingestion (COPY INTO) and continuous file ingestion (Snowpipe) share one dependency: data must first be written as files to a stage before Snowflake loads it. For event streams — fraud signals, telemetry, clickstreams, CDC — producing files adds latency and operational work.
Snowpipe Streaming removes the file step: applications send rows directly to Snowflake, which makes them queryable within seconds.
Snowpipe Streaming Architecture: Fileless Ingestion
Instead of Snowflake pulling files from a stage, client applications push rows to Snowflake through an SDK or a REST API.
+-----------------------------------------------------------------------------------------+
| TRADITIONAL FILE-BASED INGESTION (MINUTES) |
| Producer -> Micro-batch Buffer -> Cloud Storage Stage (PUT API) -> Snowpipe -> Table |
+-----------------------------------------------------------------------------------------+
vs
+-----------------------------------------------------------------------------------------+
| SNOWPIPE STREAMING (SECONDS) |
| Producer -> SDK or REST API -> Channel -> PIPE object -> Target table |
+-----------------------------------------------------------------------------------------+
The High-Performance Architecture
- Client side: The Java, Python, or Node.js SDK (all built on a shared Rust client core) buffers appended rows, batches them by time and size, compresses them, and sends them to Snowflake. Lightweight clients (IoT devices, edge services) can call the REST API directly with newline-delimited JSON (NDJSON).
- PIPE object: Rows flow through a PIPE object to the target table. The pipe can apply in-flight transformations using
COPY-style syntax (reorder, cast, expressions) and can pre-cluster data for tables with clustering keys. Snowflake also provides a default pipe per table. - Channels: A channel is the logical path that carries rows through the pipe:
- Elastic Channels (recommended starting point): producers just write; Snowflake manages channels and scales ingestion. Delivery is at-least-once without ordering guarantees.
- Named Channels: the application opens named channels (for example one per Kafka partition) and supplies offset tokens for ordered, exactly-once ingestion within each channel.
- Serverless ingestion: Snowflake commits the rows to the table; they are typically queryable in as little as about 5 seconds, with throughput of up to 20 GB/s per table depending on workload shape.
Architectural Benefits
- Low latency without files: no staging buckets, no file-size tuning, no event notifications.
- Throughput-based pricing: billed in credits per uncompressed GB ingested; no warehouse to size or suspend.
- Iceberg and schema evolution: can stream into Snowflake-managed Iceberg tables and can add new columns detected in the stream.
- Error visibility: optional error logging captures rows that fail processing after they were acknowledged.
Client Channels & SDK Implementation Mechanics
The fundamental abstraction in Snowpipe Streaming is the Channel.
What is a Channel?
A channel is a logical path that carries rows into a table. With Named Channels, multiple client processes can open independent channels to the same table — typically one per upstream partition (Kafka partition, Kinesis shard, CDC slot) — and each channel keeps its own ordering and offset position.
Channel Lifecycle in the Classic Java SDK
The example below uses the classic architecture (snowflake-ingest-java), which you will still see in existing deployments and exam-style scenarios. It has no explicit PIPE object; channels are opened directly against the table. Snowflake plans to announce its deprecation (with an 18-month migration window afterward), so new designs should use the high-performance SDKs, which follow the same channel and offset-token concepts.
// Step 1: Initialize the streaming ingest client with RSA key-pair authentication
SnowflakeStreamingIngestClient client = SnowflakeStreamingIngestClientFactory
.builder("STREAMING_CLIENT_01")
.setProperties(connectionProperties)
.build();
// Step 2: Open a dedicated channel to the target table
OpenChannelRequest openChannelRequest = OpenChannelRequest
.builder("kafka_partition_channel_0")
.setDBName("CORE_DB")
.setSchemaName("STREAMING")
.setTableName("IOT_EVENTS")
.setOnErrorOption(OpenChannelRequest.OnErrorOption.CONTINUE)
.build();
SnowflakeStreamingIngestChannel channel = client.openChannel(openChannelRequest);
// Step 3: Stream rows with an offset token for exactly-once tracking
Map<String, Object> row = new HashMap<>();
row.put("device_id", "DEV-9942");
row.put("reading", 42.85);
row.put("event_timestamp", System.currentTimeMillis());
InsertRowsResponse response = channel.insertRows(
Collections.singletonList(row),
"offset_token_1004829" // Monotonically increasing offset
);
if (response.hasErrors()) {
// Inspect row insertion errors
List<InsertError> errors = response.getInsertErrors();
}
Exactly-Once Delivery Guarantees via Offset Tokens
In high-throughput distributed streaming architectures, network drops, worker container crashes, and consumer group rebalances inevitably cause message retransmissions. Achieving exactly-once semantics without expensive downstream SQL MERGE or deduplication tables is a core architectural requirement.
The Offset Token Mechanism
Snowpipe Streaming solves distributed deduplication through Channel Offset Tokens:
- When the client calls
insertRows, it associates the batch with an opaque string identifier: theoffset_token(e.g., Kafka partition offset1004829, Kinesis sequence number, or transaction LSN). - Snowflake commits the offset token into the table metadata simultaneously with the row data commit.
- Recovery Sequence on Worker Restart:
- If a client worker crashes and restarts, it reconnects and re-opens the channel.
- The client invokes
channel.getLatestCommittedOffsetToken(). - Snowflake returns the last successfully committed token (
1004829). - The client seeks its source stream (e.g., Kafka consumer) to
1004830and resumes streaming. - Exactly-once depends on this pattern: the application reads the committed token and resumes after it, so rows at or before the committed offset are never re-sent. Offset tokens are opaque strings to Snowflake; the client defines their meaning.
+-----------------------------------------------------------------------------------------+
| CRASH RECOVERY WITH OFFSET TOKENS |
+-----------------------------------------------------------------------------------------+
| 1. Client streams records 1001-1005 with offset_token = "1005". |
| 2. Snowflake commits micro-partition & records offset_token = "1005" in catalog. |
| 3. Client crashes before receiving acknowledgment. |
| 4. Client restarts -> Calls channel.getLatestCommittedOffsetToken(). |
| 5. Snowflake responds: Latest offset is "1005". |
| 6. Client fast-forwards upstream queue and resumes from 1006. Zero duplicates! |
+-----------------------------------------------------------------------------------------+
Exam Trap: Offset tokens are strictly channel-scoped, not table-scoped. If Client A opens
channel_1and Client B openschannel_2against the same table, each channel tracks its own independent offset token sequence.
Architectural Comparison: Bulk COPY vs. Snowpipe vs. Snowpipe Streaming
Choosing the correct ingestion pattern is a fundamental competency tested on the SnowPro Advanced: Architect exam.
| Architectural Dimension | Bulk COPY INTO <table> | Snowpipe (Auto-Ingest) | Snowpipe Streaming |
|---|---|---|---|
| Primary Ingestion Paradigm | Scheduled Batch | Continuous Micro-Batch | Continuous Real-Time Streaming |
| End-to-End Ingestion Latency | Minutes to hours (batch schedule) | About a minute or two after file arrival | Seconds (as low as ~5 s) |
| Data Unit | Large files (100 MB–250 MB) | Small files (10 MB–100 MB) | Individual Rows / Records |
| Intermediate Cloud Stage? | Mandatory (S3/Azure/GCS) | Mandatory (S3/Azure/GCS) | None (Direct fileless streaming) |
| Compute Infrastructure | Customer Virtual Warehouse | Snowflake-managed | Snowflake-managed |
| Billing Model | Warehouse credits per second | Fixed credits per GB loaded | Credits per uncompressed GB ingested |
| Deduplication Engine | 64-day load metadata | 14-day pipe load history | Offset tokens (Named Channels) |
| Primary Exam Scenario | Large historical bulk migrations | Regular files landing in cloud buckets | Kafka, IoT, CDC streaming queues |
The Snowflake Connector for Apache Kafka
The Snowflake Connector for Kafka supports two loading methods: Snowpipe (the connector writes files to an internal stage and calls Snowpipe) and Snowpipe Streaming. Streaming is the lower-latency choice:
# Snowflake Kafka Connector configuration for Snowpipe Streaming
connector.class=com.snowflake.kafka.connector.SnowflakeSinkConnector
tasks.max=8
topics=financial_transactions
snowflake.topic2table.map=financial_transactions:FCT_TRANSACTIONS
snowflake.ingestion.method=SNOWPIPE_STREAMING
snowflake.role.name=INGEST_PROD_ROLE
With Snowpipe Streaming, each Kafka topic partition maps to its own channel, and the connector stores Kafka offsets as offset tokens so it can resume exactly where it left off after a restart. Latency drops from minutes to seconds and no intermediate files are created.
Downstream Architectures: Streaming Ingestion with Dynamic Tables
Snowpipe Streaming is optimized for high-throughput row landing. However, analytical workloads rarely query raw, denormalized streaming payloads directly. Modern enterprise architectures combine Snowpipe Streaming with Dynamic Tables to create automated, declarative streaming ELT pipelines:
- Landing Tier (Bronze): Snowpipe Streaming writes raw records into a landing table within seconds.
- Transformation Tier (Silver): A declarative Dynamic Table queries the landing table with
TARGET_LAG = '1 minute'. Snowflake automatically computes incremental deltas, parses JSON attributes, and materializes cleaned dimensional records. - Aggregation Tier (Gold): Downstream Dynamic Tables or aggregate views calculate rolling KPIs for real-time dashboards.
For many pipelines this removes the need for an external stream processor (such as Flink or Spark Streaming) just to pre-process data before loading into Snowflake.
An e-commerce platform produces 50,000 order events per second into Apache Kafka. Fraud analytics needs the data queryable in Snowflake within seconds, and the team wants to avoid managing staged files. Which ingestion strategy fits best?
During a network outage, an application worker streaming IoT events via the Snowpipe Streaming SDK loses connection to Snowflake. After the worker process restarts, how does it prevent duplicate records from being committed to the target table without executing an expensive downstream SQL deduplication query?
Which statement accurately describes an architectural difference between Snowpipe (Auto-Ingest) and Snowpipe Streaming?