Streams 와 Tasks (변경 추적, 서버리스 태스크, 태스크 그래프)
테이블 변경분을 추적하는 Stream 의 종류와 오프셋, 오래됨(stale) 문제, 일정과 트리거로 SQL 을 돌리는 Task 의 서버리스와 사용자 관리 방식, 태스크 그래프, 실패 처리와 비용을 정리합니다.
Stream: 무엇이 바뀌었나
Stream 은 테이블에 오프셋(책갈피) 을 하나 꽂아 두고, 그 뒤로 바뀐 행을 보여 주는 객체입니다. 데이터를 복사해 두는 것이 아니라 Time Travel 의 버전 정보를 이용합니다.
CREATE STREAM raw.orders_stream ON TABLE raw.orders;
SELECT *, METADATA$ACTION, METADATA$ISUPDATE, METADATA$ROW_ID
FROM raw.orders_stream;| 메타데이터 열 | 뜻 |
|---|---|
METADATA$ACTION | INSERT 또는 DELETE |
METADATA$ISUPDATE | UPDATE 가 DELETE 와 INSERT 쌍으로 표현된 경우 TRUE |
METADATA$ROW_ID | 행의 고유 ID, 같은 행의 변경을 추적 |
종류
| 종류 | 대상 | 보여 주는 것 |
|---|---|---|
| 표준(standard) | 테이블, 뷰, Dynamic Table | INSERT, UPDATE, DELETE 의 순 변경 |
| 추가 전용(append-only) | 테이블, 뷰 | INSERT 만. 변경이 많은 테이블에서 훨씬 가벼움 |
| 삽입 전용(insert-only) | 외부 테이블, Iceberg 테이블 | 새로 추가된 파일의 행 |
오프셋은 언제 움직이나
스트림을 SELECT 만 해서는 오프셋이 움직이지 않습니다. 스트림을 읽는 DML 이 커밋될 때 오프셋이 앞으로 갑니다. 그래서 보통 아래처럼 MERGE 안에서 소비합니다.
MERGE INTO mart.orders t
USING (SELECT * FROM raw.orders_stream WHERE METADATA$ACTION = 'INSERT') s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.status = s.status, t.amount = s.amount
WHEN NOT MATCHED THEN INSERT (id, status, amount) VALUES (s.id, s.status, s.amount);주의 같은 스트림을 두 소비자가 나눠 쓰면 한쪽이 소비할 때 다른 쪽 변경분도 사라집니다. 소비자마다 스트림을 따로 만듭니다.
오래됨(stale)
스트림 오프셋이 원본 테이블의 Time Travel 보존 기간 밖으로 밀려나면 스트림이 stale 이 되어 읽을 수 없습니다. 이를 막기 위해 Snowflake 는 소비되지 않은 스트림이 있으면 보존 기간을 최대 MAX_DATA_EXTENSION_TIME_IN_DAYS(기본 14일)까지 자동으로 늘립니다. 그래도 그 기간 동안 소비하지 않으면 stale 이 됩니다.
SHOW STREAMS IN SCHEMA raw; -- stale, stale_after 열 확인2026년 9월에는 계정 전체 스트림을 볼 수 있는 ACCOUNT_USAGE.STREAMS 뷰가 추가되었습니다.
Task: 언제 무엇을 돌리나
Task 는 SQL 문 하나나 프로시저 호출을 일정이나 조건에 따라 실행합니다.
컴퓨트 방식
| 방식 | 지정 | 과금 | 맞는 경우 |
|---|---|---|---|
| 서버리스 | WAREHOUSE 를 빼고 만듦 | 실제 사용량, 서버리스 배수 0.9 | 짧고 규칙적인 작업, 대부분 |
| 사용자 관리 | WAREHOUSE = ... | 웨어하우스 크레딧 (최소 60초) | 이미 늘 켜진 웨어하우스, XXLARGE 보다 큰 작업 |
서버리스 태스크는 Snowflake 가 이전 실행을 보고 크기를 고르고, 최대 크기는 XXLARGE 입니다. SERVERLESS_TASK_MAX_STATEMENT_SIZE 로 상한을, TARGET_COMPLETION_INTERVAL 로 끝나야 하는 시간을 줄 수 있습니다. 서버리스 태스크를 만들려면 계정 수준 EXECUTE MANAGED TASK 권한이, 태스크를 실행하려면 소유 역할에 EXECUTE TASK 권한이 필요합니다.
일정 방식
-- 10분마다
CREATE TASK ops.t_refresh SCHEDULE = '10 MINUTES' AS CALL ops.refresh_marts();
-- 매일 서울 시간 오전 6시
CREATE TASK ops.t_daily
SCHEDULE = 'USING CRON 0 6 * * * Asia/Seoul'
USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE = 'SMALL'
AS CALL ops.daily_job();
-- 트리거 태스크: 일정 없이 스트림에 데이터가 들어오면 실행
CREATE TASK ops.t_on_new_orders
TARGET_COMPLETION_INTERVAL = '5 MINUTES'
WHEN SYSTEM$STREAM_HAS_DATA('raw.orders_stream')
AS MERGE INTO mart.orders ...;
ALTER TASK ops.t_on_new_orders RESUME; -- 만들면 일시 중지 상태WHEN 조건은 클라우드 서비스에서 평가되어, 조건이 거짓이면 웨어하우스를 깨우지 않습니다. 서버리스 트리거 태스크는 TARGET_COMPLETION_INTERVAL 이 필요합니다.
태스크 그래프 (DAG)
CREATE TASK ops.root SCHEDULE = 'USING CRON 0 * * * * UTC' AS SELECT 1;
CREATE TASK ops.load_a AFTER ops.root AS CALL ops.load('a');
CREATE TASK ops.load_b AFTER ops.root AS CALL ops.load('b');
CREATE TASK ops.build AFTER ops.load_a, ops.load_b AS CALL ops.build_marts();
CREATE TASK ops.done FINALIZE = ops.root AS CALL ops.notify(); -- 성공, 실패와 관계없이 마지막에
SELECT SYSTEM$TASK_DEPENDENTS_ENABLE('ops.root'); -- 그래프 전체 재개- 루트 태스크에만 일정이 있고, 하위는
AFTER로 연결합니다. - 파이널라이저 태스크는 정리와 알림용입니다.
- 2026년 3월에 이전 실행이 끝나지 않았을 때의 동작을 정하는
OVERLAP_POLICY매개변수가 그래프에 추가되었습니다. - 태스크 사이에 값을 넘길 때는
SYSTEM$SET_RETURN_VALUE와SYSTEM$GET_PREDECESSOR_RETURN_VALUE를 씁니다.
실패 처리
| 설정 | 하는 일 |
|---|---|
SUSPEND_TASK_AFTER_NUM_FAILURES | 연속 실패 n 번이면 자동 일시 중지 |
TASK_AUTO_RETRY_ATTEMPTS | 실패한 그래프 실행을 자동 재시도 (기본 꺼짐) |
ERROR_INTEGRATION | 실패 알림을 SNS, Event Grid, Pub/Sub 로 |
USER_TASK_TIMEOUT_MS | 한 번 실행의 제한 시간 |
SELECT name, state, error_code, error_message, scheduled_time, completed_time
FROM TABLE(INFORMATION_SCHEMA.TASK_HISTORY(RESULT_LIMIT => 100))
WHERE state = 'FAILED'
ORDER BY scheduled_time DESC;Serverless Tasks Flex
일정 시각에 딱 맞춰 돌 필요가 없는 작업을 정해진 창(window) 안에서 Snowflake 가 고른 시각에 돌리는 방식입니다. SCHEDULING_MODE = 'FLEXIBLE' 과 TARGET_COMPLETION_INTERVAL 을 주고, 둘 다 최소 60분입니다. Service Consumption Table 에서 서버리스 배수가 일반 서버리스 태스크(0.9)보다 낮은 0.5 입니다. 2026-10 기준 PrPr 라 신청한 계정에서만 씁니다.
언제 Dynamic Tables 로 바꾸나
단순한 "변경분 읽어서 MERGE" 파이프라인이라면 Dynamic Tables 가 코드와 운영 부담이 훨씬 적습니다. 태스크는 프로시저 호출, 외부 알림, 조건 분기, 관리 작업에 남겨 둡니다.
트러블슈팅
증상: 태스크를 만들었는데 한 번도 돌지 않습니다.
원인: 태스크는 만들면 일시 중지 상태입니다. 또는 소유 역할에
EXECUTE TASK권한이 없습니다.해결:
ALTER TASK ... RESUME(그래프는SYSTEM$TASK_DEPENDENTS_ENABLE) 하고 권한을 확인합니다.증상: "Stream ... has become stale" 오류가 납니다.
원인: 보존 기간과 연장 기간이 지나도록 스트림을 소비하지 않았습니다.
해결: 스트림을 다시 만들고 빠진 구간은 Time Travel 이나 원본 비교로 보정합니다. 소비 주기를 보존 기간보다 충분히 짧게 잡습니다.