What you will be able to do
- Choose between scheduled and stream-triggered tasks for processing incoming documents
- Configure a triggered task on a directory-table stream, including the refresh it depends on
- Pick serverless or warehouse compute and set the parameters each one requires
- Handle failures with retries, auto-suspend and task graphs
1.Scheduled tasks versus stream-triggered tasks
A task runs a SQL statement, which can include a stored procedure call. It runs either on a fixed schedule or when an event fires. A scheduled task uses the SCHEDULE parameter, which takes either an interval such as '60 MINUTES' or a USING CRON expression with a time zone. Snowflake runs only one instance of a scheduled task at a time. "If a task is still running when the next scheduled run time occurs, then that scheduled time is skipped."
Documents tend to arrive at unpredictable times, which is the case triggered tasks are built for. Instead of a schedule, the task has a WHEN clause that calls SYSTEM$STREAM_HAS_DATA. The documentation says this "eliminates frequent polling of the source when new data arrival is unpredictable" and also cuts latency, because data is processed as soon as it arrives.
CREATE TASK triggered_task_stream
WHEN SYSTEM$STREAM_HAS_DATA('orders_stream')
AS
INSERT INTO completed_promotions
SELECT order_id, order_total, order_time, promotion_id
FROM orders_stream;You can also combine the two, so that a scheduled task checks the stream once an hour and runs only when there is something to process. Whichever form you choose, a new task does nothing until you resume it. A task is created suspended. ALTER TASK … RESUME lets it run continuously, and EXECUTE TASK runs it once, which is useful for testing.
Checkpoint 1 of 6· Fill the gap
Complete this task so that it checks the stream every hour but only does work when new data exists.
CREATE TASK triggered_task_stream
SCHEDULE = '1 HOUR'
WHEN ? ('orders_stream')
AS SELECT 1;SYSTEM$STREAM_HAS_DATA is the condition a task's WHEN clause evaluates to decide whether a stream has new change records. The other options manage task graphs or are stream metadata columns.
Source: docs.snowflake.com2.Triggering on new files: the directory-table refresh
Directory tables appear on the list of supported sources for triggered tasks, with one condition. The directory table must be refreshed before the stream sees a new file. You can set the directory table to auto-refresh, or refresh it yourself with ALTER STAGE name REFRESH. Streams on external tables and hybrid tables are not supported for triggered tasks. The documentation includes an example task that fires on a directory-table stream and records file metadata, including the file URL and the stream's action column.
CREATE TASK triggered_task_directory_table
WAREHOUSE = my_warehouse
WHEN SYSTEM$STREAM_HAS_DATA('my_directory_table_stream')
AS
INSERT INTO tasks_runs
SELECT 'trigger_t_internal_stage', relative_path, size,
last_modified, file_url, etag, metadata$action
FROM my_directory_table_stream;Once resumed, the task checks the stream. If there are changes it runs. If not, it skips the run without using compute. If more data arrives while a run is in progress, the next run waits until the current one finishes. By default, triggered tasks run at most every 30 seconds. Setting USER_TASK_MINIMUM_TRIGGER_INTERVAL_IN_SECONDS can lower that to 10 seconds. If a task hasn't run for 12 hours, Snowflake schedules a health check to keep the stream from going stale. Even so, the task body still has to consume the stream before data retention expires.
Checkpoint 2 of 6· Check yourself
Which source can a triggered task NOT be built on?
Streams on external tables are explicitly listed as unsupported for triggered tasks. Directory tables are supported, provided they are refreshed.
“Streams on external tables”Source: docs.snowflake.com
Checkpoint 3 of 6· Exam question
What does querying a stream without performing a subsequent DML operation that consumes it do to the stream's offset?
Correct answer: A — The offset does not advance, so the same change records remain available the next time the stream is queried or consumed
- A. A stream's offset only advances when it is consumed by a DML statement, such as an INSERT ... SELECT, MERGE, or COPY INTO, within an explicit or implicit transaction. A plain SELECT leaves the offset untouched, so the change records are still returned on the next query.
- B. A read-only SELECT never advances the offset; only consuming DML does. Assuming a SELECT advances the offset would cause pipelines to silently lose unprocessed change rows.
- C. Streams are not dropped as a side effect of being queried outside DML; they persist until explicitly dropped or until their underlying object is dropped.
- D. Nothing about a plain SELECT resets the offset to the object's creation time; the offset simply stays where it was until a consuming DML operation moves it forward.
Sources3
3.Serverless or warehouse compute
Every task needs compute, and there are two models. With serverless tasks, Snowflake predicts the resources each run needs from recent runs and assigns them for you. You leave out the WAREHOUSE parameter, and the role needs the global EXECUTE MANAGED TASK privilege. With user-managed tasks, you name a warehouse and size it yourself.
| Factor | Serverless tasks | User-managed tasks |
|---|---|---|
| Workload fit | Under-utilized warehouses, few concurrent tasks, stable runs | Fully utilized warehouses with many concurrent tasks, or unpredictable loads |
| Schedule adherence | Recommended when keeping to the interval matters; Snowflake scales compute up if a run overruns | Recommended when keeping to the interval matters less |
| Billing | Actual compute resource usage | Warehouse size, with a 60-second minimum each time the warehouse resumes |
| Size ceiling | Equivalent to an XXLARGE warehouse | Any warehouse size you choose |
You can put bounds on serverless sizing with SERVERLESS_TASK_MIN_STATEMENT_SIZE (default XSMALL) and SERVERLESS_TASK_MAX_STATEMENT_SIZE (default XXLARGE). Triggered tasks have an extra requirement. A triggered task has no schedule, so a serverless triggered task must declare TARGET_COMPLETION_INTERVAL. Snowflake sizes resources to finish within that interval.
Checkpoint 4 of 6· Fill the gap
Which parameter is required for this serverless triggered task?
CREATE TASK my_triggered_task
? ='15 MINUTES'
WHEN SYSTEM$STREAM_HAS_DATA('my_order_stream')
AS
INSERT INTO customer_activity
SELECT customer_id, order_total, order_date, 'order'
FROM my_order_stream;A serverless triggered task leaves out WAREHOUSE and must include TARGET_COMPLETION_INTERVAL. SCHEDULE has to be left out of a triggered task.
Source: docs.snowflake.comSources1
4.Failures, retries and multi-step task graphs
A pipeline that keeps failing still costs credits. SUSPEND_TASK_AFTER_NUM_FAILURES suspends a task once it has failed or timed out a set number of times in a row. You can set it on the task, or at account, database or schema level, and a lower-level setting overrides a higher one. TASK_AUTO_RETRY_ATTEMPTS retries a task that ends in a FAILED state. Automatic retry is off by default.
When processing has several stages, such as one step that writes raw output and later steps that build on it, you can chain tasks into a task graph (a DAG). The root task defines when the graph runs, either on a schedule or by trigger. Child tasks are created with CREATE TASK … AFTER. Siblings run in parallel, and a task with several parents waits for all of them. An optional finalizer, attached with FINALIZE = <root>, runs after everything else for cleanup or notifications. Every task in a graph must have the same owner and live in the same database and schema.
CREATE OR REPLACE TASK task_root
SCHEDULE = '1 MINUTE'
TASK_AUTO_RETRY_ATTEMPTS = 2 -- Failed task graph retries up to 2 times
SUSPEND_TASK_AFTER_NUM_FAILURES = 3 -- Task graph suspends after 3 consecutive failures
AS SELECT 1;By default, one failed child task means the whole graph run has failed, and the graph is suspended after 10 consecutive failures. You can retry from the point of failure with EXECUTE TASK … RETRY LAST. To change any task in the graph, suspend the root first. A run already in progress will finish.
Checkpoint 5 of 6· Put it in order
Put these steps for building and starting a task graph manually in order.
- 1.Create child tasks with CREATE TASK … AFTER the root
- 2.Resume the root task
- 3.Resume each child task, including the finalizer
- 4.Create the root task with CREATE TASK
Children are defined by naming their parents, so the root has to exist first. When starting the graph task by task, resume the children before the root.
“Resume each individual child task (including the finalizer) that you want to include in the run, and then resume the root task”Source: docs.snowflake.com
Checkpoint 6 of 6· Exam question
An engineer builds a document pipeline where Task A parses raw contracts with AI_PARSE_DOCUMENT and writes output rows, and Task B must run AI_EXTRACT against those rows only after Task A finishes successfully. How should Task B be defined to guarantee this ordering?
Correct answer: A — Define Task B with AFTER referencing Task A so it becomes a child task in the same task graph, running only once Task A completes
- A. The AFTER clause creates a predecessor/successor relationship in a task graph (DAG), so the child task is only scheduled to run once its predecessor completes successfully, which is exactly the dependency guarantee required here.
- B. Matching schedules only makes two tasks start around the same time; it does not guarantee Task A finishes before Task B starts, so Task B could read incomplete or stale output.
- C. SYSTEM$STREAM_HAS_DATA checks whether a stream has unconsumed change records; task history is not a stream, so this function cannot be used to gate execution on another task's completion.
- D. EXECUTE TASK is a privilege that allows a role to manually run or resume a task; it does not create a scheduling dependency between two tasks in a graph.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A triggered task on a directory-table stream fires as soon as a document is uploaded to the stage.Why is that wrong?
The directory table has to be refreshed first, either by auto-refresh or by ALTER STAGE … REFRESH, before the stream and task can see the new file.
Covered in Triggering on new files: the directory-table refresh
2.A serverless triggered task only needs a WHEN clause; omitting WAREHOUSE is enough to make it serverless.Why is that wrong?
A serverless triggered task also has to set TARGET_COMPLETION_INTERVAL, because it has no schedule for Snowflake to size against.
Covered in Serverless or warehouse compute
3.A task begins running on its schedule or trigger as soon as CREATE TASK succeeds.Why is that wrong?
New tasks start suspended. You need ALTER TASK … RESUME to start them, or EXECUTE TASK for a single run.
Practise it for real
Build a triggered task that records every file landing on a stage, using a stream on the stage's directory table.
1.Create an internal stage named my_test_stage with DIRECTORY = ( ENABLE = TRUE ) and SNOWFLAKE_SSE encryption, then create a tasks_runs table whose columns match the SELECT list in the triggered-task example.
Why: The stream needs a directory table to track, and the task needs a target table to write to.
You should see: Both objects exist. LIST @my_test_stage returns no files.
2.Run CREATE STREAM my_directory_table_stream ON STAGE my_test_stage;
Why: The stream sets its offset now and reports file-level changes made after this point.
You should see: SELECT * FROM my_directory_table_stream returns zero rows.
3.Create triggered_task_directory_table exactly as in the triggered-task example, then run ALTER TASK triggered_task_directory_table RESUME.
Why: Tasks are created suspended, so the trigger isn't watched until you resume.
You should see: SHOW TASKS lists the task as started, with SCHEDULE shown as NULL.
4.Run COPY INTO @my_test_stage/my_test_file FROM (SELECT 100) OVERWRITE=TRUE to write a file to the stage.
Why: This lands a new file, but the directory table doesn't know about it yet.
You should see: The task doesn't run yet, because the directory table hasn't been refreshed.
5.Run ALTER STAGE my_test_stage REFRESH, wait at least 30 seconds, then query tasks_runs.
Why: The refresh adds the file to the directory table. The stream picks it up and the task consumes it.
You should see: tasks_runs has a row for my_test_file with metadata$action = INSERT, and the stream is empty again.
Stuck? Get a nudge
If tasks_runs stays empty, check whether you refreshed the directory table. A triggered task can't see a file on the stage until that refresh happens.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.snowflake.com/en/user-guide/tasks-introOfficial docs
“If a task is still running when the next scheduled run time occurs, then that scheduled time is skipped.”
↩︎ Scheduled tasks versus stream-triggered tasks“it eliminates frequent polling of the source when new data arrival is unpredictable.”
↩︎ Scheduled tasks versus stream-triggered tasks“Serverless tasks: Snowflake predicts resources that are needed and assigns them automatically.”
↩︎ Serverless or warehouse compute“For user-managed tasks, billing for warehouses is based on warehouse size, with a 60-second minimum each time the warehouse is resumed.”
↩︎ Serverless or warehouse compute“The maximum size for a serverless task run is equivalent to an XXLARGE warehouse.”
↩︎ Serverless or warehouse compute“The automatic task retry is disabled by default.”
↩︎ Failures, retries and multi-step task graphs“When a task is created, it starts as suspended.”
↩︎ Exam trap 3 - 2.
“A task can transform new or changed rows that a stream surfaces using SYSTEM$STREAM_HAS_DATA.”
↩︎ Scheduled tasks versus stream-triggered tasks - 3.
“Triggered tasks run at most every 30 seconds by default.”
↩︎ Triggering on new files: the directory-table refresh“Task instructions must consume stream data before data retention expires; otherwise, the stream becomes stale.”
↩︎ Triggering on new files: the directory-table refresh“A directory table must be refreshed before a triggered task can detect the changes.”
↩︎ Exam trap 1“To create a serverless task, you must include the TARGET_COMPLETION_INTERVAL parameter.”
↩︎ Exam trap 2“A directory table must be refreshed before a triggered task can detect the changes.”
↩︎ Prediction“Streams on external tables”
↩︎ Checkpoint - 4.
“Create a root task using CREATE TASK, then create child tasks using CREATE TASK .. AFTER to select the parent tasks.”
↩︎ Failures, retries and multi-step task graphs“By default, a task graph is suspended after 10 consecutive failures.”
↩︎ Failures, retries and multi-step task graphs“Resume each individual child task (including the finalizer) that you want to include in the run, and then resume the root task”
↩︎ Checkpoint