| name | starrocks-etl-and-tasks |
| description | Build StarRocks ETL pipelines and async/scheduled tasks โ daily batch loads, incremental load with deduplication, real-time aggregation pipelines, SCD Type 2, and SUBMIT TASK for background/scheduled INSERT/CTAS execution with monitoring. Use when designing StarRocks ETL workflows, running long INSERT...SELECT in the background, scheduling recurring transformations, or backfilling data. |
| license | Apache-2.0 |
| metadata | {"author":"Tiansu Yu","version":"1.0"} |
StarRocks ETL and Tasks
Design ETL pipelines in StarRocks and run them asynchronously or on a schedule with SUBMIT TASK.
When to Use
- Designing batch, incremental, or real-time ETL pipelines
- Running long INSERT/CTAS in the background (avoid client timeout)
- Scheduling recurring transformations natively in StarRocks
- Backfilling or migrating data
- Implementing SCD Type 2 dimensions
Related skills: [starrocks-data-loading] for the underlying load methods; [starrocks-materialized-views] as an alternative to scheduled aggregation tasks; [starrocks-table-design] for staging/target schemas.
ETL Pipeline Patterns
Pattern 1: Daily Batch Load from S3
LOAD LABEL mydb.load_staging_{{ ds }}
(
DATA INFILE("s3://bucket/data/{{ ds }}/*.parquet")
INTO TABLE staging_events
FORMAT AS "parquet"
)
WITH BROKER
(
"aws.s3.access_key" = "KEY",
"aws.s3.secret_key" = "SECRET",
"aws.s3.region" = "us-east-1"
)
PROPERTIES (
"timeout" = "7200"
);
INSERT INTO events PARTITION (p{{ ds_nodash }})
SELECT
event_id,
user_id,
event_type,
DATE(event_time) as event_date,
event_time,
CAST(properties['amount'] AS DECIMAL(18,2)) as amount
FROM staging_events
WHERE DATE(event_time) = '{{ ds }}';
TRUNCATE TABLE staging_events;
Pattern 2: Incremental Load with Deduplication
CREATE TABLE user_profiles (
user_id BIGINT NOT NULL,
username VARCHAR(100),
email VARCHAR(200),
status VARCHAR(20),
updated_at DATETIME
)
PRIMARY KEY (user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 16
PROPERTIES (
"enable_persistent_index" = "true"
);
curl
-H "label:user_profile_update_$(date +%s)" \
-H "format:json" \
-T incremental_users.json \
http://fe_host:8030/api/mydb/user_profiles/_stream_load
Pattern 3: Real-time Aggregation Pipeline
CREATE TABLE raw_events (
event_time DATETIME,
event_type STRING,
user_id BIGINT,
processing_time INT
)
DUPLICATE KEY (event_time)
PARTITION BY date_trunc('hour', event_time)
DISTRIBUTED BY HASH(user_id) BUCKETS 64;
CREATE ROUTINE LOAD mydb.routine_load_events ON raw_events
COLUMNS(event_time, event_type, user_id, processing_time)
PROPERTIES (
"desired_concurrent_number" = "3",
"max_batch_interval" = "20",
"format" = "json"
)
FROM KAFKA (...);
CREATE MATERIALIZED VIEW mv_events_5min
REFRESH ASYNC START('2024-01-01 00:00:00') EVERY(INTERVAL 5 MINUTE)
AS
SELECT
date_trunc('minute', event_time) as time_bucket,
event_type,
COUNT(*) as event_count,
COUNT(DISTINCT user_id) as unique_users,
AVG(processing_time) as avg_processing_time
FROM raw_events
WHERE event_time >= DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY date_trunc('minute', event_time), event_type;
SELECT * FROM mv_events_5min
WHERE time_bucket >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY time_bucket DESC;
Pattern 4: SCD Type 2 (Slowly Changing Dimension)
UPDATE only works on Primary Key tables, so the history table must be a PK table (with valid_from in the key to keep multiple versions per user_id).
Transaction caveats: Explicit transactions require v3.5+; UPDATE inside a transaction needs a shared-data cluster on v4.0+ and must precede the INSERT on the same table. On other deployments, run the two statements separately instead of wrapping them in BEGIN/COMMIT.
CREATE TABLE dim_users_history (
user_id BIGINT NOT NULL,
valid_from DATETIME NOT NULL,
username VARCHAR(100),
email VARCHAR(200),
status VARCHAR(20),
valid_to DATETIME,
is_current BOOLEAN
)
PRIMARY KEY (user_id, valid_from)
DISTRIBUTED BY HASH(user_id) BUCKETS 16;
UPDATE dim_users_history
SET valid_to = NOW(), is_current = false
WHERE user_id IN (SELECT user_id FROM staging_users)
AND is_current = true;
INSERT INTO dim_users_history
SELECT
user_id,
NOW() as valid_from,
username,
email,
status,
NULL as valid_to,
true as is_current
FROM staging_users;
SUBMIT TASK (Async Execution)
SUBMIT TASK runs ETL statements asynchronously in the background. Available since v2.5, with scheduled execution since v3.3.
When to Use SUBMIT TASK
| Scenario | Why SUBMIT TASK | Alternative |
|---|
| Long-running INSERT INTO ... SELECT | Avoids client timeout; runs in background | Synchronous INSERT (blocks session) |
| Scheduled recurring ETL (e.g. hourly aggregation) | Built-in scheduler with EVERY interval | External scheduler (Airflow, cron) |
| CTAS for large result sets | Prevents session timeout on big datasets | Manual CREATE + INSERT |
| INSERT OVERWRITE partition refresh | Fire-and-forget partition rebuilds | Synchronous OVERWRITE (blocks) |
| Backfill / data migration | Submit multiple tasks and monitor via metadata | Sequential INSERT statements |
Use SUBMIT TASK when:
- The operation would exceed your session's
query_timeout
- You want StarRocks-native scheduling without external orchestrators
- You need to fire-and-forget and check status later
- You're running multiple independent ETL steps in parallel
Don't use SUBMIT TASK when:
- You need the result immediately in the same session
- The operation finishes in seconds (overhead not worth it)
- You already have Airflow/dbt orchestrating the pipeline (use those instead)
Syntax
SUBMIT TASK <task_name>
[SCHEDULE [START(<schedule_start>)] EVERY(INTERVAL <schedule_interval>)]
[PROPERTIES("key" = "value" [, ...])]
AS <etl_statement>
Supported ETL statements:
CREATE TABLE AS SELECT (v3.0+)
INSERT INTO / INSERT OVERWRITE (v3.0+)
CACHE SELECT (v3.3+)
One-off Background Task
SUBMIT TASK backfill_2024
AS
INSERT INTO target_table
SELECT * FROM source_table
WHERE dt >= '2024-01-01' AND dt < '2025-01-01';
Scheduled Recurring Task
SUBMIT TASK refresh_5min_agg
SCHEDULE EVERY(INTERVAL 5 MINUTE)
AS
INSERT OVERWRITE hourly_metrics
SELECT
date_trunc('hour', event_time) AS hour,
event_type,
COUNT(*) AS cnt,
SUM(amount) AS total
FROM raw_events
WHERE event_time >= DATE_SUB(NOW(), INTERVAL 2 HOUR)
GROUP BY date_trunc('hour', event_time), event_type;
Scheduled Task with Start Time
SUBMIT TASK daily_summary
SCHEDULE START('2024-01-01 00:00:00') EVERY(INTERVAL 1 DAY)
AS
INSERT OVERWRITE daily_revenue PARTITION (dt)
SELECT
DATE(order_time) AS dt,
store_id,
SUM(amount) AS revenue
FROM orders
WHERE order_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND order_time < CURDATE()
GROUP BY DATE(order_time), store_id;
Task with Session Properties
SUBMIT TASK heavy_etl
PROPERTIES(
"session.insert_timeout" = "36000",
"session.enable_profile" = "true"
)
AS
INSERT INTO warehouse.fact_orders
SELECT * FROM staging.raw_orders;
CTAS as Background Task
SUBMIT TASK create_snapshot
AS
CREATE TABLE snapshot_20240101
AS SELECT * FROM production_table
WHERE dt = '2024-01-01';
Monitoring Tasks
SELECT task_name, `schedule`, create_time, definition
FROM information_schema.tasks;
SELECT task_name, state, create_time, finish_time, error_message
FROM information_schema.task_runs
WHERE task_name = 'daily_summary'
ORDER BY create_time DESC
LIMIT 10;
SELECT task_name, state, error_message, create_time
FROM information_schema.task_runs
WHERE state = 'FAILED'
ORDER BY create_time DESC;
Drop a Task
DROP TASK `daily_summary`;
FE Configuration Tuning
| Parameter | Default | Description |
|---|
task_runs_concurrency | 4 | Max parallel TaskRuns |
task_runs_queue_length | 500 | Max pending TaskRuns |
task_runs_ttl_second | 604800 | TTL for TaskRun records (seconds; 7 days) |
task_ttl_second | 86400 | TTL for one-off Task templates (seconds) |
task_min_schedule_interval_s | 10 | Minimum schedule interval (seconds) |