Kenneth Oh
08/17/2026, 4:24 PMRocky
08/17/2026, 4:24 PMprod_app_user queries are not blocked by a backlog of prod_etl_user queries, you should use Exclusive Resource Groups and Group-level Query Queuing.
In StarRocks, there is no "priority" flag to let a query jump to the front of a single queue. Instead, priority is handled by isolating the queues and resources so that the application user never has to wait for the ETL user.
Recommended Configuration Strategy
1. Use exclusive_cpu_cores for Reserved Capacity
By setting exclusive_cpu_cores, you create a hard isolation. The cores assigned to prod_app_user are physically bound to a separate thread pool. Even if prod_etl_user submits thousands of queries, they cannot touch the cores reserved for the app user.
* prod_app_user group: Set exclusive_cpu_cores = N. These cores are "reserved" and only used by this group.
* prod_etl_user group: Use cpu_weight (Shared Group). These queries will run on the remaining cores not reserved by exclusive groups.
2. Enable Group-level Query Queuing
By default, queries may be subject to a global queue. To ensure the ETL backlog doesn't stall the App queries, you must enable Resource Group-level queuing (available since v3.1.4). This ensures each group has its own "waiting line."
sql
-- Enable this globally to isolate the queues
SET GLOBAL enable_group_level_query_queue = true;
3. Example Resource Group Setup
sql
-- 1. Group for App Users (High Priority / Reserved)
CREATE RESOURCE GROUP app_wg
TO (user='prod_app_user')
WITH (
"exclusive_cpu_cores" = "4", -- Adjust based on your BE core count
"concurrency_limit" = "20", -- App queries can always start if under this limit
"mem_limit" = "40%"
);
-- 2. Group for ETL Users (Lower Priority / Shared)
CREATE RESOURCE GROUP etl_wg
TO (user='prod_etl_user')
WITH (
"cpu_weight" = "2", -- Shares the remaining non-exclusive cores
"concurrency_limit" = "10", -- Limits ETL parallelism to prevent memory exhaustion
"mem_limit" = "40%"
);
How this behaves in your scenario:
* Capacity Reservation: The prod_app_user always has 4 cores (in the example) waiting for it. Even if the ETL group is 100% loaded, it cannot "steal" these cores unless you explicitly enable enable_resource_group_cpu_borrowing (which is true by default, but App queries will immediately reclaim them when they arrive).
* Priority in Queuing: Because enable_group_level_query_queue is on, if prod_etl_user submits 100 queries but has a concurrency_limit of 10, 90 queries will sit in the etl_wg queue. When prod_app_user submits a query, it checks the app_wg queue. Since that queue is likely empty, the App query starts immediately using its reserved cores, "skipping" the 90 ETL queries entirely.
* If you do NOT use exclusive cores: If both use cpu_weight, they share the same pool of cores. The prod_app_user will get a larger slice of CPU time (if its weight is higher), but it might still experience slightly higher latency if the CPU is heavily over-saturated by the ETL tasks.
Summary of required variables
Ensure these are set to enable the logic above:
* SET GLOBAL enable_pipeline_engine = true; (Required for resource groups)
* SET GLOBAL enable_group_level_query_queue = true; (Required for independent queues)
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.mdKenneth Oh
08/17/2026, 4:42 PMRocky
08/17/2026, 4:42 PMcpu_weight = 2 for a group like etl_wg, it determines that group's proportional share of the "Shared CPU Pool."
Here is exactly how that weight behaves against other groups:
1. The "Shared CPU Pool"
The total CPU capacity on a BE node is split into two pools:
* Exclusive Pool: Cores reserved by groups using exclusive_cpu_cores (like your app_wg).
* Shared Pool: All remaining cores. This is where cpu_weight applies.
2. Relative Proportions
The cpu_weight is not a fixed number of cores; it is a numerator in a fraction. A group’s share of the Shared Pool is calculated as:
$$\text{Group Share} = \frac{\text{Group's cpu\_weight}}{\sum(\text{cpu\_weights of all active groups in the Shared Pool})}$$
Example Scenario:
If you have a BE with 16 cores and you reserved 4 for app_wg, you have 12 cores left in the Shared Pool.
* etl_wg: cpu_weight = 2
* default_wg: (The system default group where other queries go). Its default weight is usually the total number of cores on the BE (e.g., 16).
* Result: If both are fully loaded, etl_wg gets $2 / (2 + 16) \approx 11\%$ of those 12 shared cores.
3. Soft Isolation (Borrowing)
* Under Load: If every group is busy, StarRocks enforces the weights strictly. The ETL group will be throttled to its 11% share so other groups aren't starved.
* Idle System: If the default_wg is idle, the etl_wg can "burst" and use up to 100% of the shared cores. It is only restricted when there is competition.
* Borrowing from Exclusive: By default (enable_resource_group_cpu_borrowing = true), the ETL group can even use the 4 cores reserved for app_wg if that group is currently idle. However, the moment a prod_app_user query arrives, the ETL group must immediately give those 4 cores back.
4. Comparison with default_wg
It is important to check the weight of your default_wg.
* If default_wg has a weight of 16 and your ETL group has a weight of 2, the ETL group is actually "lower priority" than the default group.
* If you want prod_etl_user to be the lowest priority on the system, ensure its cpu_weight is the smallest number among all your defined groups (the minimum is typically 1).
Summary of Behavior
Feature | Behavior for cpu_weight = 2
Share | Gets a slice of CPU relative to the weights of other active groups.
Constraint | Only throttled when the system is under contention.
Isolation | Soft isolation. It can use more than its share if other groups are quiet.
Comparison | If another group has cpu_weight = 10, it will get 5x more CPU time than the ETL group when both are running.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.mdKenneth Oh
08/17/2026, 4:44 PMRocky
08/17/2026, 4:44 PMuser classifier does not support a comma-separated list of names within a single set of parentheses. Unlike the db classifier, the user classifier expects a single string per entry.
To include multiple users in one resource group, you must provide multiple classifier blocks separated by commas in the TO clause.
Correct Syntax
sql
CREATE RESOURCE GROUP app_wg
TO
(user='user1'),
(user='user2')
WITH (
"exclusive_cpu_cores" = "4",
"mem_limit" = "40%"
);
Why this happens
* user and `role`: These classifiers match a single entity. If you want to match multiple specific users, you list them as separate classifiers.
* `db`: This is the only classifier that explicitly supports a comma-separated string (e.g., db='db1,db2').
Alternative: Using Roles
If you have many users, it is often easier to assign them to a Role and use the role classifier instead:
sql
-- 1. Create a role and assign users to it
CREATE ROLE app_role;
GRANT app_role TO user1, user2;
-- 2. Use the role in the resource group
CREATE RESOURCE GROUP app_wg
TO (role='app_role')
WITH (...)
References
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• administration/management/resource_management/resource_group.md
References
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• faq/resource_isolation_faq.md
• administration/management/resource_management/resource_group.mdKenneth Oh
08/18/2026, 7:54 PMRocky
08/18/2026, 7:54 PMapp_wg queries are worked on immediately and are never delayed by other workloads, you should use exclusive_cpu_cores.
While cpu_weight gives a group a larger slice of the CPU, only exclusive_cpu_cores provides the physical isolation required for consistent, immediate execution.
Comparison: Why Exclusive is better for "Immediate" Work
Feature | cpu_weight (Shared) | exclusive_cpu_cores (Exclusive)
Execution Pool | Queries share a common thread pool (pip_exec_com) with all other groups. | Queries run in a dedicated thread pool (pip_exec_<id>) bound to specific cores.
Scheduling | Your query must wait for a "time slice" or a free thread in the shared pool. | Your query starts on its own reserved cores immediately, regardless of what the shared pool is doing.
Impact of High Load | If the system is flooded with ETL tasks, your "high weight" query still faces scheduling jitter. | Your query is physically invisible to the ETL workload; it has its own "private lane."
Predictability | Latency can fluctuate based on how many other queries are "competing" for cycles. | Latency is highly stable because the hardware is reserved.
How to achieve "Zero Wait" execution
To get the behavior you described, use this configuration:
1. Define Exclusive Cores: This carves out a piece of the BE's CPU specifically for the app.
sql
CREATE RESOURCE GROUP app_wg
TO (user='prod_app_user')
WITH (
"exclusive_cpu_cores" = "8", -- Reserves 8 physical cores
"mem_limit" = "40%"
);
2. Enable Group-level Queuing: This is critical. Without this, if the cluster hits a global query limit, your App user might still sit in a queue behind ETL users.
sql
SET GLOBAL enable_group_level_query_queue = true;
3. Understand "CPU Borrowing":
By default, StarRocks allows other groups to "borrow" your 8 exclusive cores if app_wg is idle (enable_resource_group_cpu_borrowing = true).
* The benefit: Resources aren't wasted when your app is quiet.
* The behavior: When an app_wg query arrives, the system reclaims those cores. This reclamation is fast (tens of milliseconds), but if you need absolute zero-jitter timing, you can disable borrowing:
sql
-- Run on BEs to prevent others from ever touching app_wg cores
UPDATE information_schema.be_configs SET VALUE = "false" WHERE NAME = "enable_resource_group_cpu_borrowing";
Summary
If your goal is to ensure the app workload is never dominated and starts immediately, exclusive_cpu_cores is the correct tool. cpu_weight is better suited for balancing throughput between background tasks rather than protecting a latency-sensitive application.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.mdKenneth Oh
09/02/2026, 12:46 AMRocky
09/02/2026, 12:46 AMexclusive_cpu_cores, your queries may still "wait" because StarRocks resource groups currently only isolate CPU and Memory. Several other critical resources are shared across the entire cluster and can cause bottlenecks that CPU isolation cannot prevent.
1. I/O Is Not Isolated
StarRocks does not currently support I/O isolation (neither for local disk nor for object storage like S3).
* The Bottleneck: If a background resource group (like a heavy ETL job) is saturating the BE’s network bandwidth or disk I/O, your app_wg queries will be stuck waiting for data to be read from S3/disk, even though their "private" CPU cores are idle.
* S3 Table Buckets: If you are using many tablets (buckets), each scan generates a high volume of GET requests. S3 has a hard limit of 5,500 GET requests per second per prefix. If multiple resource groups are scanning the same bucket/prefix simultaneously, they will hit these rate limits together, causing S3 to throttle all of them (often seen as "SlowDown" errors or high latency in the Query Profile).
2. Other Non-Isolated Resources
Besides I/O, these resources are shared and can cause "waiting":
* Network Bandwidth: The physical NIC on the BE is shared. Data shuffle (Exchange nodes) between BEs for one group can starve the network for another.
* FE Metadata & Planning: Every query must be planned by the Frontend (FE). If the FE is overloaded by thousands of small queries from a background group, the app_wg query will "wait" in the planning phase before it even reaches the BEs.
* Global Query Queue: If you have enable_query_queue = true, a query might be queued at the cluster level due to total BE memory or CPU usage, regardless of your exclusive group settings.
3. How to Identify the "Wait"
To determine why your exclusive cores aren't helping, run your query and check the Query Profile (using get_query_profile('query_id')):
* If WaitTime or QueuedTime is high: The query is stuck in a StarRocks queue. Check your concurrency_limit for the resource group or global query_queue settings.
* If ScanTime is high but CPUUsage is low: You are hitting I/O or S3 bottlenecks. The CPU is literally waiting for the network/S3 to deliver data.
* Check S3 Metrics: If you see a spike in "S3 SlowDown" errors in your storage monitoring, you are hitting S3 rate limits.
Recommended Fixes
* Enable Data Cache: Using the StarRocks Data Cache (on local NVMe) can significantly reduce S3 I/O pressure. If the data is in the cache, the app_wg won't have to go to S3, avoiding the shared I/O bottleneck.
* Check Concurrency: Ensure the concurrency_limit in your app_wg isn't set too low, causing queries to queue internally.
* S3 Path Strategy: If you suspect S3 rate limits, consider using different prefixes or buckets for different high-traffic tables to distribute the 5,500 req/s load.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• administration/management/resource_management/query_queues.md
References
• faq/resource_isolation_faq.md
• administration/management/resource_management/resource_group.md
• administration/management/resource_management/query_queues.mdKenneth Oh
09/02/2026, 12:56 AMRocky
09/02/2026, 12:56 AMbe.conf):
* `datacache_enable = true`: Enables the feature.
* `datacache_disk_size`: Sets the total disk space for the cache (e.g., 214748364800 for 200GB).
* Session Variables:
* `SET enable_scan_datacache = true;`: Ensures the current session uses the cache.
* `SET populate_datacache_mode = 'always';`: Forces the query to populate the cache if data is missing.
4. Monitoring the Cache
You can verify if your Iceberg queries are actually hitting the cache by checking the Query Profile:
Look for the following metrics under the ScanNode section:
* `DataCacheReadBytes`: Amount of data read from the local cache.
* `DataCacheWriteBytes`: Amount of data written to the cache (on a miss).
* `DataCacheReadCounter`: Number of blocks hit in the cache.
Summary of Benefits
By using the Data Cache, you get MV-like performance for hot data without the storage overhead or management complexity of a Materialized View, while still maintaining the flexibility of querying the "source of truth" in Iceberg.
References
• data_source/data_cache.md
• data_source/catalog/iceberg/iceberg_catalog.md
• sql-reference/System_variable.md
References
• administration/management/BE_configuration.md
• using_starrocks/caching/query_cache.md
• data_source/data_cache.md
• using_starrocks/caching/block_cache.md
• sql-reference/System_variable.mdKenneth Oh
09/02/2026, 12:58 AMRocky
09/02/2026, 12:58 AMiceberg_table_cache_refresh_interval_sec).
* Within 60s: StarRocks uses the cached snapshot immediately.
* After 60s: If a query arrives, StarRocks uses the cached snapshot for the current query but triggers an asynchronous refresh to check the Metastore for a new snapshot ID for subsequent queries.
* Background Refresh: There is also a global background thread that polls for updates every 10 minutes (default) for any table accessed in the last 24 hours.
Summary for Snapshots: StarRocks does not check the Metastore for a new snapshot for every query by default. It relies on a "lazy" refresh policy to maintain high performance. If you need absolute 1:1 consistency, you can set the catalog property iceberg_meta_cache_ttl_sec to 0.
2. Data Layer: Are the Blocks Valid?
Once the FE has identified the specific Parquet/ORC files for a snapshot, the BE checks its Data Cache (Block Cache) for those files.
* Immutability Advantage: Iceberg files are immutable. When data is updated, Iceberg creates new files rather than overwriting old ones.
* Validation Logic: StarRocks identifies cached blocks using a key composed of: File Path + File Size + Modification Time.
* Validity Check: Because Iceberg files never change once written, if the FE tells the BE to read file_A.parquet (which it found in the latest snapshot), and the BE has blocks for file_A.parquet in its cache, those blocks are guaranteed to be valid.
Why you might see "Stale" data
If you update an Iceberg table and StarRocks still shows old data, it is almost always because the FE metadata cache is still pointing to an old Snapshot ID, not because the data blocks themselves are "corrupt" or invalid.
How to Force a Refresh
If you know the data has changed and you want to bypass the cache:
1. Manual Refresh: Run REFRESH EXTERNAL TABLE iceberg_db.table_name; to force the FE to pull the latest snapshot immediately.
2. Disable Metadata Cache (Session): SET enable_iceberg_metadata_cache = false; forces the query to fetch the latest metadata from the Metastore.
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/feature-support-data-lake-analytics.md
• data_source/datalake_faq.md
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/feature-support-data-lake-analytics.mdKenneth Oh
09/02/2026, 1:00 AMRocky
09/02/2026, 1:00 AMiceberg_table_cache_refresh_interval_sec
Increase this (default 60s) to a very high value (e.g., 3600 for 1 hour). This prevents StarRocks from even trying to check if a new Iceberg snapshot exists for that duration.
sql
ALTER CATALOG iceberg_catalog SET ("iceberg_table_cache_refresh_interval_sec" = "3600");
* FE Configuration: background_refresh_metadata_interval_millis
Increase this (default 10 minutes) to reduce background polling of the metastore.
sql
ADMIN SET FRONTEND CONFIG ("background_refresh_metadata_interval_millis" = "3600000"); -- 1 hour
* Enable Metadata Disk Cache (v3.3+)
This allows metadata to persist across FE restarts.
sql
ADMIN SET FRONTEND CONFIG ("enable_iceberg_metadata_disk_cache" = "true");
* Disable Column Stats Retrieval
If you don't need exact min/max for every query, disabling column stats retrieval speeds up the planning phase by reading less metadata.
sql
SET enable_get_stats_from_external_metadata = false;
2. Data Caching (BE/CN Level)
This ensures the actual data blocks remain on your local NVMe disks, bypassing S3 entirely for "hot" data.
* Session Variable: populate_datacache_mode
Set this to always to ensure that any data not already in the cache is immediately written there during the first read.
sql
SET populate_datacache_mode = 'always';
* BE Config: datacache_disk_size
Allocate as much local SSD/NVMe space as possible. If your cache is too small, StarRocks will evict data (LRU), forcing expensive S3 reads.
* BE Config: datacache_block_size
The default is 1MB. For large sequential Iceberg scans, this is usually optimal, but ensure your storage_root_path in be.conf points to your fastest local disks.
3. Execution Strategy
* Session Variable: plan_mode
If you have many small manifest files and high concurrency, set this to local. If you have a few massive manifest files, set it to distributed. The default auto is usually good, but local is fastest for metadata-heavy small queries.
sql
SET plan_mode = 'local';
* Session Variable: enable_bucket_aware_execution_on_lake
Ensure this is true (default in v4.0+) to allow StarRocks to optimize joins and aggregations based on Iceberg's bucketing/partitioning, reducing data shuffle.
Recommended "Fastest" Session Settings:
Run these before your performance-critical workloads:
sql
SET enable_scan_datacache = true;
SET populate_datacache_mode = 'always';
SET plan_mode = 'local';
SET enable_get_stats_from_external_metadata = false;
Summary of Trade-offs
By using these settings, StarRocks will effectively treat your Iceberg table as a "local" table. It will only hit S3 when a cache miss occurs, and it will only hit the Metastore once per hour (or whatever interval you set), resulting in sub-second latencies for repeated queries.
References
• data_source/catalog/iceberg/iceberg_catalog.md
• sql-reference/System_variable.md
• using_starrocks/caching/block_cache.md
References
• sql-reference/System_variable.md
• data_source/catalog/iceberg/iceberg_catalog.mdKenneth Oh
09/02/2026, 1:24 AMRocky
09/02/2026, 1:24 AMSET plan_mode = 'distributed';
Forces the FE to distribute the work of parsing Iceberg manifest files across all BE/CN nodes. This is significantly faster for tables with thousands of manifests.
* SET enable_iceberg_metadata_cache = true;
Reuses previously parsed metadata objects.
* SET enable_get_stats_from_external_metadata = false;
Skips fetching detailed column statistics (min/max/null counts) from Iceberg if they aren't strictly needed for your query, saving metadata I/O.
2. Execution Parallelism (CPU & Pipeline)
StarRocks uses a Pipeline Engine to execute queries. You can increase the "Degree of Parallelism" (DOP) to use more CPU cores for a single query.
* SET pipeline_dop = <number_of_physical_cores>;
By default, this is 0 (adaptive, usually half of CPU cores). For maximum speed on a single query, set it equal to the total number of physical CPU cores on your BE/CN nodes.
* SET enable_shared_scan = true;
Allows multiple operators to share the same scan results, reducing redundant work when multiple joins or subqueries access the same Iceberg table.
3. Scan & I/O Concurrency
This controls how many concurrent requests are sent to the storage layer (or Data Cache).
* SET enable_connector_adaptive_io_tasks = false;
Disables the automatic throttle.
* SET connector_io_tasks_per_scan_operator = 32;
Increases the number of concurrent I/O tasks per scan instance (default is 16). Increase this if you have high-performance NVMe disks for your Data Cache.
* max_hdfs_scanner_num (BE Config in be.conf):
Default is 50. Increase this (e.g., to 100 or 200) to allow more concurrent connector scan tasks to run globally on a BE node.
* pipeline_connector_scan_thread_num_per_cpu (BE Config):
Default is 8. This multiplies with your CPU count to determine the total thread pool size for scanning external data.
4. Maximizing Data Cache Throughput
If you are reading from the Data Cache (local disk), the bottleneck shifts from network latency to local disk I/O and memory bandwidth.
* SET populate_datacache_mode = 'always';
Ensures that every block read is immediately cached.
* Multiple Cache Disks:
Ensure your storage_root_path in be.conf points to multiple NVMe SSDs (separated by commas). StarRocks will strip data across them, naturally increasing parallel I/O throughput.
* Example: storage_root_path = /nvme1/data, /nvme2/data, /nvme3/data
Recommended "Aggressive" Session Profile
Use these settings for a high-parallelism, high-performance "hot" data read:
sql
-- Parallelism Settings
SET pipeline_dop = 32; -- Set to your BE core count
SET plan_mode = 'distributed';
SET connector_io_tasks_per_scan_operator = 32;
SET enable_connector_adaptive_io_tasks = false;
-- Cache & Staleness Settings
SET enable_scan_datacache = true;
SET populate_datacache_mode = 'always';
SET enable_iceberg_metadata_cache = true;
SET enable_get_stats_from_external_metadata = false;
-- Catalog Staleness (allows 1-hour stale metadata)
ALTER CATALOG iceberg_catalog SET ("iceberg_table_cache_refresh_interval_sec" = "3600");
Note: Setting pipeline_dop and connector_io_tasks to very high values will make a single query very fast but will consume significantly more CPU and Memory, reducing the number of concurrent users the cluster can support.
References
• sql-reference/System_variable.mdRocky
09/02/2026, 1:24 AMKenneth Oh
09/02/2026, 1:27 AMRocky
09/02/2026, 1:27 AMbe.conf or cn.conf)
The r8gd.4xlarge has a single 950GB NVMe. You must point StarRocks to this disk for the Data Cache to bypass S3/HDFS.
bash
# Point to your NVMe mount point (e.g., /mnt/nvme)
storage_root_path = /mnt/nvme
# Allocate ~85% of the disk to Data Cache (approx 800GB)
datacache_disk_size = 858993459200
# Increase global scan threads for 16 vCPUs
# Default is 40, increase to handle more parallel I/O tasks
pipeline_scan_thread_pool_thread_num = 64
*Note: Ensure you are using the StarRocks ARM64 (Aarch64) binary for Graviton4 optimization.*
2. Session Variables for Maximum Parallelism
Since each node has 16 vCPUs, you want your queries to utilize them fully. Run these before your heavy Iceberg workloads:
sql
-- Use all physical threads on the node for one query
SET pipeline_dop = 16;
-- Drive high I/O concurrency to the local NVMe
SET connector_io_tasks_per_scan_operator = 32;
SET enable_connector_adaptive_io_tasks = false;
-- Distributed metadata planning is faster for large Iceberg tables
SET plan_mode = 'distributed';
-- Always pull data into the NVMe on the first read
SET populate_datacache_mode = 'always';
-- Disable stats to shave off planning time
SET enable_get_stats_from_external_metadata = false;
3. Catalog-Level "Forever Cache"
To make stale data acceptable and eliminate Metastore (Glue/HMS) latency, set a very long refresh interval on your catalog.
sql
ALTER CATALOG <your_iceberg_catalog> SET (
-- Cache metadata for 24 hours to ensure zero Metastore hits
"iceberg_table_cache_refresh_interval_sec" = "86400",
-- Enable metadata disk cache if using StarRocks 3.3+
"enable_iceberg_metadata_disk_cache" = "true"
);
Why this works for your hardware:
* Pipeline DOP 16: Matches your vCPU count per node. This ensures the CPU is never the bottleneck during complex aggregations on your Iceberg data.
* NVMe Throughput: The r8gd NVMe can handle over 250,000 IOPS. Increasing connector_io_tasks to 32 ensures you are keeping the NVMe queue depth high enough to reach its max throughput.
* Memory Efficiency: With 128GB per node, you have 8GB per vCPU. This is a "sweet spot" for StarRocks, allowing you to run these high-parallelism settings without hitting OOM (Out of Memory) errors during large shuffles.
Pro-Tip: Warm up the cache
If performance on the first query is critical, you can manually trigger a cache "warm-up" so the NVMe is pre-loaded:
sql
-- Forces a full scan to populate the NVMe cache
SELECT count(*) FROM <your_iceberg_table>;
References
• administration/management/BE_configuration.md
• sql-reference/System_variable.md
• data_source/catalog/iceberg/iceberg_catalog.mdKenneth Oh
09/02/2026, 1:37 AMRocky
09/02/2026, 1:37 AMsql
CREATE MATERIALIZED VIEW iceberg_speed_mv
REFRESH ASYNC EVERY (INTERVAL 10 MINUTE) -- Define your "acceptable staleness" here
AS
SELECT ... FROM iceberg_catalog.db.table;
2. Iceberg Variant Type Support
If your data lake has JSON or semi-structured data, 4.1 introduces native support for the Iceberg Variant type.
* Performance: Previously, JSON was often stored as a STRING, which required expensive parsing during queries. Variant is a binary format that StarRocks 4.1 processes directly in its vectorized engine.
* Action: If you are using Iceberg v3, ensure your semi-structured columns use the VARIANT type for a massive speedup in parallelism and CPU efficiency.
3. Iceberg v3 Row Lineage Columns
If you need to track data changes for high-performance incremental processing, 4.1 adds support for Iceberg v3 Row Lineage.
* Setting: You can now query virtual columns like _row_id and _last_updated_sequence_number.
* Use Case: This allows you to build high-performance "delta" pipelines that only process changed rows, rather than full table scans.
4. Optimized Cache Lookup (CachingIcebergCatalog)
In 4.1, the internal CachingIcebergCatalog has been optimized for even lower latency when checking for metadata updates.
* Refresh Interval: To maximize this, keep your catalog refresh interval high (as previously suggested) so StarRocks bypasses the Metastore entirely.
sql
ALTER CATALOG iceberg_catalog SET ("iceberg_table_cache_refresh_interval_sec" = "86400"); -- 24 hours
5. Host-Level Sorting for Iceberg Sinks
While your question is about reads, StarRocks 4.1 added host-level sorting when writing back to Iceberg.
* Impact on Reads: If you use StarRocks to write or "compact" your Iceberg tables, this setting ensures the data is perfectly organized for StarRocks' own scan engine, leading to significantly higher scan parallelism and better min/max pruning on your reads.
6. Caution: Shared Scan
In version 4.0/4.1, the shared_scan feature (which reduces redundant scans for multiple queries) was temporarily disabled in some configurations due to a transition to a new event-based scheduler.
* Check: If you were using SET enable_shared_scan = true;, monitor your query profiles in 4.1 to see if it provides the expected benefit, as it may be automatically bypassed depending on your specific sub-version.
Summary Checklist for 4.1 on r8gd nodes:
1. Use Incremental MVs to turn your S3-based Iceberg data into local SR-format data on your NVMe disks.
2. Enable Variant types if you have semi-structured data.
3. Ensure plan_mode = 'distributed' to leverage all 16 nodes for metadata processing.
4. Set iceberg_table_cache_refresh_interval_sec to a very high value to lock in your metadata state and prevent S3/Metastore "checks" during queries.
References
• data_source/catalog/iceberg/iceberg_catalog.mdRocky
09/02/2026, 1:37 AMKenneth Oh
09/02/2026, 1:57 AMRocky
09/02/2026, 1:58 AMINCREMENTAL refresh mode.
Unlike "Partitioned Refresh" (which requires a date/time column to track changes), the INCREMENTAL mode uses Iceberg's snapshot/metadata features to identify and process only the changed rows.
1. The INCREMENTAL Refresh Mode (StarRocks 4.1+)
This is the most direct answer to your question. You can create an MV that stays updated row-by-row or batch-by-batch without needing a PARTITION BY clause in the MV definition.
How to set it up:
sql
CREATE MATERIALIZED VIEW iceberg_dim_mv
REFRESH ASYNC EVERY (INTERVAL 10 MINUTE)
PROPERTIES (
"refresh_mode" = "INCREMENTAL"
)
AS
SELECT id, name, category, updated_at
FROM iceberg_catalog.db.dim_table;
Key Requirements for INCREMENTAL mode in 4.1:
* Source Table: Currently only supported for Iceberg and Paimon catalogs.
* Append-Only: The source Iceberg table must primarily use append-only operations. If StarRocks detects an UPDATE, DELETE, or OVERWRITE on the source table:
* If refresh_mode is set to INCREMENTAL, the refresh task will fail.
* If refresh_mode is set to AUTO (default), StarRocks will automatically fall back to a full refresh to ensure data consistency.
* Supported SQL: It supports SELECT, FILTER, INNER JOIN, and AGGREGATE with GROUP BY. It does not yet support OUTER JOINs in the incremental path.
──────────
2. Handling Dimension Tables in Multi-Table MVs
If you are joining a large fact table with these dimension tables, you likely don't want an update to a small dimension table to trigger a massive refresh of the entire MV.
You can use excluded_trigger_tables to decouple the refresh logic:
sql
CREATE MATERIALIZED VIEW fact_with_dim_mv
REFRESH ASYNC
PROPERTIES (
-- Changes to the 'dim_table' will NOT trigger a refresh
-- Only changes to the 'fact_table' will trigger it
"excluded_trigger_tables" = "iceberg_catalog.db.dim_table"
)
AS
SELECT f.*, d.name
FROM iceberg_catalog.db.fact_table f
JOIN iceberg_catalog.db.dim_table d ON f.dim_id = d.id;
──────────
3. Using "Staleness" for Performance
Since you mentioned you accept stale data, you should combine the above with Staleness Rewrite. This allows your queries to hit the MV even if the dimension table has changed slightly in the background, preventing the query from falling back to the slower Iceberg scan.
sql
ALTER MATERIALIZED VIEW iceberg_dim_mv SET PROPERTIES (
"mv_rewrite_staleness_second" = "3600" -- Allow using the MV even if it's 1 hour out of sync
);
Summary Recommendation for 4.1:
1. For Dimension tables: Use refresh_mode = 'INCREMENTAL'. This works without partitioning.
2. For joins: Use excluded_trigger_tables so that "dimension bloat" doesn't force your main fact MVs to re-run from scratch constantly.
3. Check your operators: If your dimension logic uses LEFT JOIN, StarRocks 4.1 will fall back to full refresh. For incremental, stick to INNER JOIN or simple SELECT filters.
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/02/2026, 2:02 AMRocky
09/02/2026, 2:03 AMrefresh_mode = 'INCREMENTAL' property for asynchronous MVs currently only supports append-only operations on the source Iceberg table.
* Why: Incremental refresh works by tracking new files/snapshots. If a "Merge" happens, it creates row-level deletes or replaces existing data. The current incremental engine cannot yet "re-calculate" just the changed rows in a non-partitioned table when deletes are involved.
* Result: If you set refresh_mode = 'INCREMENTAL', the refresh task will fail when it detects a merge. If set to AUTO, it will automatically fall back to a Full Refresh.
2. Why "Full Refresh" is actually okay for your hardware
With 16 r8gd.4xlarge nodes (256 vCPUs), a "Full Refresh" of a dimension table is likely much faster than you expect. StarRocks is exceptionally good at "Full Overwrites" into local disks.
For example, a 100-million-row dimension table can often be fully refreshed from Iceberg into a StarRocks MV in under 30 seconds on a cluster of your size.
How to optimize the Full Refresh for Dimension Tables:
sql
CREATE MATERIALIZED VIEW iceberg_dim_mv
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
-- Allow the MV to be used for 1 hour even if the base table changed
"mv_rewrite_staleness_second" = "3600",
-- Ensure the refresh doesn't hog all cluster resources
"colocate_with" = "group_dim"
)
AS
SELECT * FROM iceberg_catalog.db.dim_table;
3. Better Alternative: StarRocks Primary Key Tables
If your goal is to have a "live" version of your Iceberg dimension table inside StarRocks that handles merges efficiently, you should use a Primary Key (PK) table instead of a Materialized View.
Why PK Tables are better for Merges:
1. Native Merge Support: PK tables are designed specifically for continuous UPSERT and DELETE operations.
2. No Full Overwrites: You can sync only the rows that changed in Iceberg (if you have a last_updated column).
3. Memory-Speed Lookups: PK tables use a primary key index that makes dimension joins (shuffles) significantly faster than querying Iceberg directly.
How to implement this:
1. Create the PK table in StarRocks:
sql
CREATE TABLE starrocks_dim (
id BIGINT PRIMARY KEY,
name STRING,
updated_at DATETIME
) ENGINE=OLAP
DISTRIBUTED BY HASH(id);
2. Sync from Iceberg (using a simple SQL task or Airflow):
sql
-- This will perform a high-speed upsert of only the latest changes
INSERT INTO starrocks_dim
SELECT * FROM iceberg_catalog.db.dim_table
WHERE updated_at > (SELECT MAX(updated_at) FROM starrocks_dim);
Summary for your 16-node cluster:
* Don't use refresh_mode = 'INCREMENTAL' for these tables; it will fail due to the merges.
* Use Full Refresh (AUTO) if the dimension tables are small to medium sized. Your Graviton4 nodes will handle the scan/write with ease.
* Use Primary Key tables if you want to sync large dimension tables without doing a full overwrite every time.
* Set mv_rewrite_staleness_second to ensure your queries always hit the fast local data, even while a refresh is happening in the background.
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• table_design/table_types/primary_key_table.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
ReferencesKenneth Oh
09/02/2026, 2:45 AMfailed after 1 times, try lock failed: 0, error-msg : com.starrocks.sql.common.DmlException: Insert has filtered data, txn_id = 1990789, tracking sql = select tracking_log from information_schema.load_tracking_logs where job_id=4286258
at com.starrocks.load.InsertOverwriteJobRunner.executeInsert(InsertOverwriteJobRunner.java:404)
at com.starrocks.load.InsertOverwriteJobRunner.doLoad(InsertOverwriteJobRunner.java:183)
at com.starrocks.load.InsertOverwriteJobRunner.handle(InsertOverwriteJobRunner.java:163)
at com.starrocks.load.InsertOverwriteJobRunner.transferTo(InsertOverwriteJobRunner.java:229)
at com.starrocks.load.InsertOverwriteJobRunner.prepare(InsertOverwriteJobRunner.java:282)
at com.starrocks.load.InsertOverwriteJobRunner.handle(InsertOverwriteJobRunner.java:160)
at com.starrocks.load.InsertOverwriteJobRunner.run(InsertOverwriteJobRunner.java:148)
at com.starrocks.load.InsertOverwriteJobMgr.executeJob(InsertOverwriteJobMgr.java:84)
a…Rocky
09/02/2026, 2:45 AMINSERT OVERWRITE internally) encountered "bad" data that does not fit the schema of your Materialized View.
By default, StarRocks operates in Strict Mode for MV refreshes. If even a single row of data is "filtered" (fails to be processed), the entire refresh job is aborted to prevent data corruption or silent loss.
1. How to find the "Bad" Data
The error message actually tells you exactly how to find the problem. You need to run the "tracking SQL" provided in your error log (replace the job_id with yours):
sql
SELECT tracking_log
FROM information_schema.load_tracking_logs
WHERE job_id = 4286258;
Common results you will see in that log:
* column [name] value is too long: A string in your source table exceeds the VARCHAR length you defined in the MV.
* column [name] cannot be null: Your MV column is defined as NOT NULL, but the source data has a NULL.
* data quality check failed: A data type mismatch (e.g., a string that cannot be cast to a number).
──────────
2. Why this happens specifically with Iceberg/Data Lakes
Since you are likely refreshing from Iceberg (based on our previous discussion):
* Schema Evolution: If the Iceberg table's schema was changed (e.g., a column was added or a type changed) but the MV wasn't recreated, the data types might no longer match.
* Precision Mismatch: DECIMAL precision in Iceberg might be higher than what was defined in the StarRocks MV.
* Invalid Dates: If your Iceberg data contains "zero-dates" (like 0000-00-00) or dates out of the StarRocks range, they will be filtered.
──────────
3. How to fix it
Option A: Fix the Schema (Recommended)
Once you identify the column from the tracking_log, adjust your MV definition. For example, if a column is too long:
1. Drop the MV.
2. Re-create it with a larger VARCHAR or use STRING.
Option B: Allow the refresh to ignore bad data
If you don't care about a few rows of bad data and want the refresh to succeed anyway, you can change a global setting (requires admin privileges):
sql
-- Set this at the FE level to allow MV refreshes to continue even if rows are filtered
ADMIN SET FRONTEND CONFIG ("mv_refresh_fail_on_filter_data" = "false");
Note: This will simply drop the "bad" rows during the refresh.
Option C: Adjust Session Variables for the MV
You can also try setting the max_filter_ratio for the specific MV so it has some tolerance:
sql
ALTER MATERIALIZED VIEW your_mv_name SET ("session.enable_insert_strict" = "false");
Summary Recommendation:
Run the query in Step 1 first. Most often, this is caused by a VARCHAR being too short for a dimensional attribute or a NULL appearing in a column that was inferred as NOT NULL when the MV was first created.
References
• loading/loading_introduction/troubleshooting_loading.md
• administration/management/FE_configuration.md
• sql-reference/information_schema/load_tracking_logs.md
• loading/InsertInto.md
References
• loading/loading_introduction/troubleshooting_loading.md
• administration/management/FE_configuration.md
• sql-reference/information_schema/load_tracking_logs.md
• loading/InsertInto.mdRocky
09/02/2026, 2:45 AMKenneth Oh
09/02/2026, 2:53 AMRocky
09/02/2026, 2:54 AMTEXT or LONGTEXT type like Postgres or MySQL. Instead, you should use VARCHAR with an explicit length.
Here is how the string types work in StarRocks (as of v2.1 through v4.x):
1. STRING vs. VARCHAR(n)
* STRING: This is just an alias for VARCHAR(65533). If your data exceeds 65,533 bytes (roughly 64KB), the refresh will fail with the "filtered data" error you saw.
* VARCHAR(M): You can explicitly set M up to 1,048,576 (1MB). This is the maximum length for any single string column in StarRocks.
2. How to fix the MV Error
Since your MV is likely "inferring" the schema from the base table or defaulting to 65k bytes, you need to explicitly cast that column to a larger size in your CREATE MATERIALIZED VIEW statement:
sql
CREATE MATERIALIZED VIEW your_mv_name
REFRESH ASYNC ...
AS
SELECT
id,
-- Explicitly expand the limit to 1MB
CAST(long_description AS VARCHAR(1048576)) as long_description,
other_cols
FROM iceberg_catalog.db.dim_table;
Important Technical Details:
* Bytes vs. Characters: StarRocks defines VARCHAR(n) in bytes, not characters. In UTF-8, a single character can take up to 4 bytes. If your dimension table contains long strings with special characters (emojis, non-Latin scripts), you must account for that in your length definition.
* Memory Impact: While VARCHAR is variable-length and only stores the actual data on disk, StarRocks uses the defined length (M) to estimate memory for certain internal operations (like sorting or shuffling). Setting everything to 1MB won't break the cluster, but it is best practice to set it to a realistic maximum (e.g., VARCHAR(200000)) rather than always using the 1MB limit.
* Absolute Limit: If you have a single row with a string larger than 1MB, StarRocks cannot store it in a standard column. You would need to store that data as a JSON type (which has a slightly different internal limit) or split it across multiple columns.
Summary:
If STRING is too short, switch to VARCHAR(1048576) in your MV definition.
References
• sql-reference/data-types/string-type/VARCHAR.md
• sql-reference/data-types/string-type/STRING.md
• faq/table_design_faq.md
References
• sql-reference/data-types/string-type/VARCHAR.md
• sql-reference/data-types/string-type/STRING.md
• sql-reference/System_limit.md
• sql-reference/data-types/string-type/BINARY.md
• faq/table_design_faq.md
• knowledge/usage/best-practice.md
• sql-reference/data-types/semi-structured/JSON.md
References
• sql-reference/data-types/string-type/STRING.md
• knowledge/usage/best-practice.md
• faq/table_design_faq.md
• sql-reference/data-types/string-type/VARCHAR.md
• faq/Others.md
• sql-reference/data-types/string-type/BINARY.md
• sql-reference/System_limit.mdKenneth Oh
09/02/2026, 2:55 AMRocky
09/02/2026, 2:55 AMVARCHAR and STRING data types in StarRocks.
1. The 1MB Limit
In StarRocks, the maximum size for a single column of type VARCHAR or VARBINARY is 1,048,576 bytes (exactly 1 MB).
* STRING is just an alias for VARCHAR(65533).
* VARCHAR(M) can be defined up to 1048576.
* Your data (1,565,197 bytes) is ~1.5 MB, which exceeds this limit. This is why the refresh is failing with "filtered data."
2. The Solution: Use the JSON Data Type
The only native data type in StarRocks that supports values larger than 1 MB is JSON, which has a maximum limit of 16 MB.
Even if your data is just plain text (not a JSON object), you can store it in a JSON column as a "JSON String."
How to update your Materialized View:
You need to re-create your Materialized View and cast the long column to the JSON type:
sql
CREATE MATERIALIZED VIEW iceberg_dim_mv
REFRESH ASYNC ...
AS
SELECT
id,
-- Use TO_JSON to safely wrap the long text into a JSON-compatible format
TO_JSON(iceberg_column_name) as long_description
FROM iceberg_catalog.db.dim_table;
3. Trade-offs to Consider
Using the JSON type for large text comes with specific restrictions:
1. No Joins/Grouping: You cannot use a JSON column in a JOIN condition, GROUP BY, or ORDER BY clause. (Since this is a 1.5MB string, you likely weren't doing this anyway).
2. Function Limits: Most StarRocks string functions (like SUBSTRING, REPLACE, etc.) return a VARCHAR. If you process a 1.5MB JSON value with these functions and the result is still >1MB, the function may return an error or null.
3. Retrieval: When you SELECT long_description, StarRocks will return the data to your client (Python, Java, BI tool) correctly. However, if you try to CAST(long_description AS VARCHAR), it will be truncated to 1MB or fail.
4. Alternative: Splitting the Column (Not Recommended)
If you absolutely must use VARCHAR (e.g., for compatibility with a specific tool that doesn't support JSON), you would have to split the data into two columns in your MV:
sql
SELECT
LEFT(iceberg_col, 1000000) as part_1,
SUBSTRING(iceberg_col, 1000001, 1000000) as part_2
Recommendation: Use the JSON type. It is designed to handle the 16MB payload and will allow your Materialized View refresh to succeed.
References
• sql-reference/data-types/semi_structured/JSON.md
• sql-reference/System_limit.md
• sql-reference/data-types/string-type/VARCHAR.md
References
• sql-reference/data-types/semi_structured/JSON.md
• sql-reference/sql-statements/loading_unloading/BROKER_LOAD.md
• loading/Json_loading.md
• using_starrocks/Flat_json.md
• loading/StreamLoad.mdKenneth Oh
09/02/2026, 2:57 AMRocky
09/02/2026, 2:58 AMsession. property prefix. This feature allows you to attach specific session variables directly to the MV's refresh job.
1. Recommended Method (v3.4 and above)
You can set the max_filter_ratio or disable strict_mode specifically for one MV. This tells StarRocks to ignore rows that fail due to data quality issues (like the "string too long" error you encountered).
If you are creating a new MV:
sql
CREATE MATERIALIZED VIEW your_mv_name
REFRESH ASYNC ...
PROPERTIES (
-- Allow up to 10% of rows to be filtered (0.1) or all of them (1.0)
"session.max_filter_ratio" = "1.0",
-- Disable strict mode so bad data is dropped instead of failing the job
"session.enable_insert_strict" = "false"
)
AS
SELECT ... ;
If the MV already exists:
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.max_filter_ratio" = "1.0",
"session.enable_insert_strict" = "false"
);
2. Global FE Configuration (v3.3 and below)
If you are on an older version or want to apply this change to all MVs in the cluster, you must change the global Frontend (FE) configuration. This is often necessary in v3.5+ where strict mode is enabled by default for all MV refreshes.
sql
ADMIN SET FRONTEND CONFIG ("mv_refresh_fail_on_filter_data" = "false");
* True (default): The MV refresh fails immediately if any row is filtered.
* False: The MV refresh succeeds, but the "bad" rows are simply excluded from the MV.
Which setting should I use?
* session.enable_insert_strict = false: This is usually enough. It tells StarRocks to skip rows that don't match the schema instead of throwing a DmlException.
* session.max_filter_ratio: Use this if you want to set a threshold (e.g., "Allow the refresh to succeed as long as less than 5% of rows are bad"). Setting it to 1.0 allows the job to succeed even if every single row is filtered (not recommended).
Warning: Allowing filtered rows means your Materialized View will contain incomplete data. If you have 1.5MB strings and you allow filtering, those specific rows will simply be missing from your MV. Since you already know the cause is a long string, the better long-term fix is usually to use the JSON data type as discussed previously.
References
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• administration/management/FE_configuration.md
• sql-reference/sql-statements/loading_unloading/INSERT.md
References
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• using_starrocks/async_mv/Materialized_view.md
• sql-reference/System_variable.md
• administration/management/FE_configuration.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.mdKenneth Oh
09/02/2026, 3:07 AMRocky
09/02/2026, 3:08 AMMemory of process exceed limit. read and decompress page indicates that the StarRocks Backend (BE) process ran out of memory while trying to decompress data from your S3 Parquet file.
Because you have 1.5 MB strings, StarRocks is likely trying to load too much data into memory at once. By default, StarRocks processes data in "batches" (usually 4096 rows). If each row has a 1.5 MB string, a single batch requires ~6 GB of memory. With multiple parallel threads, you will hit the 100 GB BE limit very quickly.
1. Reduce the Batch Size
This is the most effective fix for your scenario. Reducing the batch_size forces StarRocks to process fewer rows at a time, significantly lowering the memory footprint per thread.
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.batch_size" = "128" -- Start low (e.g., 128 or 256)
);
2. Limit Parallelism
If your cluster has many CPU cores, it will try to run many scan tasks in parallel. You can throttle this for just this Materialized View:
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.pipeline_dop" = "2", -- Limits the degree of parallelism
"session.parallel_fragment_exec_instance_num" = "1"
);
3. Enable Spilling
From v3.1 onwards, you can allow StarRocks to "spill" intermediate data to disk if it runs out of memory. This is particularly helpful for MVs involving joins or aggregations:
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.enable_spill" = "true"
);
4. Partition the Materialized View
If your base table in Iceberg/S3 is large, refreshing the entire MV at once is risky. You should partition the MV so that it only refreshes one partition at a time (e.g., by day or by the video_id_partition mentioned in your error).
Example of a partitioned MV:
sql
CREATE MATERIALIZED VIEW your_mv_name
PARTITION BY (partition_col)
REFRESH ASYNC
AS
SELECT ... FROM base_table;
Summary of Recommended Fix
Apply these properties to your MV to make the refresh process "leaner":
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.batch_size" = "128",
"session.pipeline_dop" = "4",
"session.enable_spill" = "true"
);
Note: If you haven't already, ensure you changed the column type to JSON in the MV definition, as VARCHAR will still fail with the "string too long" error once you solve the memory issue.
References
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/System_variable.md
• administration/management/BE_configuration.md
References
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• administration/management/BE_configuration.md
• using_starrocks/async_mv/Materialized_view.md
• sql-reference/System_variable.md
• faq/Sql_faq.md
• administration/management/resource_management/resource_group.md
• using_starrocks/async_mv/materialized_view_task_run_details.md
• administration/management/monitoring/alert.md
References
• faq/Sql_faq.mdKenneth Oh
09/02/2026, 3:17 AMRocky
09/02/2026, 3:17 AMsql
CREATE TABLE sr_internal_table
PRIMARY KEY (id) -- Specify your PK columns here
DISTRIBUTED BY HASH (id) -- Specify your Bucket key here
AS
SELECT * FROM iceberg_catalog.db_name.iceberg_table_name;
* Pros: Automatically handles type conversion (e.g., Iceberg string -> SR varchar).
* Cons: Executes immediately. If you only want the DDL without data, add WHERE 1=0 at the end.
──────────
Method 2: Generate DDL via information_schema
If you need a script to generate the DDL string for many tables dynamically (e.g., in a migration script), you can query the StarRocks metadata.
Run this query (replacing the table and catalog names) to generate a CREATE TABLE statement:
sql
SELECT
CONCAT(
'CREATE TABLE ', table_name, '_pk (\n',
GROUP_CONCAT(CONCAT(' ', column_name, ' ', data_type,
CASE WHEN is_nullable = 'NO' THEN ' NOT NULL' ELSE '' END)
ORDER BY ordinal_position SEPARATOR ',\n'),
',\n PRIMARY KEY (',
(SELECT column_name FROM information_schema.columns
WHERE table_name = 'your_iceberg_table' AND ordinal_position = 1), -- Defaults to 1st col
')\n) DISTRIBUTED BY HASH (',
(SELECT column_name FROM information_schema.columns
WHERE table_name = 'your_iceberg_table' AND ordinal_position = 1),
');'
) AS ddl
FROM information_schema.columns
WHERE table_catalog = 'iceberg_catalog'
AND table_schema = 'db_name'
AND table_name = 'iceberg_table_name'
GROUP BY table_name;
──────────
Method 3: Using Materialized Views (Sync Approach)
If your goal is to keep the StarRocks table "synced" with the Iceberg table, creating an Asynchronous Materialized View is often better than a static Primary Key table. It acts like an internal table but handles the "dynamic" DDL and data refresh automatically.
sql
CREATE MATERIALIZED VIEW sr_mv_sync
PRIMARY KEY (id)
DISTRIBUTED BY HASH (id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR) -- Or MANUAL
AS
SELECT * FROM iceberg_catalog.db_name.iceberg_table_name;
Key Mapping Tips:
* Data Types: StarRocks STRING columns are converted to VARCHAR(65533) by default in CTAS. If your Iceberg data exceeds 1MB (as in your previous error), you must manually change the DDL to use the JSON type for those specific columns.
* Partitioning: If your Iceberg table is partitioned, you should add PARTITION BY to your StarRocks DDL to match the Iceberg partition columns for better performance.
* Persistent Index: For Primary Key tables, always add "enable_persistent_index" = "true" in the PROPERTIES to save memory if the table is large.
References
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE_AS_SELECT.md
• using_starrocks/async_mv/Materialized_view.md
• table_design/table_types/primary_key_table.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.mdKenneth Oh
09/02/2026, 3:36 AMRocky
09/02/2026, 3:36 AMapi_attachments from your query to a column available in the MV.
Common Causes
1. Column Missing from MV: The column api_attachments is used in your query (in the SELECT, WHERE, or JOIN clause), but it was not included in the SELECT list when you created the Materialized View.
2. Expression Mismatch: Your query might be performing a function on the column (e.g., JSON_EXTRACT(api_attachments, '$.id') or LENGTH(api_attachments)), but the MV only stores the raw column. While StarRocks can sometimes derive expressions, complex transformations often require the exact expression to be present in the MV definition.
3. Alias Ambiguity: If your query or MV definition uses aliases, the optimizer might be failing to resolve the reference.
4. Data Type Issues: Given your previous context regarding 1.5 MB strings, if api_attachments is a very large JSON or VARCHAR column, the optimizer may struggle to rewrite it if the types don't match perfectly between the Iceberg source and the MV definition.
──────────
How to Diagnose
You can get a specific reason for the failure by running the TRACE command on your query:
sql
-- This will tell you exactly why the rewrite for 'api_attachments' failed
TRACE REASON MV <YOUR_QUERY_HERE>;
If you need even more detail (available in v3.2+):
sql
TRACE LOGS MV <YOUR_QUERY_HERE>;
How to Fix
* Add the column to the MV: Ensure api_attachments is explicitly selected in your CREATE MATERIALIZED VIEW statement.
sql
CREATE MATERIALIZED VIEW your_mv ... AS
SELECT
id,
api_attachments, -- Make sure this is here!
...
FROM base_table;
* Match expressions exactly: If your query uses lower(api_attachments), try adding lower(api_attachments) as a column in the MV itself.
* Check for Nullability: Sometimes a rewrite fails because the base table column is nullable but the MV column is inferred as not-null (or vice-versa). Using IFNULL() in the MV definition can sometimes bridge this gap.
Note: If you add api_attachments to the MV and start seeing the "Memory exceed limit" error again, refer to the previous advice about reducing batch_size and pipeline_dop, as large strings in the projection consume significant memory during the moment they are loaded into the MV.
References
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.mdKenneth Oh
09/02/2026, 3:44 AMapi_attachments column was too large so i casted to json, and query rewrite is failing. how do i still use the mvRocky
09/02/2026, 3:44 AMapi_attachments (a STRING), but the MV only contains CAST(api_attachments AS JSON) (a JSON type), the types do not match. The optimizer cannot automatically "un-cast" the JSON back to a String to satisfy the query.
To fix this and still benefit from the MV, you have three options:
1. Update the Query to match the MV (Easiest)
If you change your query to include the same CAST used in the MV, the optimizer will recognize the expression and rewrite it successfully.
* MV Definition: SELECT CAST(api_attachments AS JSON) AS api_attachments ...
* Change your Query to:
sql
SELECT CAST(api_attachments AS JSON) FROM iceberg_table WHERE ...
2. Use a "View-Based Rewrite" (Recommended for v3.3+)
If you cannot change your application's queries, you can use a Logical View as a middle layer. StarRocks v3.3+ is optimized to handle rewrites through views.
1. Create a Logical View that performs the cast:
sql
CREATE VIEW iceberg_table_view AS
SELECT *, CAST(api_attachments AS JSON) AS api_attachments_json
FROM iceberg_catalog.db.iceberg_table;
2. Create the MV on the View:
sql
CREATE MATERIALIZED VIEW mv_accelerator
REFRESH ASYNC AS
SELECT api_attachments_json, ... FROM iceberg_table_view;
3. Query the View: Queries against iceberg_table_view will now be transparently rewritten to the MV.
3. Include both versions in the MV (Memory Intensive)
If some rows have small strings and some have large strings, you could try to include the raw column and the JSON column. However, as you've seen, the raw STRING (VARCHAR) will likely cause the "Memory exceed limit" or "String too long" error if any single value exceeds 1MB.
Why JSON is the right choice here:
In StarRocks, VARCHAR is limited to 1MB. If your Iceberg strings are 1.5MB, they must be stored as JSON (which supports up to 16MB) or they will fail to load into an internal StarRocks table or MV.
How to verify the fix
After making the change, use the TRACE command to confirm it's working:
sql
TRACE REASON MV SELECT ... FROM iceberg_table;
If successful, you will see SCAN [mv_name] in the logical plan.
Important: Memory Tuning
Since you are dealing with 1.5MB+ JSON blobs, ensure your MV has the memory protections we discussed earlier:
sql
ALTER MATERIALIZED VIEW your_mv SET (
"session.batch_size" = "128",
"session.pipeline_dop" = "2"
);
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• sql-reference/data-types/semi_structured/JSON.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/data-types/semi_structured/JSON.md
• sql-reference/sql-functions/json-functions/json-query-and-processing-functions/cast.md
• using_starrocks/Flat_json.mdRocky
09/02/2026, 3:44 AMKenneth Oh
09/02/2026, 3:46 AMRocky
09/02/2026, 3:46 AMCAST(api_attachments AS JSON) and your query asks for api_attachments (the raw STRING), the optimizer sees two different data types and expression structures. Currently, there is no session variable to "loosen" this specific type-matching requirement to make it transparently swap a String for a JSON column.
However, you can achieve your goal using one of these two "least-intrusive" methods:
1. Alias Shadowing (Most likely to work)
If you name the column in your MV exactly the same as the column in the Iceberg table, the optimizer sometimes has a better chance of matching, but it still often requires the query to use the same expression.
Try defining your MV like this:
sql
CREATE MATERIALIZED VIEW mv_name
REFRESH ASYNC
AS
SELECT
id,
CAST(api_attachments AS JSON) AS api_attachments -- Alias it exactly like the base column
FROM iceberg_catalog.db.table;
Even with this, if the query is SELECT api_attachments, the optimizer sees STRING (Query) vs JSON (MV) and will likely fail.
2. The "Hidden" View Approach (Recommended)
You mentioned wanting to avoid a view, but a Logical View is the only way to "loosen" the plan without changing your application's SQL. This is the standard pattern for handling type mismatches in StarRocks:
1. Rename your Iceberg table reference in your mind.
2. Create a Logical View with the same name as the table your app expects to query:
sql
-- If your app queries `iceberg_db.api_table`
CREATE VIEW iceberg_db.api_table_speedup AS
SELECT *, CAST(api_attachments AS JSON) AS api_attachments
FROM iceberg_catalog.iceberg_db.api_table;
3. Point the MV at this View:
sql
SET enable_view_based_mv_rewrite = true; -- Enable v3.3+ view rewrite
CREATE MATERIALIZED VIEW mv_speedup AS
SELECT * FROM iceberg_db.api_table_speedup;
4. Result: When you query the view, StarRocks sees the CAST in the view, matches it to the CAST in the MV, and rewrites it perfectly.
Why you can't just "loosen" it
* Semantic Risk: JSON and STRING behave differently in StarRocks (e.g., sorting, comparison, and memory handling). The optimizer cannot assume that a user who asked for a STRING will be happy receiving a JSON object (which might be formatted differently or escaped).
* The 1MB Limit: Since StarRocks VARCHAR cannot exceed 1MB, the engine cannot simply cast the JSON back to STRING during the rewrite if the data is 1.5MB, as the query would then crash with the "String too long" error you saw earlier.
Summary of what won't work:
* `query_rewrite_consistency = 'LOOSE'`: This only affects data freshness (allowing stale data), not expression matching.
* `enable_materialized_view_text_match_rewrite`: This requires the text of the query to match the MV. Since your query doesn't have the CAST, the text won't match.
Recommendation: If you absolutely cannot change the query or add a view, the only other option is to use an Internal Primary Key table (Method 1 in the previous answer) and sync the data into it. In an internal table, you can define the column as JSON from the start, and StarRocks will handle the ingestion.
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/System_variable.md
ReferencesRocky
09/02/2026, 3:46 AMKenneth Oh
09/02/2026, 3:48 AMRocky
09/02/2026, 3:48 AMSTRING and a JSON column.
The error cannot totally rewrite expr 6: api_attachments is happening because of a fundamental conflict in how StarRocks handles large data:
1. The 1MB Limit: StarRocks VARCHAR (and the STRING alias) has a hard limit of 1,048,576 bytes (1MB). Since your data is 1.5MB, StarRocks cannot store it as a String in an MV.
2. The JSON Solution: JSON columns support up to 16MB. Casting to JSON is the correct technical solution to store your 1.5MB strings.
3. The Rewriter Block: The optimizer sees your query asking for a STRING but your MV providing a JSON object. Because these types have different memory layouts and serialization rules, the optimizer refuses to swap them transparently.
Is there any way to fix this without changing the query or using a view?
Technically, no. If you cannot change the SQL query and you cannot use a Logical View, the rewriter will continue to fail because it cannot find a way to "cast back" the JSON to a String without hitting that 1MB limit again.
The "Least Intrusive" Workaround: Alias Shadowing (v3.3+)
If you are on v3.3 or higher, you can try to leverage Text-Based Rewrite. This is slightly more flexible than structural rewrite:
1. Set this variable:
sql
SET enable_materialized_view_text_match_rewrite = true;
2. Modify the MV to use a CTE or Subquery that exactly matches the text of your query, but use the CAST inside it.
Note: This is often unreliable if the types don't match, but it's the only "looser" matching logic available.
Why the Logical View is actually the best "Loosening" tool
If you create a Logical View in a StarRocks database that points to the Iceberg table and performs the CAST, you are essentially "telling" the optimizer: "From now on, treat this column as JSON."
1. Create the View:
sql
CREATE VIEW sr_db.api_data AS
SELECT *, CAST(api_attachments AS JSON) AS api_attachments
FROM iceberg_catalog.iceberg_db.api_table;
2. Point the MV at the View.
3. Point your App at the View.
This is the only way to satisfy the optimizer's requirement for type-safety while bypassing the 1MB VARCHAR limit.
Summary of why "Loosening" doesn't exist for this:
If StarRocks allowed you to "loosen" this and it successfully rewrote the query, the query would likely crash immediately after the rewrite. The engine would try to output the 1.5MB JSON value as a STRING (to satisfy your original query), hit the 1,048,576-byte limit, and throw a String/VARCHAR is too long error.
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• sql-reference/System_limit.md
• sql-reference/data-types/string-type/VARCHAR.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• knowledge/usage/best-practice.md
• sql-reference/data-types/string-type/VARCHAR.md
• faq/table_design_faq.md
• sql-reference/System_limit.md
• faq/Others.md
• sql-reference/data-types/string-type/STRING.mdRocky
09/02/2026, 3:48 AMKenneth Oh
09/02/2026, 5:12 AMRocky
09/02/2026, 5:13 AMquery rewrite is not supported for refresh_mode=INCREMENTAL occurs because the "Incremental Refresh" mode (introduced recently to use a specialized incremental computation engine) is currently a dedicated refresh path that has not yet been integrated with the Materialized View (MV) Query Rewrite optimizer.
To fix this while still maintaining incremental updates, you should use the Partition-based Incremental Refresh method, which is the standard, high-performance path in StarRocks that fully supports query rewrite.
The Fix: Switch to Partition-based Refresh
1. Drop your current Materialized View.
2. Recreate it without the "refresh_mode" = "INCREMENTAL" property.
3. Ensure you use PARTITION BY on a column that matches your base table (e.g., the dt column from your Iceberg table).
Example of the correct definition:
sql
CREATE MATERIALIZED VIEW mv_name
-- 1. Use PARTITION BY to enable partition-level incremental updates
PARTITION BY (dt)
DISTRIBUTED BY HASH(id)
-- 2. Set to ASYNC or MANUAL refresh
REFRESH ASYNC
PROPERTIES (
-- 3. DO NOT set refresh_mode = INCREMENTAL here
"partition_refresh_number" = "1"
)
AS
SELECT
id,
dt,
CAST(api_attachments AS JSON) AS api_attachments
FROM iceberg_catalog.db.table;
Why this is better
* Query Rewrite: The StarRocks optimizer is specifically built to handle MVs created this way. It will transparently rewrite your queries to use the MV.
* Incremental Efficiency: Even without the "INCREMENTAL" keyword, StarRocks performs Partition-level Incremental Refresh. When data changes in your Iceberg table, StarRocks detects which partitions changed and only refreshes those specific partitions in the MV.
* Compatibility: This is the most stable and feature-complete way to accelerate data lake queries in StarRocks v3.x.
If you are on v3.3+ and still see rewrite failures:
Ensure that you have not disabled the rewrite session variables. You can check or set them with:
sql
SET enable_materialized_view_rewrite = true;
-- If your MV is built on a view:
SET enable_view_based_mv_rewrite = true;
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.mdKenneth Oh
09/02/2026, 6:50 PMRocky
09/02/2026, 6:51 PMpipeline_connector_scan_thread_num_per_cpu.
In StarRocks, connector (external table) scanning performance is governed by a few key parameters that scale automatically with your cluster's CPU resources, but can be manually tuned.
1. The Core Variable: pipeline_connector_scan_thread_num_per_cpu
* Default: 8
* What it does: This is a BE configuration that defines how many scan threads are assigned to the Pipeline Connector per CPU core on each BE node.
* Total threads per BE: 8 * [Number of CPU Cores].
* Adjustment: If you have a cluster with very high-core BEs and you are hitting I/O limits on your object store (S3/HDFS), you might lower this. If your BEs are under-utilized during external queries, you can increase it.
2. The Concurrency Formula
When querying external catalogs (Iceberg, Hive, etc.), the overall "worker" concurrency is roughly calculated as:
pipeline_dop _ connector_io_tasks_per_scan_operator _ [Number of BE Nodes]
To adjust this based on cluster size or specific query needs, use these session variables:
* pipeline_dop: This controls the parallelism of a single query fragment. By default, it is set to 0 (which means it equals the number of CPU cores). For high-concurrency small queries, you might lower this to 4 or 8 to prevent a single query from exhausting the cluster.
* connector_io_tasks_per_scan_operator:
* Default: 16
* What it does: The maximum concurrent I/O tasks issued by a single scan operator.
* Adjustment: If you are seeing "Slow I/O" in your query profile but have plenty of CPU, increase this to 32 or 64.
3. How to adjust for Cluster Size
* For Vertical Scaling (More Cores per BE): StarRocks handles this automatically because pipeline_connector_scan_thread_num_per_cpu and pipeline_dop (if set to 0) scale with cores.
* For Horizontal Scaling (More BE Nodes): The total cluster-wide concurrency scales linearly. If you add more BEs and find your external storage (like an HDFS NameNode or S3 rate limit) is struggling, you should decrease connector_io_tasks_per_scan_operator globally to avoid overwhelming the source.
Summary of Tuning Commands:
To lower the pressure on an external system (at the cost of query speed):
sql
SET GLOBAL enable_connector_adaptive_io_tasks = false;
SET GLOBAL connector_io_tasks_per_scan_operator = 8;
SET GLOBAL pipeline_dop = 8;
To maximize throughput for a single large query (on a powerful cluster):
sql
SET connector_io_tasks_per_scan_operator = 32;
SET pipeline_dop = 0; -- Use all cores
References
• administration/management/BE_configuration.md
• sql-reference/System_variable.md
• knowledge/usage/best-practice.md
References
• loading/Kafka-connector-starrocks.md
• sql-reference/System_variable.md
• administration/management/BE_configuration.md
• knowledge/usage/best-practice.mdKenneth Oh
09/17/2026, 11:33 PMRocky
09/17/2026, 11:34 PMpipeline_dop × connector_io_tasks_per_scan_operator × Number of BE Nodes
* pipeline_dop: Default is usually your CPU core count (e.g., 16 or 32).
* connector_io_tasks_per_scan_operator: Default is 16.
* Multiplier: On a 10-node cluster with 16 cores, you could have $16 \times 16 \times 10 = 2,560$ concurrent I/O requests for a single query.
* The Result: A few parallel queries can easily exceed the 5,500 GET requests/sec S3 limit.
2. The Iceberg "Manifest Storm"
Iceberg tables consist of many small Manifest Files and Manifest Lists.
* Distributed Planning: In newer StarRocks versions, the work of reading these manifest files is distributed across BE nodes to speed up query planning.
* Throttling at the Metadata Level: Before a single row of data is read, StarRocks may issue thousands of GET requests to S3 just to fetch the manifest files to determine which data files to scan. Because these metadata files often share the same S3 prefix (e.g., `s3://bucket/table/metadata/`), they hit the same S3 partition limit simultaneously.
3. Connector Scan Threads vs. I/O Tasks
The BE parameter pipeline_connector_scan_thread_num_per_cpu (default 8) defines the number of worker threads available to process the data. However, the number of outstanding network requests to S3 is governed by connector_io_tasks_per_scan_operator.
* If you have many scan threads but the S3 storage is throttling, those threads will sit idle waiting for I/O, leading to high query latency and thread pool exhaustion on the BE.
How to Fix and Mitigate
A. Limit Global Concurrency (The "Safety Valve")
If you are seeing 503 errors, reduce the aggressiveness of the connector:
sql
-- Lower the I/O concurrency per operator (Default is 16)
SET GLOBAL connector_io_tasks_per_scan_operator = 4;
-- Turn off adaptive I/O if it's scaling too aggressively
SET GLOBAL enable_connector_adaptive_io_tasks = false;
-- Limit the parallelism of a single query
SET GLOBAL pipeline_dop = 8;
B. Enable Metadata Caching
Caching manifests on the FE and BE prevents StarRocks from hitting S3 for every query:
* Memory/Disk Cache: Ensure enable_iceberg_metadata_disk_cache is true in your FE configuration.
* Refresh Strategy: Use enable_background_refresh_connector_metadata to let StarRocks refresh metadata in the background at a controlled rate, rather than "bursting" requests during query time.
C. Tune the BE Scan Pool
If you have a very large cluster (e.g., 100+ BEs), the default pipeline_connector_scan_thread_num_per_cpu = 8 might be too high for external catalogs. You can lower this in be.conf to reduce the total "pressure" the cluster can exert on S3:
properties
pipeline_connector_scan_thread_num_per_cpu = 4
Summary of Symptoms
* Profile: You see high "ScanTime" or "OpenFilesTime" but very low CPU usage.
* Logs: BE or FE logs show com.amazonaws.services.s3.model.AmazonS3Exception: Slow Down (Service: Amazon S3; Status Code: 503; Error Code: 503 Slow Down).
* Behavior: Queries on small Iceberg tables are fast, but queries on tables with thousands of partitions/manifests fail or hang.
References
• data_source/catalog/iceberg/iceberg_catalog.md
• sql-reference/System_variable.md
• administration/management/BE_configuration.mdRocky
09/17/2026, 11:34 PMKenneth Oh
09/17/2026, 11:34 PMRocky
09/17/2026, 11:34 PMpipeline_dop × connector_io_tasks_per_scan_operator × Number of BE Nodes.
* The Burst: If you have a 20-node cluster and a high pipeline_dop, a single query can instantly fire over 10,000 metadata requests to the Glue REST Catalog and S3 Table storage.
* REST API Limits: While the S3 data plane is fast, the AWS Glue Iceberg REST endpoint (which handles the table metadata) has its own transaction-per-second (TPS) limits that are often lower than the S3 bucket itself.
──────────
How to Tune StarRocks for S3 Tables
1. Limit the "Blast Radius" of Parallel Scans
If you are hitting 503 "Slow Down" errors, you should reduce the aggressiveness of the connector. These settings are most effective when set globally or at the session level:
sql
-- Lower the concurrent I/O tasks per scan operator (Default: 16)
SET GLOBAL connector_io_tasks_per_scan_operator = 4;
-- Disable adaptive I/O tasks to keep concurrency predictable
SET GLOBAL enable_connector_adaptive_io_tasks = false;
-- Limit the parallelism of a single query (Default: 0, which uses all cores)
SET GLOBAL pipeline_dop = 8;
2. Optimize Metadata Retrieval
StarRocks v3.3+ introduced an Adaptive Metadata Retrieval strategy. You can force StarRocks to use a more "S3-friendly" approach for metadata:
* plan_mode: Set this to local to have the FE handle metadata more conservatively, or distributed if you want the BEs to scan manifest files in parallel (but with the lower concurrency limits above).
sql
SET plan_mode = 'local'; -- Reduces the "storm" of requests from multiple nodes
* Metadata Caching: Ensure FE caching is enabled so that repeated queries don't hit S3/Glue for the same manifest files.
properties
# In fe.conf
enable_iceberg_metadata_disk_cache = true
3. Adjust the BE Thread Pool
The BE parameter pipeline_connector_scan_thread_num_per_cpu (default 8) determines how many threads per core are dedicated to external scans. In very large clusters (100+ BEs), you should consider lowering this to avoid overwhelming the S3 endpoint.
* In `be.conf`: pipeline_connector_scan_thread_num_per_cpu = 4
Summary Recommendation
S3 Table Buckets solve the "data file" throttling problem for most users, but the Iceberg Manifest/Metadata phase remains a bottleneck because it is a burst of thousands of tiny GET requests.
*Start by setting `connector_io_tasks_per_scan_operator = 4`*—this is the most effective way to "smooth out" the request spikes without sacrificing significant query throughput.
References
• sql-reference/System_variable.md
• data_source/catalog/iceberg/iceberg_catalog.md
• administration/management/BE_configuration.md
• knowledge/usage/best-practice.md
References
• data_source/catalog/iceberg/iceberg_catalog.md
• knowledge/usage/best-practice.mdKenneth Oh
09/18/2026, 4:20 AMRocky
09/18/2026, 4:20 AMdate_trunc('month', col) expression in the MV's PARTITION BY clause to align with Iceberg's month() transform.
Example Scenario
* Iceberg Table: iceberg_catalog.db.orders
* Iceberg Partition: month(order_date)
* Goal: Create a StarRocks MV that inherits this monthly partitioning for incremental refreshes.
SQL Example
sql
CREATE MATERIALIZED VIEW mv_monthly_orders
-- Align with Iceberg's month(order_date) transform
PARTITION BY date_trunc('month', order_date)
-- Distributed by a high-cardinality column for performance
DISTRIBUTED BY HASH(order_id)
-- Refresh automatically when base table data changes
REFRESH ASYNC
PROPERTIES (
-- Limit concurrency: refresh only 1 month at a time to save resources
"partition_refresh_number" = "1",
-- Optional: Keep only the last 24 months of data in the MV
"partition_ttl_number" = "24"
)
AS
SELECT
order_id,
order_date,
customer_id,
amount
FROM iceberg_catalog.db.orders;
Key Implementation Details
1. Partition Mapping:
* If your Iceberg table uses month(order_date), your StarRocks MV must use date_trunc('month', order_date).
* This "Partition Alignment" allows StarRocks to track which specific months have changed in the Iceberg catalog and refresh only those corresponding partitions in the MV (Incremental Refresh).
2. Refresh Strategy:
* `REFRESH ASYNC`: The MV will detect data changes in the Iceberg table (via snapshot IDs/manifests) and trigger a refresh.
* `partition_refresh_number`: For external tables, it is often safer to set this to 1 or 2. This ensures that if you backfill 12 months of data in Iceberg, StarRocks won't try to refresh all 12 months in one massive, resource-heavy transaction.
3. Performance Tip (Bucketing):
* Always include DISTRIBUTED BY HASH(...). Even though the data is partitioned by month, StarRocks needs a distribution key to manage data across BE nodes.
4. Version Requirement:
* Standard date_trunc partitioning for MVs is available in most StarRocks 3.x versions.
* If your Iceberg table uses multi-column partitioning (e.g., partitioned by region AND month), you should use StarRocks v3.5.0 or later, which supports multi-column partition mapping for Lakehouse integration.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• data_source/catalog/iceberg/iceberg_catalog.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.mdKenneth Oh
09/18/2026, 4:24 AMRocky
09/18/2026, 4:24 AMEVERY (INTERVAL ...) clause to your REFRESH ASYNC statement.
Corrected Example
sql
CREATE MATERIALIZED VIEW mv_monthly_orders
-- Align with Iceberg's month(order_date) transform
PARTITION BY date_trunc('month', order_date)
DISTRIBUTED BY HASH(order_id)
-- FIX: Specify a refresh interval (e.g., every 1 hour)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
"partition_ttl_number" = "24",
-- Required for query rewrite to work with external tables
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
order_id,
order_date,
customer_id,
amount
FROM iceberg_catalog.db.orders;
Key Requirements for External Iceberg MVs
1. Refresh Strategy: You have two choices for external tables:
* Scheduled: REFRESH ASYNC EVERY (INTERVAL 1 DAY) — StarRocks will check for Iceberg snapshot changes at this interval and refresh only the affected partitions.
* Manual: REFRESH MANUAL — You must trigger the refresh yourself using REFRESH MATERIALIZED VIEW mv_name.
2. Partition Alignment: Using date_trunc('month', col) is correct for Iceberg's month() transform. This allows for incremental partition refreshes. Even if the schedule is set to "every 1 hour," StarRocks will only refresh a month's partition if it detects the Iceberg snapshot for that partition has changed.
3. Query Rewrite: By default, StarRocks is conservative with external data. To allow the optimizer to automatically rewrite user queries to use the MV, you usually need to set "force_external_table_query_rewrite" = "true" in the properties.
Summary of the Error
* Cause: REFRESH ASYNC (without an interval) implies "refresh immediately whenever the base table changes." StarRocks cannot "watch" S3/Iceberg for changes in real-time to trigger this.
* Solution: Tell StarRocks how often to poll for changes by adding EVERY (INTERVAL ...).
References
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/Materialized_view.md
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.mdKenneth Oh
09/18/2026, 4:27 AMRocky
09/18/2026, 4:27 AMInvalid parameter partition_ttl_number does not support non-range-partitioned materialized view occurs because StarRocks only supports the TTL (Time-To-Live) property on Range Partitioned materialized views.
If your Iceberg base table's partition column (or the result of the partition transform) is interpreted as a STRING by StarRocks, it defaults to creating a List Partition, which does not support automatic TTL pruning based on chronological order.
How to Fix
To enable TTL, you must ensure the MV is created with Range Partitioning by using a DATE or DATETIME type for the partition key.
1. Explicitly Cast to DATE (If the column is a String)
If order_date in your Iceberg table is a string, you must convert it to a DATE type in the SELECT list and partition by that alias.
2. Use partition_ttl (Recommended)
partition_ttl is the modern replacement for partition_ttl_number (supported since v3.1.5). It allows you to specify a time range (e.g., '24 MONTH') instead of just a number of partitions.
Corrected SQL Example
sql
CREATE MATERIALIZED VIEW mv_monthly_orders
-- PARTITION BY must use a DATE/DATETIME column to be "Range Partitioned"
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(order_id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
-- Use partition_ttl for a clear time window (v3.1.5+)
"partition_ttl" = "24 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
order_id,
-- If order_date is a string '2024-01-01', cast it:
-- CAST(order_date AS DATE) as mv_date,
order_date as mv_date,
customer_id,
amount
FROM iceberg_catalog.db.orders;
Why this works:
* Range vs. List: By ensuring the partitioning expression date_trunc('month', ...) operates on a DATE type, StarRocks recognizes the MV as Range Partitioned. This allows it to sort partitions chronologically and apply the TTL.
* Alignment: Even though you use date_trunc in the MV, StarRocks is smart enough to map this to an Iceberg table partitioned by month(order_date) for incremental refreshes.
* TTL Behavior: With "partition_ttl" = "24 MONTH", StarRocks will automatically drop any partition whose date is older than 24 months during every refresh cycle.
Checklist if it still fails:
1. Check Data Type: Run DESC iceberg_catalog.db.orders;. If order_date is a VARCHAR or STRING, you must use CAST(order_date AS DATE) in the SELECT and use that alias in the PARTITION BY.
2. StarRocks Version: If you are on a version older than 3.1.5, use "partition_ttl_number" = "24", but the requirement for a DATE/DATETIME partition key remains the same.
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• data_source/catalog/iceberg/iceberg_catalog.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/18/2026, 4:30 AMRocky
09/18/2026, 4:30 AMInvalid parameter partition_ttl_number does not support non-range-partitioned materialized view occurs because StarRocks has determined your Materialized View (MV) is not "Range Partitioned." Only Range Partitioned MVs support automatic data retention via TTL properties.
This typically happens for two reasons in your specific SQL:
1. *Use of `SELECT `: StarRocks needs to explicitly map the MV's partition key to a column in the SELECT list to establish a Range relationship. Using * makes this mapping ambiguous or fails to verify the data type.
2. Column Type or Name: If the date column in your Iceberg table is a STRING/VARCHAR (even if it looks like a date), StarRocks will treat the MV as "List Partitioned" by default. Additionally, date is a reserved keyword and should be escaped.
Recommended Fix
To ensure the MV is created as a Range Partitioned table that supports TTL:
* Explicitly cast the partition column to DATE or DATETIME.
* Use an alias for the casted column.
* *Avoid `SELECT ` and list your columns explicitly.
* Use partition_ttl (e.g., '60 MONTH') which is the modern replacement for partition_ttl_number (supported in v3.1.5+).
Corrected Example
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- Use the alias defined in the SELECT list
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
-- 'partition_ttl' is the recommended property for v3.1.5+
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
-- Explicitly cast and alias the partition column
CAST(`date` AS DATE) AS mv_date,
col1,
col2
FROM iceberg.viewiq.core_age_range_view;
Why this solves it:
* Range Partitioning: By casting the column to DATE and using date_trunc, StarRocks explicitly recognizes this as a time-series range. This enables the partition_ttl logic.
* Metadata Mapping: Explicitly naming the column in both the PARTITION BY and SELECT clauses allows the metadata engine to verify that the MV partition aligns with the Iceberg base table partitions (Partition Alignment).
* Reserved Keywords: Enclosing `` date `` in backticks prevents syntax errors if your column is named after the DATE type.
If you still see errors:
Ensure the base Iceberg table core_age_range_view is indeed a partitioned table. If it is an Iceberg view rather than a table, StarRocks may not be able to infer the partition structure required for an incremental partitioned MV.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/18/2026, 4:34 AMRocky
09/18/2026, 4:35 AMdate_trunc to an explicit column in your SELECT list.
This is usually caused by two things in your current query:
1. *Using `SELECT `: StarRocks requires the partitioning column to be explicitly named in the SELECT list to establish the partition alignment between the base table and the MV.
2. Reserved Keyword: date is a reserved keyword (it's a data type). When used in a function within the PARTITION BY clause, the analyzer may fail to recognize it as a column name unless it is explicitly aliased or backticked.
How to Fix
You must explicitly list the column in your SELECT clause and use a clear alias.
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- 1. Use the alias defined in the SELECT list below
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
-- 2. Explicitly name the column and provide an alias
-- Also use backticks if the source column name is actually `date`
`date` AS mv_date,
other_col1,
other_col2
FROM iceberg.viewiq.core_age_range_view;
Why this change is required:
* Column Mapping: StarRocks' MV optimizer needs to "see" the column in the SELECT output to verify that it can partition the resulting data. SELECT * often masks the metadata needed for this verification.
* Type Safety: If the base table's date column is a STRING, you must cast it: `CAST(date AS DATE) AS mv_date`. Partitioning functions like date_trunc in MVs require the input to be a DATE or DATETIME type.
* Partition Alignment: For the MV to refresh incrementally (only refreshing new months), the column used in date_trunc should ideally be the same column used to partition the underlying Iceberg table.
Summary Checklist:
1. Change SELECT * to a list of columns.
2. Alias the date column (e.g., AS mv_date).
3. Use that alias in the PARTITION BY date_trunc(...) expression.
4. If the original column name is date, wrap it in backticks: `` date ``.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/18/2026, 4:40 AMdate)Rocky
09/18/2026, 4:40 AMCREATE MATERIALIZED VIEW statement to the actual columns in your SELECT list, and date is a reserved keyword.
Because your Iceberg table is already partitioned by month(date), you have two ways to fix this depending on your StarRocks version.
Option 1: Modern Alignment (Recommended for v3.2.3+)
Starting from StarRocks v3.2.3, if an Iceberg table uses a partition transform like month(date), you can simply partition the MV by the base column name. StarRocks will automatically recognize the transform and align the MV partitions by month.
Note: You must explicitly list the columns in the SELECT clause (avoid SELECT *) and wrap the reserved keyword `` date `` in backticks.
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- StarRocks v3.2.3+ will automatically align this with Iceberg's month(date)
PARTITION BY (`date`)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
-- Use partition_ttl for easier management (e.g., '60 MONTH')
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
`date`, -- Explicitly select the column
col1,
col2
FROM iceberg.viewiq.core_age_range_view;
Option 2: Explicit Rollup (Required if Option 1 fails)
If you are on an older version or if the automatic alignment isn't triggering, you must use date_trunc. To fix the "must relate to column" error, ensure the column used in date_trunc is explicitly projected in the SELECT list.
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- Use the alias 'mv_date' to ensure the relation is clear to the analyzer
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
`date` AS mv_date, -- Provide an explicit alias
col1,
col2
FROM iceberg.viewiq.core_age_range_view;
Important Prerequisites
1. Is it a Table or a View? Your base table is named core_age_range_view. If this is an Iceberg View rather than an Iceberg Table, StarRocks cannot detect the underlying partition transforms (month(date)). Materialized View partitioning requires the base source to be a physical table.
2. Reserved Keywords: Always wrap the column name `` date `` in backticks to prevent the analyzer from confusing it with the DATE data type.
3. Range Partitioning: The error does not support non-range-partitioned was likely a side-effect of the partition expression failing to resolve. Once the PARTITION BY clause is correctly linked to the column, the MV will be created as Range Partitioned, and TTL will work.
References
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/18/2026, 4:53 AMRocky
09/18/2026, 4:54 AMInvalid parameter partition_ttl_number does not support non-range-partitioned materialized view happens because StarRocks has failed to verify that your Materialized View (MV) is "Range Partitioned." Only Range Partitioned MVs support automatic data retention via TTL properties.
For an Iceberg table already partitioned by month(date), the analyzer is likely failing for these specific reasons:
1. Reserved Keyword Conflict: The name date is a reserved keyword in StarRocks. Without backticks, date_trunc('month', date) can be misinterpreted as the DATE data type rather than your column.
2. *Use of `SELECT `: StarRocks requires the partitioning column to be explicitly present and identifiable in the SELECT list to establish the "Range" relationship. Using * makes this mapping ambiguous.
3. Iceberg Version Alignment: If your StarRocks version is older than v3.2.3, it may not automatically recognize the Iceberg month() transform as a valid Range partition for the MV unless explicitly mapped.
The Corrected SQL
To fix this, you must backtick the reserved keyword, explicitly list your columns, and use an alias to ensure the analyzer can link the partition function to the result column.
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- 1. Use the alias defined in the SELECT list
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
-- 2. 'partition_ttl' is the modern replacement for 'partition_ttl_number'
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
-- 3. Explicitly project the column with backticks and an alias
`date` AS mv_date,
col1,
col2
FROM iceberg.viewiq.core_age_range_view;
Why this solves the issue:
* Establish Range Property: By using date_trunc on a column projected in the SELECT list, StarRocks marks the MV as Range Partitioned. This enables the partition_ttl property to function.
* Resolves "Must relate with column": Explicitly using mv_date in both the PARTITION BY and SELECT clauses eliminates the ambiguity caused by SELECT *.
* Partition Alignment: For Iceberg tables, StarRocks (v3.2.3+) will see that your MV's month truncation matches the Iceberg table's month transform, allowing for Incremental Refresh (it will only refresh the months that changed).
Important Note on Table vs. View
Your base table is named core_age_range_view. If this is an Iceberg View (and not a physical Iceberg Table), StarRocks cannot perform partition-level tracking or TTL. Partition alignment and incremental refresh only work when the source is a physical partitioned table. If it is a view, you will have to refresh the entire MV every time.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/18/2026, 4:57 AMRocky
09/18/2026, 4:57 AMInvalid parameter partition_ttl_number does not support non-range-partitioned materialized view indicates that StarRocks has classified your Materialized View (MV) as a List-partitioned or non-partitioned table, rather than a Range-partitioned table. In StarRocks, TTL properties (like partition_ttl_number or partition_ttl) are strictly only supported for Range partitions.
Even though your column is a DATE type and you are using date_trunc, several factors can cause StarRocks to default to List partitioning when dealing with Iceberg tables:
1. Reserved Keyword Conflict
The name date is a reserved keyword (a data type) in StarRocks. If you use PARTITION BY date_trunc('month', date) without backticks, the analyzer may fail to link the function to your actual column and instead treat the partition as a static expression or a List of values.
2. Iceberg Partition Transform Alignment
StarRocks has specific logic for Iceberg transforms like month(date).
* For StarRocks v3.2.3 and later: You should partition the MV directly by the column name `` date ``. StarRocks will automatically detect the Iceberg month() transform and align it as a Range partition.
* For Older Versions: You must use date_trunc, but you must ensure the column is explicitly selected and aliased in the SELECT list to force Range partitioning.
3. "List" vs "Range" Detection
If StarRocks cannot find a continuous "range" mapping from the base table to the MV (which often happens with SELECT * or ambiguous column names), it fallback to List partitioning. List partitions do not support the partition_ttl property.
──────────
The Recommended Fix
Update your SQL to use backticks around the reserved word `` date ``, explicitly alias the column, and use the modern partition_ttl property (which replaced partition_ttl_number since v3.1.5).
sql
CREATE MATERIALIZED VIEW <http://default_catalog.reporting.mv|default_catalog.reporting.mv>
-- 1. Use the alias 'mv_date' to ensure the analyzer creates a Range partition
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"partition_refresh_number" = "1",
-- 2. Use 'partition_ttl' (e.g., '60 MONTH') which is more robust than '_number'
"partition_ttl" = "60 MONTH",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
id,
-- 3. Backtick the reserved word and provide a clear alias
`date` AS mv_date,
other_col1,
other_col2
FROM iceberg.viewiq.core_age_range_view;
Why this works:
* Range Partitioning: By explicitly mapping date_trunc to a projected column alias, StarRocks identifies the MV as Range-partitioned (specifically Expression Partitioning), which allows the use of TTL.
* TTL Support: Once the MV is successfully created as Range-partitioned, the error about partition_ttl_number will disappear.
* Iceberg Alignment: Because your Iceberg table is already partitioned by month(date), this configuration allows StarRocks to perform Incremental Refresh, only updating the specific months that have changed in S3.
One final check: Ensure core_age_range_view is a physical Iceberg Table. If it is actually an Iceberg View, StarRocks cannot see the underlying partitions and will always treat the MV as a single non-partitioned unit, which does not support TTL.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
• table_design/data_distribution/expression_partitioning.mdKenneth Oh
09/18/2026, 4:59 AMRocky
09/18/2026, 4:59 AMInvalid parameter partition_ttl_number does not support non-range-partitioned materialized view occurs because StarRocks has classified your Materialized View (MV) as List-partitioned (or non-partitioned), but TTL features are strictly only supported for Range-partitioned tables.
Even though your Iceberg table is partitioned by month(date), several syntax choices in your MV definition can cause StarRocks to drop the "Range" classification:
1. Don't use month() in the MV definition
If you used PARTITION BY month(date) in your MV SQL, StarRocks treats the result as an integer (1-12). This creates a List partition, which does not support TTL.
The Fix: Use date_trunc instead. In StarRocks 3.2.3 and later (including your v4.1.4), date_trunc('month', ...) is the correct way to align with Iceberg’s month() transform while maintaining a Range partition for the MV.
2. Escape the Reserved Keyword
The word date is a reserved keyword (a data type) in StarRocks. If you don't use backticks, the analyzer may fail to recognize it as a column, causing the partition mapping to fail and defaulting the MV to a non-partitioned state.
The Fix: Always use `` date ``.
3. Update the Property Name
Since StarRocks v3.1.5, partition_ttl_number has been replaced by partition_ttl. While the old property is sometimes supported for backward compatibility, on v4.1.4 you should use the modern string-based TTL.
──────────
Recommended SQL for StarRocks 4.1.4
Use this structure to ensure the MV is recognized as Range-partitioned and aligns with your Iceberg source:
sql
CREATE MATERIALIZED VIEW your_catalog.your_db.your_mv
-- 1. Use date_trunc to ensure it's a Range partition
PARTITION BY date_trunc('month', mv_date)
DISTRIBUTED BY HASH(some_id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
-- 2. Modern TTL property (e.g., '24 MONTH' or '2 YEAR')
"partition_ttl" = "24 MONTH",
"partition_refresh_number" = "1",
"force_external_table_query_rewrite" = "true"
)
AS
SELECT
some_id,
-- 3. Explicitly alias the reserved keyword column
`date` AS mv_date,
col1,
col2
FROM your_iceberg_catalog.your_db.your_table;
Why this works:
* Range Detection: date_trunc returns a `DATE`/`DATETIME` value. StarRocks uses this to build a Range partition, enabling the partition_ttl logic.
* Iceberg Alignment: StarRocks automatically maps its date_trunc('month', ...) to Iceberg's month() transform. This allows Incremental Refresh (it will only refresh the specific month partitions that changed in S3).
* Explicit Mapping: By aliasing the column as mv_date and using it in both the SELECT and PARTITION BY clauses, you remove any ambiguity for the SQL analyzer.
Note on Partition Evolution: If your Iceberg table has undergone "Partition Evolution" (e.g., you changed the partition spec from day to month over time), StarRocks currently does not support creating a partitioned MV on that table. You can verify this by checking the Iceberg table metadata.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.mdKenneth Oh
09/18/2026, 5:05 AMRocky
09/18/2026, 5:05 AMSHOW MATERIALIZED VIEWS)
This is the quickest way to see if the MV is "Active" and whether the last refresh succeeded.
sql
SHOW MATERIALIZED VIEWS WHERE NAME = 'your_mv_name'\G
Key fields to look at:
* is_active: Must be true for the MV to be usable. If false, check inactive_reason.
* last_refresh_state: Shows SUCCESS, FAILED, RUNNING, or PENDING.
* last_refresh_error_message: Provides a brief error snippet if the last refresh failed.
* task_name: Take note of this (usually looks like mv-12345) to use in the detailed query below.
──────────
2. Detailed Execution History (task_runs)
Because async MVs run as background tasks, you can query the information_schema to see exactly what happened during the creation or refresh, including start/end times and full error logs.
sql
SELECT *
FROM information_schema.task_runs
WHERE task_name = 'mv-YOUR_TASK_ID' -- Get this ID from the SHOW command above
ORDER BY create_time DESC
LIMIT 5\G
Why use this?
* It shows the progress percentage while a build is running.
* It provides the full error_message if the build failed, which is often more descriptive than the summary in SHOW MATERIALIZED VIEWS.
──────────
3. Check Partition-Level Status
Since you are using a partitioned MV, you may want to see which specific monthly partitions have been successfully built and which are still "outdated" or missing.
sql
SHOW MATERIALIZED VIEW STATUS FOR your_mv_name;
This command (introduced in later 3.x versions) helps identify if specific months are stale because the underlying Iceberg data changed.
Summary of common states:
* is_active = false: Usually means the schema of the base Iceberg table changed significantly, or there was a metadata error. You may need to run ALTER MATERIALIZED VIEW your_mv_name ACTIVE; to try re-activating it.
* last_refresh_state = FAILED: Check task_runs for the exact error. For Iceberg, this is often related to S3 connectivity, permissions, or the "non-range-partitioned" error we discussed earlier.
References
• sql-reference/sql-statements/materialized_view/SHOW_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/information_schema/task_runs.md
References
• sql-reference/sql-statements/materialized_view/SHOW_ALTER_MATERIALIZED_VIEW.md
• using_starrocks/Materialized_view-single_table.md
• sql-reference/sql-statements/materialized_view/SHOW_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/Materialized_view.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.mdKenneth Oh
09/18/2026, 5:05 AMRocky
09/18/2026, 5:05 AMSHOW PROC command to monitor the active queries triggered by Materialized View (MV) creation and refresh tasks, along with other system-level tools for status tracking.
1. Monitor Active Refresh Queries
When an asynchronous MV is being created or refreshed, StarRocks executes an internal INSERT OVERWRITE query. You can see these active queries using:
sql
SHOW PROC '/current_queries';
-- Or for all FE nodes in a cluster:
SHOW PROC '/global_current_queries';
In the output, look for queries where the User is often the system user or the query definition starts with INSERT OVERWRITE. You can see real-time metrics like ScanBytes, MemoryUsage, and ExecTime.
──────────
2. Monitor Task Status (Modern Approach)
Since StarRocks 3.x/4.x handles MV refreshes as "Tasks," the most detailed way to see the "Creation" or "Refresh" status is through the information_schema.
To see the progress and error messages of the current run:
sql
SELECT
query_id,
task_name,
state,
progress,
error_message,
create_time
FROM information_schema.task_runs
ORDER BY create_time DESC
LIMIT 5;
──────────
3. High-Level Summary
If you just want to see if the MV finished building and is "Active":
sql
SHOW MATERIALIZED VIEWS WHERE NAME = 'your_mv_name'\G
* last_refresh_state: Shows RUNNING, SUCCESS, or FAILED.
* last_refresh_error_message: This is where your previous "Invalid parameter" error would have appeared.
Summary of Monitoring Commands:
Command | Best For...
SHOW PROC '/current_queries' | Seeing real-time CPU/Memory/Scan usage of an active refresh.
SELECT * FROM task_runs | Checking the percentage Progress (%) and historical errors.
SHOW MATERIALIZED VIEWS | Checking if the MV is currently Active: true.
SHOW ALTER MATERIALIZED VIEW | Note: This is only for synchronous (simple) MVs, not modern Async MVs.
References
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROC.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/information_schema/task_runs.md
References
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• knowledge/usage/best-practice.md
• using_starrocks/async_mv/Materialized_view.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROC.mdKenneth Oh
09/18/2026, 10:32 PMRocky
09/18/2026, 10:32 PMINSERT OVERWRITE query. If the cluster is too busy, StarRocks "parks" this query in a queue to prevent an Out-of-Memory (OOM) or CPU collapse.
1. Immediate Workaround
You can increase the time a query is allowed to sit in the queue before failing. Execute this globally (as an admin):
sql
SET GLOBAL query_queue_pending_timeout_second = 600; -- Increase to 10 minutes
2. Identify the Bottleneck
The queue is triggered because one of your resource thresholds was hit. Run these commands to see why your cluster is queuing tasks:
* Check active/pending queries:
sql
SHOW RUNNING QUERIES;
Look for the State column. If many queries are PENDING, look at the ResourceGroupId to see which group is full.
* Check Backend (BE) resource usage:
sql
SHOW PROC '/backends'\G
Check MemUsedPct and CpuUsedPct. If these are high (e.g., >90%), the queue will naturally hold back new MV refreshes.
3. Why MV Refreshes specifically fail
MV refreshes are heavy. If mv_staging_video_bs_confidence is large, it might be trying to refresh too many partitions at once, hitting the memory limit immediately.
Recommended Fixes for the MV:
* Reduce Batch Size: Instead of refreshing everything at once, tell StarRocks to refresh only 1 partition at a time to keep resource usage low.
sql
ALTER MATERIALIZED VIEW mv_staging_video_bs_confidence
SET ("partition_refresh_number" = "1");
* Check MV Concurrency: StarRocks limits how many MV tasks can run at once (default is 4). You can check this FE configuration:
sql
ADMIN SHOW FRONTEND CONFIG LIKE 'task_runs_concurrency';
4. Adjust Queue Thresholds (Advanced)
If your cluster has plenty of RAM/CPU but is still queuing, your concurrency limits might be too strict. Check these variables:
* `query_queue_concurrency_limit`: Maximum queries per BE node.
* `query_queue_mem_used_pct_limit`: Triggers queue when memory exceeds this % (e.g., 0.9).
* `query_queue_cpu_used_permille_limit`: Triggers queue when CPU exceeds this (e.g., 800).
Summary: The error isn't a bug in your MV logic; it's the StarRocks "Traffic Controller" stopping the refresh to protect the cluster. Increasing the timeout buys you time, but reducing partition_refresh_number is the best way to ensure the refresh actually succeeds without overloading the system.
References
• administration/management/resource_management/query_queues.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• sql-reference/System_variable.md
References
• administration/management/FE_configuration.md
• administration/management/BE_configuration.md
• sql-reference/System_variable.mdKenneth Oh
09/19/2026, 4:33 AMRocky
09/19/2026, 4:33 AMPARTITION BY HASH(id) for materialized views (MVs). Partitioning in StarRocks MVs is primarily designed for Range or List partitioning (typically date-based) to enable incremental refreshes.
Here is why you are likely hitting limitations and how to resolve the OOM/Timeout issues for your large Iceberg dimension tables.
1. The "Partition Alignment" Rule
In StarRocks, a partitioned materialized view can only be created if the base table (your Iceberg table) is also partitioned.
* If the Iceberg table is not partitioned: You cannot create a partitioned MV on it. StarRocks will treat it as a single unit and try to refresh the entire table in one query, leading to the OOMs and timeouts you are seeing.
* Supported Iceberg Transforms: Even if your Iceberg table is partitioned, StarRocks currently only supports mapping partitions using identity, year, month, day, or hour. The Iceberg bucket (hash) transform is explicitly not supported for MV partition mapping.
2. How to handle large Dimension Table replication
If your dimension table is too large to refresh in a single transaction, here are the recommended strategies:
A. Manual "Sharded" Ingestion (Best for Non-Partitioned Tables)
Since you cannot use an MV to refresh in chunks for a non-partitioned base table, use a StarRocks Task with manual batching.
1. Create a standard StarRocks table (Duplicate or Primary Key) instead of an MV.
2. Partition this table by a "shard" ID (e.g., id % 10).
3. Create a task to load data in chunks:
sql
-- Example: Loading in 10 batches to prevent OOM
INSERT INTO target_table SELECT * FROM iceberg_catalog.db.dim_table WHERE abs(hash(id)) % 10 = 0;
-- (Repeat for 1 through 9)
B. Optimize the MV Refresh (If you must use an MV)
If you prefer the MV for its transparent rewrite capabilities, you can try to "muscle through" the OOM by increasing resource limits specifically for the MV refresh:
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.query_mem_limit" = "17179869184", -- Increase memory limit (e.g., 16GB+)
"session.insert_timeout" = "3600" -- Increase timeout to 1 hour
);
C. Use DISTRIBUTED BY HASH (For Query Performance)
While you can't partition by hash for the refresh logic, you should definitely distribute by hash for query performance on your Shared-Nothing cluster. This ensures the dimension data is balanced across your BE nodes:
sql
CREATE MATERIALIZED VIEW your_mv
DISTRIBUTED BY HASH(id) BUCKETS 32 -- Spreads data across nodes
REFRESH ASYNC
AS SELECT ...
D. Check the "Partition Refresh Number" (If Iceberg IS partitioned)
If your Iceberg table is partitioned (e.g., by date) and you are still hitting OOM, it is because StarRocks tries to refresh all changed partitions at once. You can force it to refresh them one-by-one:
sql
ALTER MATERIALIZED VIEW your_mv_name SET ("partition_refresh_number" = "1");
Summary Recommendation
For a large, non-partitioned Iceberg dimension table, an MV is often the wrong tool because it lacks the "refresh by chunk" logic for non-partitioned sources.
* Recommended: Create a standard StarRocks table and use a Task (CREATE TASK) or a script to perform INSERT INTO ... SELECT with a WHERE clause filter to load the data in manageable batches.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
ReferencesKenneth Oh
09/19/2026, 4:35 AMRocky
09/19/2026, 4:36 AMSELECT ... FROM iceberg_catalog.db.table, the StarRocks optimizer rewrites the execution plan to SELECT ... FROM starrocks_native_mv.
Key Features & Requirements
* Data Consistency: By default, StarRocks checks if the base Iceberg table has changed. If the Iceberg data is newer than the MV, the rewrite will not happen (to avoid returning stale data).
* Staleness Tolerance: You can allow the query to use an older "cached" version of the data by setting the mv_rewrite_staleness_second property on the MV. This is common for large dimension tables.
sql
PROPERTIES (
"mv_rewrite_staleness_second" = "3600" -- Use the MV even if it's up to 1 hour out of date
)
* Partition-Level Tracking: Starting from v3.1.4, StarRocks can detect partition-level changes in Iceberg. If only one partition in Iceberg changed, StarRocks can still rewrite queries for the other "clean" partitions.
* Supported Transforms: Since v3.2.3, StarRocks supports partitioning the MV based on Iceberg transforms like identity, year, month, day, and hour.
Example
If you have an Iceberg table and create this MV:
sql
CREATE MATERIALIZED VIEW iceberg_cache_mv
DISTRIBUTED BY HASH(id)
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
AS
SELECT id, name, region FROM iceberg_catalog.db.large_dim_table;
When a user runs:
sql
SELECT name FROM iceberg_catalog.db.large_dim_table WHERE region = 'US';
StarRocks will automatically fetch the result from iceberg_cache_mv (native storage) instead of reading from S3/HDFS.
Verification
You can check if a query is being rewritten by running EXPLAIN <query>. Look for the MaterializedViewRewrite section in the output or see if the OlapScanNode points to your MV name instead of the Iceberg table.
References
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.mdKenneth Oh
09/19/2026, 4:45 AMRocky
09/19/2026, 4:45 AMmem_limit and a spill_mem_limit_threshold to trigger disk spilling instead of failing when memory is tight.
sql
CREATE RESOURCE GROUP rg_mv_limit
WITH (
"mem_limit" = "0.3", -- Limit to 30% of BE query memory
"cpu_weight" = "10", -- Give it lower CPU priority
"spill_mem_limit_threshold" = "0.7", -- Start spilling to disk at 70% of this group's limit
"concurrency_limit" = "2" -- Limit simultaneous refreshes
);
2. Bind the MV to the Resource Group
Once the group is created, attach your MV to it. This ensures the background refresh task always runs within those constraints.
* For an existing MV:
sql
ALTER MATERIALIZED VIEW your_mv_name SET ("resource_group" = "rg_mv_limit");
* During MV creation:
sql
CREATE MATERIALIZED VIEW your_mv_name
REFRESH ASYNC
PROPERTIES (
"resource_group" = "rg_mv_limit"
)
AS SELECT ...;
3. Tuning the Default MV Resource Group
If you haven't created a custom group, StarRocks uses a system-defined group called default_mv_wg. You can limit its memory globally by modifying BE configurations (requires a BE restart or using curl to update at runtime):
* default_mv_resource_group_memory_limit: Default is 0.8 (80%). Lower this (e.g., to 0.4) to restrict all MVs.
* default_mv_resource_group_spill_mem_limit_threshold: Default is 0.8. Set this to a lower value (e.g., 0.5) to trigger spilling earlier and prevent OOMs.
4. Important Session Variables for MVs
You can also set memory limits directly on the MV's refresh session via properties. This is helpful if one specific MV is the "bad actor":
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.query_mem_limit" = "8589934592", -- Limit to 8GB per refresh task
"partition_refresh_number" = "1" -- Refresh only 1 partition at a time to keep memory footprint low
);
Summary of Strategy
1. Use mem_limit to cap the memory.
2. Use spill_mem_limit_threshold to trade speed for stability (spilling to disk).
3. Use partition_refresh_number to reduce the amount of data processed in a single batch.
References
• administration/management/resource_management/resource_group.md
• administration/management/BE_configuration.md
• best_practices/audit_log_resource_group.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
References
• administration/management/resource_management/resource_group.md
• administration/management/BE_configuration.md
• best_practices/audit_log_resource_group.md
References
• administration/management/BE_configuration.md
• best_practices/audit_log_resource_group.md
• administration/management/resource_management/resource_group.mdRocky
09/19/2026, 4:45 AMKenneth Oh
09/19/2026, 4:59 AMRocky
09/19/2026, 4:59 AMINSERT OVERWRITE task that scans the entire Iceberg table.
* Single Transaction: The entire refresh must complete within a single transaction. If your Iceberg table is 1TB, StarRocks will attempt to pull, process (join/aggregate), and write 1TB of data in one go.
* Memory Pressure: Because it's a single task, all intermediate data (like hash tables for joins or state for aggregations) is allocated under one query ID, quickly hitting the memory limits of your Resource Group or BE nodes.
2. Why isn't it spilling to disk?
Even if you have disk spilling enabled for the cluster, it may not be working for your MV refresh for three main reasons:
A. Spilling is not enabled for the MV session
By default, the enable_spill variable is often false. For MVs, you must explicitly enable it in the MV's properties so the background refresh task knows to use it:
sql
ALTER MATERIALIZED VIEW your_mv_name SET ("session.enable_spill" = "true");
B. The "Scan" operator does not spill
StarRocks' spilling feature currently supports Aggregates, Sorts, and Joins.
* If your OOM happens during the Scan phase (e.g., while reading massive Parquet files or handling huge Iceberg metadata), it will not spill.
* Data lake scans require memory to buffer rows and metadata. If a single "scan range" or the metadata for the entire unpartitioned table is too large, the process will OOM before it even reaches a "spillable" operator.
C. Resource Group Thresholds
If you are using Resource Groups, the refresh task will be killed as soon as it hits the group's mem_limit. Spilling only triggers when the memory usage reaches a certain threshold (default is often 80% of the limit). If the memory spikes faster than the spill mechanism can react, the task hits the hard limit and is terminated.
3. How to fix it
If you cannot partition the MV by a specific column to reduce the refresh grain, try these steps:
1. Force Spilling & Increase Memory:
sql
ALTER MATERIALIZED VIEW your_mv_name SET (
"session.enable_spill" = "true",
"session.query_mem_limit" = "34359738368", -- Set to 32GB or higher
"session.insert_timeout" = "7200" -- 2 hour timeout
);
2. Lower Concurrency:
If multiple MVs are refreshing at once, they compete for memory. Use a dedicated resource group for MVs with concurrency_limit = 1.
3. Check Iceberg Metadata:
If the Iceberg table has many small files, the metadata scan itself can cause OOM. Ensure you have run compaction on the Iceberg table.
4. Use a "Fake" Partition (If possible):
If the Iceberg table has any column that can act as a rough partition (even if not ideal for hash), use it in a PARTITION BY clause for the MV. This allows StarRocks to refresh the table in smaller "chunks" rather than one massive 1TB block.
References
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• administration/management/resource_management/spill_to_disk.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.mdRocky
09/19/2026, 4:59 AMKenneth Oh
09/19/2026, 5:58 AMRocky
09/19/2026, 5:58 AMbucket() transform.
According to the StarRocks documentation (up to v3.5), the mapping for Iceberg partition transforms is restricted to time-based and identity transforms. The support status is as follows:
Iceberg Transform | Iceberg partition expression | Materialized view partition expression | Status
Identity | <col> | <col> | Supported
hour | hour(<col>) | date_trunc('hour', <col>) | Supported
day | day(<col>) | date_trunc('day', <col>) | Supported
month | month(<col>) | date_trunc('month', <col>) | Supported
year | year(<col>) | date_trunc('year', <col>) | Supported
bucket | bucket(<col>, <n>) | N/A | Not Supported
truncate | truncate(<col>) | N/A | Not Supported
Why this is a problem
Because StarRocks cannot align its internal partitions with the Iceberg bucket partitions, you are forced to create the Materialized View as unpartitioned. As discussed previously, this leads to:
1. Full Refreshes Only: StarRocks cannot perform incremental refreshes and must scan the entire Iceberg table every time.
2. OOM Risks: Large unpartitioned scans are processed as a single massive transaction, which is more likely to hit memory limits.
Possible Workarounds
1. Add a "Virtual" Partition to Iceberg: If you have control over the Iceberg table, adding a higher-level partition (like a date or month) would allow you to align the MV to that column instead of the bucket.
2. Manual Refresh Logic: If you know which data has changed, you could technically use a synchronous MV or a standard table with a manual INSERT INTO ... SELECT script, but you would lose the "transparent rewrite" benefits of asynchronous MVs.
3. Strict Resource Management: If you must stay unpartitioned, ensure you have spilling enabled ("session.enable_spill" = "true") and enough query memory allocated to the MV's resource group to handle the full scan.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/19/2026, 6:01 AMRocky
09/19/2026, 6:01 AMdays() or months() which transform a timestamp into a coarser grain, Identity uses the raw value of the column as the partition key.
Key Use Cases
1. Enabling Incremental Refresh for Non-Time Columns
The most common use case is when your base table is partitioned by a discrete category rather than time.
* Scenario: An Iceberg table partitioned by region_id or tenant_id.
* MV setup: PARTITION BY (region_id) using the Identity mapping.
* Benefit: If data for region_id = 5 changes in Iceberg, StarRocks detects this change at the partition level and only refreshes that specific region in the MV. Without this, it would have to re-scan the entire table.
2. Multi-Column Partition Alignment (v3.5+)
If your Iceberg table is partitioned by multiple columns (e.g., event_date and country_code), you can use Identity partitions to align the MV to that exact structure.
* Scenario: PARTITIONED BY (days(ts), country_code) in Iceberg.
* MV setup: PARTITION BY (date_trunc('day', ts), country_code).
* Benefit: This creates a granular refresh strategy where only specific "day + country" combinations are updated, significantly saving resources.
3. Data Lake Acceleration with Native Indexes
You use Identity partitions to bring data from a data lake into StarRocks while keeping the same organizational structure, which then allows you to apply StarRocks-specific optimizations.
* Scenario: Mapping a large Hive/Iceberg table to an MV.
* Benefit: Once the data is in the partitioned MV (stored as a StarRocks native table), you can add Bitmap Indexes, Bloom Filter Indexes, or use Colocate Join groups on those partition columns—none of which are available directly on the data lake.
4. Handling High-Cardinality "ID" Partitions
If you have a column like batch_id or load_date_string that is already a string/integer and correctly partitions your data, you use Identity to avoid unnecessary function calls (like date_trunc).
Comparison to your "Bucket" issue
The Identity partition is the "fix" for your current OOM problem, provided you can change the underlying Iceberg table.
* Bucket Partition (Your current issue): bucket(id, 5) is a hash. StarRocks doesn't know which rows belong to which bucket without re-calculating the hash for the whole table.
* Identity Partition: If you changed the Iceberg table to be partitioned by id (if cardinality is low enough) or a different column like category_id, StarRocks would see the folders in the data lake and know exactly which ones to refresh.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.mdKenneth Oh
09/19/2026, 6:03 AMRocky
09/19/2026, 6:03 AMbucket() or truncate() transforms.
1. Partitioning vs. Bucketing in StarRocks
It is important to distinguish between how StarRocks uses these terms:
* Partitioning (PARTITION BY): This is the logical level where StarRocks tracks data changes for MVs. Currently, this only supports Identity (direct column mapping) or Time-based (date_trunc) transforms.
* Bucketing/Distribution (DISTRIBUTED BY HASH): This is the physical sharding of data within a partition. You can (and should) use DISTRIBUTED BY HASH(id) for your MV, but this only affects how data is stored on disk; it does not help StarRocks perform an incremental/partitioned refresh based on your Iceberg buckets.
2. Is it on the roadmap?
While StarRocks is rapidly improving Iceberg integration (v4.0 recently introduced better handling of Iceberg "hidden partitions" and metadata statistics), the ability to use bucket() as a partitioning key for an MV is not yet available.
The complexity lies in the fact that StarRocks' internal partitioning engine is based on Range or List values. A hash-based bucket from Iceberg doesn't map cleanly to these types without StarRocks implementing a specific "Hash Partition" type for MVs, which is currently a feature request in the community.
3. What you can do now
Since you cannot align by bucket, your MV will remain "unpartitioned" from a refresh perspective. To prevent the OOM issues you're seeing:
1. Use `DISTRIBUTED BY HASH(id)`: While this doesn't help the refresh logic, it ensures the data is evenly spread across your BE nodes once it is loaded.
2. Enable Spilling: As mentioned before, ensure session.enable_spill = true is set in the MV properties.
3. Partition by a different column: If your Iceberg table has any other column that is an Identity or Date type (even if it's not the primary way you query), partitioning the MV by that column will force StarRocks to process the table in smaller chunks rather than one giant unpartitioned blob.
4. Monitor v4.x Releases: The community is actively working on "Hidden Partition" support. You can track progress on the StarRocks GitHub.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• best_practices/bucketing.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.mdKenneth Oh
09/19/2026, 6:10 AMRocky
09/19/2026, 6:10 AMid % 5 as the partition key.
There are three main reasons why this approach will not work in StarRocks:
1. Partition Alignment Requirement
In StarRocks, a partitioned MV is only "partitioned" if its partitions align with the partitions of the base table (in your case, the Iceberg table). This alignment is what allows StarRocks to:
* Identify which specific partitions have changed in the base table.
* Refresh only the corresponding partitions in the MV (Incremental Refresh).
Since your Iceberg table is partitioned by bucket(id) and StarRocks does not currently support the bucket transform for MV alignment, it cannot "link" the metadata of the Iceberg buckets to a custom column like id % 5.
2. Supported Partition Expressions
The PARTITION BY clause in a CREATE MATERIALIZED VIEW statement only supports a limited set of functions designed for partition alignment, specifically:
* Time-based: date_trunc(), time_slice().
* Type conversion: str2date() (used when the base table has string dates).
* Identity: Directly using the base table's partition column.
Arbitrary mathematical expressions like modulo (%) or hash functions are not supported within the PARTITION BY clause of an MV.
3. Base Table Constraint
StarRocks requires that the column used in the MV's PARTITION BY clause must be a partition column in the base table.
* If you create a new column in your SELECT statement (e.g., SELECT id % 5 AS part_num), that column part_num is considered a computed column, not a partition column of the base table.
* StarRocks will fail to create the MV, typically returning an error stating that the partition relationship cannot be mapped.
Recommendation
If your goal is to avoid the memory/performance issues of an unpartitioned MV:
1. Change the Iceberg Partitioning: If possible, add a day or month partition to the Iceberg table. You can then use PARTITION BY date_trunc('day', your_date_col) in the MV.
2. Use Bucketing for Distribution: While you cannot partition by id % 5, you should still use DISTRIBUTED BY HASH(id) in the MV definition. This ensures that even if the MV is refreshed as one large task, the data is physically distributed across your nodes efficiently for future queries.
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
ReferencesKenneth Oh
09/19/2026, 3:54 PM026-09-19 15:47:19.128Z WARN (pool-68-thread-1|167) [RpcTimerTask.run():73] correlationId:15438 timeout with bound channel =>[id: 0xeaa8aac1, L:/10.0.71.99:53688 - R:/10.0.58.20:8060]
2026-09-19 15:47:19.128Z WARN (starrocks-mysql-nio-pool-26|4053) [FragmentInstanceExecState.waitForDeploymentCompletion():299] catch a execute exception
java.util.concurrent.ExecutionException: A error occurred: errorCode=62 errorMessage:method request time out, please check 'onceTalkTimeout' property. current value is:60000(MILLISECONDS) correlationId:15438 timeout with bound channel =>[id: 0xeaa8aac1, L:/10.0.71.99:53688 - R:/10.0.58.20:8060]
at com.baidu.jprotobuf.pbrpc.client.ProtobufRpcProxy$2.get(ProtobufRpcProxy.java:578)
at com.starrocks.qe.scheduler.dag.FragmentInstanceExecState.waitForDeploymentCompletion(FragmentInstanceExecState.java:291)
at com.starrocks.qe.scheduler.Deployer.waitForDeploymentCompletion(Deployer.java:326)
at com.starrocks.qe.scheduler.Deployer.deployFragments(Deployer.java:213)
at com.starrocks.qe.scheduler.dag.AllAtOnceExecutionSchedule.schedule(AllAtOnceExecutionSchedule.java:149)
at com.starrocks.qe.DefaultCoordinator.deliverExecFragments(DefaultCoordinator.java:688)
at com.starrocks.qe.DefaultCoordinator.startScheduling(DefaultCoordinator.java:581)
at com.starrocks.qe.scheduler.Coordinator.execWithQueryDeployExecutor(Coordinator.java:108)
at com.starrocks.qe.StmtExecutor.handleQueryStmt(StmtExecutor.java:1948)
at com.starrocks.qe.StmtExecutor.execute(StmtExecutor.java:1073)
at com.starrocks.qe.ConnectProcessor.executeQueryAttempt(ConnectProcessor.java:664)
at com.starrocks.qe.ConnectProcessor.runWithParserStageRetry(ConnectProcessor.java:553)
at com.starrocks.qe.ConnectProcessor.handleQuery(ConnectProcessor.java:490)
at com.starrocks.qe.ConnectProcessor.dispatch(ConnectProcessor.java:899)
at com.starrocks.qe.ConnectProcessor.processOnce(ConnectProcessor.java:1298)
at com.starrocks.mysql.nio.MySQLReadListener.handleRequest(MySQLReadListener.java:152)
at com.starrocks.mysql.nio.MySQLReadListener.lambda$handleEvent$0(MySQLReadListener.java:92)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: com.baidu.jprotobuf.pbrpc.ErrorDataException: A error occurred: errorCode=62 errorMessage:method request time out, please check 'onceTalkTimeout' property. current value is:60000(MILLISECONDS) correlationId:15438 timeout with bound channel =>[id: 0xeaa8aac1, L:/10.0.71.99:53688 - R:/10.0.58.20:8060]
at com.baidu.jprotobuf.pbrpc.client.ProtobufRpcProxy.doWaitCallback(ProtobufRpcProxy.java:651)
at com.baidu.jprotobuf.pbrpc.client.ProtobufRpcProxy.access$0(ProtobufRpcProxy.java:611)
at com.baidu.jprotobuf.pbrpc.client.ProtobufRpcProxy$2.get(ProtobufRpcProxy.java:576)
... 19 more
2026-09-19 15:47:19.128Z WARN (starrocks-mysql-nio-pool-26|4053) [FragmentInstanceExecState.waitForDeploymentCompletion():325] exec plan fragment failed, errmsg=exec rpc error. backend [id=174100] [host=starrocks-etl-cn-1.starrocks-etl-cn-search.starrocks-etl.svc.cluster.local], code=THRIFT_RPC_ERROR, fragmentId=F01, backend=starrocks-etl-cn-1.starrocks-etl-cn-search.starrocks-etl.svc.cluster.local:9060
2026-09-19 1Rocky
09/19/2026, 3:54 PMstarrocks-etl-cn-1). Specifically, the FE waited 60 seconds (the default value) for the node to acknowledge the query plan, but received no response.
1. Root Cause
The error errorCode=62 errorMessage:method request time out comes from the underlying RPC library. In StarRocks, this happens when the FE's Deployer cannot successfully send the execution plan to a Backend/Compute Node (BE/CN) within the configured limit.
The specific parameter controlling this in the FE is brpc_send_plan_fragment_timeout_ms, which defaults to 60000 (60 seconds).
2. Common Reasons
* High BE/CN Load: If the Compute Node is under extreme CPU pressure or experiencing long Garbage Collection (GC) pauses, it may not be able to process the incoming RPC request in time.
* Large Query Plan: If the SQL query is extremely complex (e.g., thousands of partitions, massive IN clauses, or hundreds of joins), the serialized "plan fragment" becomes very large. Sending and deserializing this large plan can exceed the 60s timeout.
* Network Congestion: Transient network latency or packet loss between the FE and the CN.
* RPC Thread Pool Exhaustion: The BE/CN may have run out of available service threads to handle new incoming requests.
3. Recommended Actions
Immediate Fix: Increase Timeout
If your queries are complex or your network is slow, you can increase the timeout on the FE. This is a dynamic configuration:
sql
-- Increase to 120 seconds
ADMIN SET FRONTEND CONFIG ("brpc_send_plan_fragment_timeout_ms" = "120000");
Troubleshooting the Node
Check the health of the node mentioned in the log (10.0.58.20):
1. Check Resources: Monitor CPU and Memory usage on starrocks-etl-cn-1. If CPU is consistently at 100%, query deployment will likely fail.
2. Check BE/CN Logs: Look at <http://be.INFO|be.INFO> or be.warn.log on that specific node around 2026-09-19 15:47:19 to see if there are "heartbeat timeout" or "process busy" warnings.
3. Check Connectivity: Ensure there is no firewall or MTU issue between the FE (10.0.71.99) and CN (10.0.58.20) on the RPC port (default 8060).
Optimize the Query
If this only happens with one specific query:
* Check if the query is touching a massive number of partitions.
* Simplify the query or use a Materialized View to reduce the plan complexity.
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.md
• sql-reference/System_variable.md
References
• administration/management/FE_configuration.md
• administration/management/BE_configuration.mdKenneth Oh
09/19/2026, 3:55 PMRocky
09/19/2026, 3:55 PMr8g) nodes suggests that the issue is likely related to the architecture shift or the network configuration of these new Nitro-based instances, rather than the "health" of the nodes themselves.
Here are the three most likely causes and how to fix them:
1. Architecture Mismatch (Emulation Slowdown)
The r8g instances use the ARM64 architecture. If you moved your existing Kubernetes pods to these nodes without changing the Docker image, they might be running the x86_64 version of StarRocks via QEMU/binfmt_misc emulation.
* The Symptom: Emulation typically results in a 10x–20x performance penalty. An RPC call that usually takes 5ms now takes 100ms; a complex query plan deployment that takes 5 seconds now takes over 60 seconds, triggering the errorCode=62 timeout.
* How to check: Run uname -m inside one of your StarRocks pods. If it says x86_64 while running on an r8g node, you are using emulation.
* The Fix: Ensure you are using the ARM64 specific StarRocks images. In your Docker registry or Helm chart, switch to images tagged with -arm64 (e.g., starrocks/fe-ubuntu:3.x.x-arm64).
2. Network MTU / Nitro Enclaves Issues
The r8g instances are built on the latest AWS Nitro System. If your new nodes are in a different subnet or VPC with different MTU (Maximum Transmission Unit) settings, large query plans (which can be several megabytes) may be getting fragmented or dropped.
* The Symptom: Small queries work, but complex queries (large plans) consistently time out at 60s.
* How to check: Check if jumbo frames (MTU 9001) are enabled on your AWS VPC and that the nodes/network interfaces support it.
* The Fix:
* Ensure the MTU is consistent across FE and CN/BE nodes.
* If you suspect large plans are the issue, you can temporarily increase the FE timeout to see if it eventually succeeds:
sql
ADMIN SET FRONTEND CONFIG ("brpc_send_plan_fragment_timeout_ms" = "120000");
3. JProtobuf / JNI Compatibility
The stack trace shows the error occurring in com.baidu.jprotobuf.pbrpc, which is the Java-to-C++ RPC bridge StarRocks uses. This library uses native JNI code to handle serialization.
* The Issue: Older versions of the StarRocks FE might be bundled with JNI libraries that are not optimized for Graviton4 or have issues with the specific instruction set of the r8g (Neoverse-V2 cores).
* The Fix:
* If you are on an older version of StarRocks (e.g., early v2.5 or v3.0), upgrade to a more recent point release (v3.1.x, v3.2.x, or v3.3.x). Recent versions have significant stability improvements for Graviton/ARM environments.
Summary Checklist
1. Verify Binary: Are you running the arm64 native image? (Most likely cause).
2. Increase Timeout: Set brpc_send_plan_fragment_timeout_ms to 120000 or higher to see if the query plan ever deploys.
3. Check BE Logs: Check the <http://be.INFO|be.INFO> logs on the target node (10.0.58.20) to see if it reports "receive plan fragment" at all, or if it shows high CPU/GC activity.
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.md
• sql-reference/System_variable.md
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.mdRocky
09/19/2026, 3:55 PMKenneth Oh
09/19/2026, 4:26 PMRocky
09/19/2026, 4:26 PMSHOW MATERIALIZED VIEWS are taking exactly 60 seconds (the default timeout), it indicates a fundamental communication or architectural failure between your FE and BE/CN nodes following the migration to Graviton4 (r8g) instances.
Because this happened immediately after moving to r8g.16xlarge, the cause is almost certainly one of the following three:
1. Architecture Mismatch (Most Likely)
The r8g instances are ARM64 (Graviton4). If your Docker image or binary is x86_64, it is running under QEMU emulation.
* The Problem: Emulation is extremely slow (often 10x-50x slower). A metadata query that takes 200ms natively will take 10-20 seconds; any query requiring BE coordination will easily hit the 60s timeout.
* Verification: Run uname -m inside the FE and BE pods. If it returns x86_64 while running on an r8g node, you are in emulation.
* Fix: You must use the ARM-specific images (e.g., starrocks/fe-ubuntu:3.x.x-arm64).
2. Zombie Nodes or IP Mismatches
When moving pods to new nodes, the Frontend often retains the old IPs of the Backends in its metadata.
* The Problem: When you run SHOW MATERIALIZED VIEWS, the FE attempts to reach every BE to check its status or data size. If it tries to reach an old IP that no longer exists, it will hang for exactly 60 seconds (brpc_send_plan_fragment_timeout_ms) before returning an error or a partial result.
* Verification: Run SHOW BACKENDS;.
* Check the IP column. Are these the current IPs of your pods?
* Check the Alive column. If any are false, the FE is timing out trying to talk to them.
* Fix: Drop the old BE/CN nodes using ALTER SYSTEM DROP BACKEND "old_ip:port"; and add the new ones. Ensure priority_networks in your fe.conf and be.conf is updated to match the new VPC subnet.
3. Security Group / Port Blocking
The r8g instances likely belong to a new AWS Launch Template or Security Group.
* The Problem: StarRocks requires several ports to be open between all nodes (FE-to-FE, FE-to-BE, and BE-to-BE). If the FE cannot reach the BE's Heartbeat port (9050) or RPC port (8060), the FE metadata threads will hang.
* Verification: Try to telnet <BE_IP> 8060 from the FE pod. If it hangs, it’s a network/firewall issue.
* Fix: Ensure the Security Group allows all traffic between nodes on these ports:
* FE: 9010, 9020, 9030
* BE/CN: 9050, 8060, 9060
Recommended Action Plan
1. Run SHOW BACKENDS; — This is the fastest way to see if the FE can "see" the workers. If they are Alive: false, it's a network/IP issue.
2. Verify the Architecture — Ensure your pod is running the -arm64 version of StarRocks.
3. Check FE Logs (fe.warn.log) — Look for HeartbeatMgr errors. It will explicitly say "failed to send heartbeat to 10.x.x.x".
References
• administration/management/FE_configuration.md
• faq/Deploy_faq.md
• administration/management/BE_configuration.md
• sql-reference/sql-statements/materialized_view/SHOW_MATERIALIZED_VIEWS.md
• deployment/helm.md
References
• administration/management/FE_configuration.md
• faq/Deploy_faq.md
• administration/management/BE_configuration.md
• sql-reference/sql-statements/cluster-management/backend_node/SHOW_BACKENDS.mdRocky
09/19/2026, 4:26 PMKenneth Oh
09/19/2026, 4:27 PMRocky
09/19/2026, 4:27 PMSHOW MATERIALIZED VIEWS are taking exactly 60 seconds after a vertical scale-up (2xl to 16xl) is a strong indicator that the Frontend (FE) is trying to communicate with a node that is no longer reachable or is not responding.
In StarRocks, 60,000ms (60 seconds) is the default value for brpc_send_plan_fragment_timeout_ms. Although SHOW commands are metadata-driven, several of them (especially for Materialized Views) trigger background checks or status updates from the Backends (BE) or Compute Nodes (CN) to report row counts and refresh states.
Here is why this is likely happening on your new r8g.16xlarge nodes:
1. Stale IP Addresses (Most Likely)
When you moved your pods to new, larger nodes, they likely received new internal IP addresses.
* The Issue: If the FE still has the old IPs registered for the BE/CN nodes in its metadata, it will attempt to reach them for status updates (like row counts for the MV) and wait exactly 60 seconds for the RPC to time out.
* How to fix:
1. Run SHOW BACKENDS; or SHOW COMPUTE NODES;.
2. Check the IP column and the Alive column.
3. If Alive is false or the IPs don't match your current pods, you must drop the old nodes and add the new ones:
sql
-- Drop old
ALTER SYSTEM DROP COMPUTE NODE "old_ip:9050";
-- Add new
ALTER SYSTEM ADD COMPUTE NODE "new_ip:9050";
2. Security Group / Port Blocking
The r8g.16xlarge instances may be using a different Security Group or have different rules than your previous 2xlarge instances.
* The Issue: StarRocks requires open communication on several ports (Heartbeat: 9050, RPC: 8060, Thrift: 9060). If the FE can't reach the BEs on these ports, the metadata threads will hang for the duration of the timeout.
* Check: From an FE pod, try to telnet <BE_IP> 8060. If it times out, your AWS Security Group is blocking the traffic.
3. FE Leader Forwarding
If you have multiple FEs, all metadata-modifying or complex SHOW queries are forwarded to the Leader FE.
* The Issue: If the Follower FE cannot reach the Leader FE (due to the same IP/Network issues mentioned above), the query will hang for 60s.
* Check: Run SHOW FRONTENDS; and ensure the Leader is Alive and reachable from the node you are connected to.
4. ENA Express / MTU Mismatch
The r8g.16xlarge instance supports ENA Express and much higher network throughput.
* The Issue: If your VPC has a specific MTU (e.g., 9001 for Jumbo Frames) and the new nodes are defaulting to a different MTU (or if ENA Express is causing packet fragmentation for large metadata payloads), RPC calls can hang.
* Fix: Ensure the MTU is consistent (usually 9001 on AWS) across all nodes in the cluster.
5. Accidental Architecture Switch (x86 vs ARM64)
Even though you were already on Graviton (r8g), verify that the container image used for the redeploy is the ARM64 native version.
* Check: Run uname -m inside the pod. If it shows x86_64, the node is using QEMU emulation to run the code, which is slow enough to cause 60s timeouts on almost any task. You should be using the image tagged with -arm64.
Immediate Troubleshooting Step:
Run SHOW BACKENDS; right now. If any rows show Alive: false, that is the source of your 60-second delay. The FE is waiting for those "dead" nodes to respond before giving up.Kenneth Oh
09/21/2026, 2:21 AMRocky
09/21/2026, 2:21 AMgrpc.GetShard indicates that the internal gRPC communication between your BE/CN and the FE's StarOS service is timing out before all 1024 shards can be processed.
1. Increase StarOS/Starlet gRPC Timeout
The default timeout for internal StarOS metadata calls is often too short for high-bucket-count operations. Increase this on your FE nodes:
sql
-- Increase the StarMgr gRPC timeout (Default is usually 5 or 10s)
ADMIN SET FRONTEND CONFIG ("starmgr_grpc_timeout_seconds" = "30");
-- Also increase the general tablet creation timeout
ADMIN SET FRONTEND CONFIG ("tablet_create_timeout_second" = "30");
2. Increase BE/CN Parallelism
Since you have moved to large r8g.16xlarge nodes, your BEs can handle more parallel tablet creation tasks. Increase the worker count in be.conf (requires a restart of BE/CN):
conf
# be.conf or cn.conf
create_tablet_worker_count = 16
3. Review Bucket Count (Recommendation)
1024 buckets for a single Materialized View is very high unless your data volume per partition is in the multi-terabyte range.
* The Problem: In shared-data mode, every bucket corresponds to a file metadata operation in S3. 1024 buckets means every refresh or query must manage 1024+ files, which introduces high metadata overhead and latency.
* The Fix: Unless you have hundreds of Terabytes in this MV, consider reducing the bucket count to a smaller number (e.g., 64, 128, or 256). StarRocks performance is often better with fewer, larger tablets (aim for 1GB–10GB per tablet) rather than many tiny ones.
4. Check for StarOS Worker Thread Exhaustion
With 16xlarge nodes, you might be hitting the thread limit on the FE for StarOS requests. Check the FE config:
sql
-- Check current value
ADMIN SHOW FRONTEND CONFIG LIKE '%starmgr_grpc_server_max_worker_threads%';
-- If it is low (e.g., 256), increase it to 1024
ADMIN SET FRONTEND CONFIG ("starmgr_grpc_server_max_worker_threads" = "1024");
Summary of what happened
When you scaled up to r8g.16xlarge, the system became capable of pushing much higher concurrency. When you triggered the MV creation with 1024 buckets, the BEs flooded the FE with shard creation requests. The FE's StarOS service couldn't acknowledge the GetShard requests within the default 5-10 second window, leading to the Deadline Exceeded error.
Try setting starmgr_grpc_timeout_seconds to 30 and reducing the bucket count to 256 if possible.
References
• faq/shared_data_faq.md
• administration/management/FE_configuration.md
• administration/management/BE_configuration.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
References
• faq/shared_data_faq.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
• deployment/shared_data/azure.md
• deployment/shared_data/minio.md
• deployment/shared_data/s3.mdKenneth Oh
09/21/2026, 7:27 PMRocky
09/21/2026, 7:27 PMr8g (or r8gd) instances, you should definitely use it for spilling, as join spills are extremely I/O intensive.
How to configure it
To ensure spills use your NVMe volumes, follow these steps:
1. Identify your NVMe Mount Point
Ensure your NVMe volume is formatted and mounted (e.g., at /mnt/nvme0/).
2. Update be.conf (or cn.conf)
Add or update the following parameter to point to your NVMe directory:
properties
# Separate multiple paths with semicolons if you have multiple volumes
spill_local_storage_dir=/mnt/nvme0/starrocks_spill
_Note: If you are using an older version (pre-v3.0), the parameter was query_scratch_dirs, but spill_local_storage_dir is the standard for current versions._
3. Restart the BE/CN
You must restart the Backend or Compute Node for this configuration to take effect.
4. Enable Spilling in your Session
Even with the path configured, spilling is disabled by default to protect performance. You must enable it for your queries:
sql
SET enable_spill = true;
-- Optional: 'auto' triggers spill when memory is nearly full
SET spill_mode = 'auto';
Why NVMe is recommended
* Throughput: Join spilling involves writing and reading back massive amounts of intermediate hash table data. NVMe provides the high IOPS and throughput required to prevent the query from stalling.
* Isolation: The official documentation strongly recommends using a different disk for spilling than the one used for your primary data (storage_root_path). This prevents spill I/O from slowing down standard data scans.
Summary
If your NVMe is just "present" on the OS but not specified in be.conf, StarRocks will likely spill to its default installation directory (usually on the OS drive/EBS), which will be significantly slower.
References
• administration/management/BE_configuration.md
• administration/management/resource_management/spill_to_disk.md
• sql-reference/System_variable.md
References
• administration/management/BE_configuration.md
• administration/management/resource_management/spill_to_disk.md
• sql-reference/System_variable.mdKenneth Oh
09/21/2026, 7:54 PMRocky
09/21/2026, 7:54 PMdatacache_enable is set to true by default.
2. Iceberg-Specific Metadata Caching
In addition to the raw data blocks on the BE, StarRocks also caches Iceberg metadata (manifests, snapshots, etc.) to speed up query planning:
* FE Memory/Disk Cache: The Frontend (FE) caches Iceberg metadata in a two-level system (Memory + Local Disk).
* Asynchronous Refresh: StarRocks can automatically poll the Iceberg Metastore (Glue/Hive) to refresh this cache in the background so planning stays fast even as the table changes.
3. Key Configurations for your BEs
If you are using r8g.16xlarge nodes with NVMe, you should ensure the cache is utilizing those disks for maximum performance:
* storage_root_path: In be.conf, this defines where the cache is stored. Point this to your NVMe mount.
* datacache_disk_size: You can limit how much disk space is used for the cache (e.g., datacache_disk_size = 500G).
* datacache_mem_size: You can also allocate a portion of BE RAM to cache data blocks in memory for even faster access (default is usually 10% of total memory).
4. How to verify it's working
You can check if your queries are hitting the cache by looking at the Query Profile. Run your query, then run get_query_profile('query_id') and look for these metrics:
* `DataCacheReadBytes`: How much data was read from the local disk cache.
* `DataCacheWriteBytes`: How much data was fetched from S3 and written to the cache.
Tip: If you see DataCacheReadBytes increasing on the second run of the same query, the cache is working correctly.
References
• using_starrocks/caching/block_cache.md
• administration/management/BE_configuration.md
• data_source/data_cache.md
• data_source/catalog/iceberg/iceberg_catalog.mdKenneth Oh
09/21/2026, 10:39 PMRocky
09/21/2026, 10:39 PMdefault_catalog) is not supported via a single RENAME or SWAP command.
In StarRocks, the RENAME and SWAP WITH operations for both tables and materialized views are restricted to the same database. If you attempt to rename an MV into a different database (e.g., ALTER MATERIALIZED VIEW db1.mv1 RENAME db2.mv1), the operation will fail.
Here are the three ways to achieve this, depending on whether you want to keep the existing data:
Method 1: Backup and Restore (Best for keeping data)
If your MV is large and you do not want to trigger a full refresh (rebuild), you can use the StarRocks Backup/Restore feature to move the physical data tablets to a new database.
1. Create a repository (S3, HDFS, etc.) if you don't have one.
2. Back up the MV in the source database:
sql
BACKUP DATABASE source_db SNAPSHOT mv_snapshot ON (MATERIALIZED VIEW my_mv);
3. Restore to the target database:
sql
RESTORE SNAPSHOT mv_snapshot FROM your_repo DATABASE target_db ON (MATERIALIZED VIEW my_mv);
Method 2: Drop and Recreate (Simplest, but requires refresh)
If you don't mind the time it takes to re-calculate the data, this is the most straightforward approach.
1. Get the DDL:
sql
SHOW CREATE MATERIALIZED VIEW source_db.my_mv;
2. Run the DDL in the target database:
sql
USE target_db;
-- Paste the DDL here
3. Drop the old one:
sql
DROP MATERIALIZED VIEW source_db.my_mv;
Method 3: Cross-Cluster Data Migration Tool
If you are moving many objects or migrating between clusters, the official StarRocks Data Migration Tool supports synchronizing MV schemas and building statements, though it generally does not synchronize the physical data of the MV itself (you would still need to refresh).
Summary of Restrictions
* RENAME: Only changes the name within the current database.
* SWAP WITH: Atomic exchange only works if both MVs reside in the same database.
* Permissions: You will need CREATE MATERIALIZED VIEW privileges on the target database and SELECT privileges on all base tables involved in the MV definition.
References
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• administration/management/Backup_and_restore.md
• knowledge/usage/best-practice.md (Section 128: SWAP WITH does not support cross-db)
References
• administration/management/Backup_and_restore.md
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/Materialized_view.md
• administration/data_migration_tool.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• sql-reference/sql-statements/materialized_view/SHOW_ALTER_MATERIALIZED_VIEW.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.mdRocky
09/21/2026, 10:39 PMKenneth Oh
09/21/2026, 10:52 PMRocky
09/21/2026, 10:52 PMSHOW GRANTS and the recommended method using Roles.
Method 1: Manual Copy (Quickest for one-time)
You can extract the exact SQL commands used for the existing user and run them for the new user.
1. View existing grants:
sql
-- Replace 'old_user'@'%' with the actual user identity
SHOW GRANTS FOR 'old_user'@'%';
2. Copy and Execute:
The output will show a list of statements in the Grants column (e.g., GRANT SELECT ON DATABASE db1 TO USER 'old_user'@'%').
* Copy these statements.
* Change the username to your 'new_user'@'%'.
* Run the modified statements.
Method 2: Role-Based (Best Practice)
Instead of granting permissions directly to users, you should grant them to a Role. This makes it easy to add more users with the same permissions later.
1. Create a Role:
sql
CREATE ROLE my_common_role;
2. Grant privileges to the Role:
Instead of individual users, point your grants to the role:
sql
GRANT SELECT, INSERT ON ALL TABLES IN DATABASE mydb TO ROLE my_common_role;
3. Assign the Role to both users:
sql
GRANT my_common_role TO USER 'old_user'@'%';
GRANT my_common_role TO USER 'new_user'@'%';
Note: You may also want to set it as the default role:
ALTER USER 'new_user'@'%' DEFAULT ROLE my_common_role;
Bonus: Inspecting via System Tables
If you want to see all user privileges in a table format (useful for scripting or auditing), you can query the system views:
sql
SELECT * FROM sys.grants_to_users WHERE GRANTEE = "'old_user'@'%'";
Important Notes
* Admin Rights: You must have the user_admin role to view other users' grants or create new roles.
* Impersonation: If you specifically want one user to be able to "act" as another without copying all permissions, you can use:
GRANT IMPERSONATE ON USER 'old_user'@'%' TO USER 'new_user'@'%';
References
• sql-reference/sql-statements/account-management/SHOW_GRANTS.md
• sql-reference/sys/grants_to_users.md
• administration/user_privs/authorization/User_privilege.md
• sql-reference/sql-statements/account-management/GRANT.md
References
• sql-reference/sql-statements/account-management/SHOW_GRANTS.md
• sql-reference/sys/grants_to_users.md
• sql-reference/sql-statements/account-management/SHOW_AUTHENTICATION.md
• sql-reference/sys/grants_to_roles.mdKenneth Oh
09/21/2026, 11:05 PMRocky
09/21/2026, 11:05 PMdefault_catalog, you need to combine two types of grants: USAGE on the catalog itself and SELECT on the materialized views.
In StarRocks, the USAGE privilege is not applied directly to materialized views; instead, it is required at the Catalog level to allow the user to access any objects within that catalog.
Step-by-Step Commands
Run the following as a user with user_admin or root privileges:
1. Switch to the target catalog (optional if already in default_catalog):
sql
SET CATALOG default_catalog;
2. Grant usage on the catalog:
This allows the user to "see" and enter the default_catalog.
sql
GRANT USAGE ON CATALOG default_catalog TO USER 'your_user'@'%';
3. Grant select on all materialized views:
This allows the user to query any existing and future materialized views in all databases within that catalog.
sql
GRANT SELECT ON ALL MATERIALIZED VIEWS IN ALL DATABASES TO USER 'your_user'@'%';
Key Details
* Scope: ALL MATERIALIZED VIEWS IN ALL DATABASES is a global-style grant that automatically applies to MVs created in the future.
* Privilege Names: Materialized views support SELECT, ALTER, REFRESH, and DROP. There is no specific USAGE privilege for an MV object; SELECT is what allows a user to query it.
* Roles: It is highly recommended to grant these to a Role instead of a user for easier management:
sql
GRANT SELECT ON ALL MATERIALIZED VIEWS IN ALL DATABASES TO ROLE your_role;
References
• sql-reference/sql-statements/account-management/GRANT.md
• administration/user_privs/authorization/User_privilege.md
• using_starrocks/async_mv/Materialized_view.md
References
• sql-reference/sql-statements/account-management/GRANT.md
• data_source/catalog/hive_catalog.md
• administration/user_privs/authorization/User_privilege.mdKenneth Oh
09/21/2026, 11:07 PMRocky
09/21/2026, 11:08 PMALTER USER command (note that this uses the username, not the user_identity like user@'%'):
sql
-- Set default catalog and database for user 'jack'
ALTER USER 'jack' SET PROPERTIES (
"catalog" = "external_hive_catalog",
"database" = "analytics_db"
);
To clear these properties later:
sql
ALTER USER 'jack' SET PROPERTIES ("catalog" = "", "database" = "");
2. Set via Connection String (JDBC/MySQL)
If you want different users of the same role to land in specific databases, the most common practice is to define the catalog and database in the client’s connection string.
* MySQL Client:
bash
mysql -h <FE_IP> -P 9030 -u <username> -p -D default_catalog.my_db
* JDBC String:
text
jdbc:mysql://<FE_IP>:9030/default_catalog.my_db
3. Using Roles for Activation
While roles don't hold the "default database," they do control whether a user is allowed to access that database. If you want a user to automatically have the permissions associated with a specific role (which might be tied to a specific database) upon login, you use:
sql
SET DEFAULT ROLE 'my_admin_role' TO 'jack'@'%';
Summary of Scope
Feature | Supported at Role Level? | Supported at User Level?
Privileges | Yes (GRANT ... TO ROLE) | Yes (GRANT ... TO USER)
Default Catalog/DB | No | Yes (v3.3.3+)
Default Roles | No | Yes
Max Connections | No | Yes
References
• sql-reference/sql-statements/account-management/ALTER_USER.md
• sql-reference/System_variable.md
• administration/user_privs/authorization/User_privilege.md
References
• administration/user_privs/authorization/User_privilege.md
• sql-reference/sql-statements/account-management/ALTER_USER.md
• sql-reference/sql-statements/account-management/DROP_ROLE.md
• sql-reference/sql-statements/cluster-management/nodes_processes/ALTER_SYSTEM.mdKenneth Oh
09/23/2026, 4:03 AMRocky
09/23/2026, 4:04 AMGRANT SELECT ON ALL MATERIALIZED VIEWS IN ALL DATABASES TO USER 'jack'.
* Ranger RBAC: You create an Access Policy in the Ranger Admin UI.
* Resource: Select "Materialized View".
* Scope: Use the wildcard * for Catalog, Database, and Materialized View to mimic "ALL".
* Permissions: Select SELECT.
3. Comparison of Grant Capabilities
Feature | Native StarRocks RBAC | StarRocks + Apache Ranger
Catalog/DB/Table Level | Yes | Yes
Materialized Views | Yes | Yes (supported in StarRocks Ranger plugin)
Wildcards (ALL) | Yes (ALL TABLES) | Yes (using * in Ranger policies)
Row-Level Security | Yes | Yes (defined as policies in Ranger)
Data Masking | Yes | Yes (defined as policies in Ranger)
IDP Group Mapping | Yes (v3.5+) | Yes (via Group Provider)
4. Important Limitations
* Hybrid Restriction: If you use Security Integration (the feature that connects to Keycloak/JWT), StarRocks currently requires you to use Apache Ranger for authorization. You cannot use the native GRANT SQL commands for users authenticated via an IDP; permissions must be defined in Ranger.
* Command Usage: Once you set access_control = ranger in fe.conf, the native GRANT and REVOKE SQL statements are disabled for those resources. You must manage them through the Ranger UI or Ranger's REST API.
Summary
You can achieve the same granular permissions (Select on all MVs, usage on catalogs, etc.), but you will no longer use the SQL GRANT syntax. Instead, you will define matching policies in the Ranger StarRocks Service.
References
• administration/user_privs/authorization/ranger_plugin.md
• administration/user_privs/group_provider.md
• best_practices/authentication_authorization.md
• sql-reference/sql-statements/account-management/GRANT.md
References
• administration/user_privs/authorization/privilege_item.md
• sql-reference/sql-statements/account-management/GRANT.md
• administration/user_privs/authorization/User_privilege.md
• data_source/catalog/hive_catalog.md
• administration/user_privs/authorization/ranger_plugin.mdKenneth Oh
09/25/2026, 5:31 PMRocky
09/25/2026, 5:32 PMSELECT _, the _ is expanded at the time of creation into a fixed list of columns. If the underlying Iceberg table schema changes, the MV will not automatically pick up those changes.
Here is how you handle different scenarios:
1. Will it pick up changes automatically?
No. Because the schema of an asynchronous MV is physically stored in StarRocks, it is static.
• If you add a column to the Iceberg table, the MV will continue to function but will not include the new column.
• If you drop or rename a column that the MV specifically references, the MV will be set to an INACTIVE state.
2. Can you alter it?
You cannot use ALTER MATERIALIZED VIEW to modify the query logic (like adding a new column to the selection). ALTER is limited to changing the refresh interval, name, or status.
3. How to "Sync" the schema
If the Iceberg schema has changed and you want the MV to reflect those changes (especially to pick up new columns from a SELECT *), you have two main options:
A. The "Active" Shortcut (v3.1.4+)
Instead of dropping the MV, you can try to re-activate it. This forces StarRocks to re-evaluate the original SQL definition.
sql
ALTER MATERIALIZED VIEW your_mv_name ACTIVE;
* Behavior: StarRocks will attempt to "recreate" the MV based on its original SQL. If the original SQL was SELECT _, this command will re-expand the _ to include the current columns of the Iceberg table.
* Requirement: The original SQL must still be valid. If you dropped a column that was explicitly named in the MV (not part of a *), this might fail, and you'll need to recreate it.
B. Drop and Recreate
If the schema change is significant or if ALTER ... ACTIVE does not achieve the desired result, you must manually drop and recreate the MV:
sql
DROP MATERIALIZED VIEW your_mv_name;
CREATE MATERIALIZED VIEW your_mv_name ... AS SELECT * FROM iceberg_table;
Summary of Principles
* MV Status: Schema changes on base table columns referenced by the MV will set the MV to INACTIVE.
* Automatic Activation: From v3.1.4/v3.2.0, StarRocks attempts to automatically re-activate MVs, but for a SELECT _ to pick up _new* columns, a manual ACTIVE command or a refresh that triggers re-activation is typically required to update the physical schema.
* Query Rewrite: If an MV is INACTIVE, it cannot be used for automatic query rewrite until it is made ACTIVE again.
References
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
09/30/2026, 5:09 PMRocky
09/30/2026, 5:10 PMsql
CREATE RESOURCE GROUP large_queries
TO (user='heavy_user')
WITH (
'big_query_scan_rows_limit' = '100000000' -- Reject if plan scans > 100M rows
);
* How it works: If the optimizer calculates that a query will scan more than the limit, it returns an error immediately: exceed big query scan_rows limit: current is ... but limit is ....
2. Terminating Queries During Execution (Circuit Breaker)
Since peak memory is difficult to predict perfectly before a query runs, StarRocks uses big_query_mem_limit as a runtime circuit breaker. The query starts, but if its memory footprint reaches the threshold, it is killed.
sql
CREATE RESOURCE GROUP heavy_limit
TO (db='analytics')
WITH (
'big_query_mem_limit' = '2147483648' -- Limit to 2GB per query
);
* Behavior: If a query hits this limit on any BE, it fails with a Memory Exceed Limit error.
* Global Variable: You can also set a per-session limit using SET query_mem_limit = <bytes>;.
3. Preventing System Overload (Query Queues)
If you want to reject or delay queries because the cluster's total memory is currently too high (rather than a specific query's plan), you use Query Queues.
sql
-- Enable the queue
SET GLOBAL enable_query_queue_select = true;
-- Reject new queries if system memory usage > 90%
SET GLOBAL query_queue_mem_used_pct_limit = 0.9;
-- Set max queue length; if more queries come in while the system is full, they are rejected
SET GLOBAL query_queue_max_queued_queries = 50;
4. Summary Table: How to Block "Big" Queries
Method | Target Metric | Rejection Timing | Best For
big_query_scan_rows_limit | Estimated Scan Rows | Pre-execution | Preventing massive full-table scans.
big_query_mem_limit | Real-time Memory | During execution | Killing queries that explode in memory (e.g., big joins).
query_queue_mem_used_pct_limit | Cluster Memory | Pre-execution | Protecting the cluster when it's already under pressure.
big_query_cpu_second_limit | Accumulated CPU | During execution | Stopping long-running complex calculations.
Important: To use Resource Groups, you must ensure the Pipeline Engine is enabled (it is by default in recent versions): SET GLOBAL enable_pipeline_engine = true;.
References
• administration/management/monitor_manage_big_queries.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• administration/management/resource_management/resource_group.md
• administration/management/resource_management/query_queues.md
References
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• administration/management/monitor_manage_big_queries.mdRocky
09/30/2026, 5:10 PMKenneth Oh
09/30/2026, 6:59 PMRocky
09/30/2026, 7:00 PMid, filtering on any other column will currently result in a full table scan.
Here are the best ways to accelerate filtering on a non-sort column, ordered by performance impact:
1. Materialized View (The "Secondary Index" Pattern)
This is the most effective way to speed up filtering on a non-sort column in StarRocks. You can create a synchronous materialized view (MV) that acts as a secondary index by using a different sort order.
* How it works: When you create an MV, StarRocks automatically selects the first few columns in the SELECT list as the Sort Key (Prefix Index).
* Implementation:
sql
CREATE MATERIALIZED VIEW mv_filter_column AS
SELECT target_column, id, col2, col3 -- Put your filter column first
FROM your_table;
* Result: When you query the base table with WHERE target_column = 'value', the StarRocks optimizer will transparently rewrite the query to use the MV. Because the MV is physically sorted by target_column, it will use the Prefix Index and Zone Maps to skip 99% of the data.
* Trade-off: This doubles the storage for that table, but it provides sub-second performance on 5 billion rows.
2. Bloom Filter Index
If your filter column has high cardinality (many unique values) and you primarily perform equality queries (e.g., WHERE col = 'xyz' or IN), a Bloom Filter is very effective.
* Implementation:
sql
ALTER TABLE your_table SET ("bloom_filter_columns" = "target_column");
* How it works: StarRocks uses the Bloom Filter to quickly determine if a data page could contain the value. If not, it skips the page entirely without reading it from disk. It is low-overhead compared to a Materialized View.
3. Inverted Index (For Strings/Text)
If your version is v3.3 or higher, you can use an Inverted Index. This is particularly powerful for string filtering or when you need to match keywords within a column.
* Implementation:
sql
ALTER TABLE your_table ADD INDEX index_name (target_column) USING GIN;
* Result: It builds a mapping of values to row IDs. It is much faster than a standard scan for high-cardinality string columns and supports MATCH or equality predicates.
4. Bitmap Index
If the column has moderate to high cardinality and you often combine it with other filters, a Bitmap Index can help.
* Implementation:
sql
CREATE INDEX index_name ON your_table (target_column) USING BITMAP;
* Note: In StarRocks, Bitmap indexes are optimized for columns where a specific value filters out at least 99.9% of the data.
5. Modify the Table Sort Key (v3.3+)
If you find that you almost always filter by this new column instead of id, you can actually change the physical sort order of the table without recreating it (supported for Duplicate Key tables in v3.3+ and Primary Key tables in v3.0+).
* Implementation:
sql
ALTER TABLE your_table ORDER BY (target_column, id);
* Warning: This is a heavy background operation that rewrites the data files.
Summary Recommendation
* For maximum speed (range & equality): Use the Materialized View (Option 1). It provides a new Prefix Index.
* For point lookups (equality only): Use a Bloom Filter (Option 2).
* For string/keyword searches: Use an Inverted Index (Option 3).
References
• best_practices/table_clustering.md
• sql-reference/sql-functions/utility-functions/encode_sort_key.mdKenneth Oh
10/02/2026, 10:20 PMRocky
10/02/2026, 10:20 PMSELECT *, your approach depends on whether you just want to pick up new columns or if you want to change the query logic entirely.
1. To pick up new columns (same query)
If you simply added columns to the base table and want the SELECT _ in your MV to include them, you do not need to drop it. You can force StarRocks to re-expand the _ and update the MV's schema using:
sql
ALTER MATERIALIZED VIEW your_mv_name ACTIVE;
* How it works: When an MV is set to ACTIVE, StarRocks re-evaluates the original SQL definition. If the original SQL was SELECT _, it will re-expand that _ against the current schema of the base table.
* When to use: Use this if the MV has become INACTIVE due to a schema change or if you just want to "sync" the column list.
* Note: In versions 3.1.4+ and 3.2+, StarRocks often attempts to do this automatically in the background, but the manual command guarantees an immediate update.
2. To change the query logic (different query)
If you want to change the query itself (e.g., changing SELECT * to SELECT col1, col2), StarRocks does not support ALTER MATERIALIZED VIEW ... AS SELECT ....
To achieve this without downtime or a "gap" where the MV doesn't exist, use the SWAP method:
1. Create a new temporary MV with your new desired query:
sql
CREATE MATERIALIZED VIEW your_mv_name_new
REFRESH ASYNC ...
AS SELECT col1, col2 FROM base_table;
2. Swap the new MV with the old one:
sql
ALTER MATERIALIZED VIEW your_mv_name SWAP WITH your_mv_name_new;
3. Drop the old MV (which is now named your_mv_name_new):
sql
DROP MATERIALIZED VIEW your_mv_name_new;
Why use SWAP?
* No Downtime: The swap is an atomic metadata operation.
* Query Rewrite Preservation: Any queries relying on the MV for automatic rewrite will continue to work without interruption.
Summary
* *Just refreshing SELECT *?* Use ALTER ... ACTIVE.
* Changing the SQL code? Use CREATE + SWAP WITH + DROP.
References
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
References
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
10/02/2026, 10:33 PMRocky
10/02/2026, 10:33 PMALTER MATERIALIZED VIEW ... ACTIVE is not picking up your new columns is that for Asynchronous Materialized Views, the SELECT * is expanded and fixed at the moment the MV is created.
Unlike a logical view (which re-evaluates the * every time it is queried), an Asynchronous MV is a physical table with its own defined schema. When you create it, StarRocks looks at the base table, sees the current columns, and saves that specific list of columns into the MV's metadata and physical storage.
To include new columns from the base table, you have to redefine the MV. Since StarRocks does not currently support ALTER MATERIALIZED VIEW ... AS SELECT ..., you should use the Atomic Swap method to avoid downtime:
The Correct Procedure (Atomic Swap)
This method allows you to "redefine" the MV without dropping it first, ensuring that any queries using it for automatic rewrites don't fail.
1. Create a new temporary MV (this will pick up the new columns via its own SELECT * expansion):
sql
CREATE MATERIALIZED VIEW my_mv_new
REFRESH ASYNC -- matching your original refresh settings
AS SELECT * FROM base_table;
2. Swap the metadata: This makes the new physical table (with the new columns) the "official" MV and moves the old one to the temporary name.
sql
ALTER MATERIALIZED VIEW my_mv SWAP WITH my_mv_new;
3. Drop the old version:
sql
DROP MATERIALIZED VIEW my_mv_new;
Why ALTER ... ACTIVE exists
The ACTIVE command is not for changing the schema; it is for restoring functionality.
If you perform a schema change on a base table (like dropping it or changing a column type), StarRocks sets dependent MVs to INACTIVE to prevent data corruption or query errors. ALTER ... ACTIVE tells StarRocks: "I have fixed the base table, please try to use this MV again with its existing column definitions."
Summary
* Logical Views: Use ALTER VIEW ... AS SELECT * (it re-expands).
* Materialized Views: Must use the SWAP pattern because they are physical tables with a schema that was locked at the time of CREATE.
References
• sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.md
• using_starrocks/async_mv/Materialized_view.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
References
• using_starrocks/async_mv/Materialized_view.md
• using_starrocks/async_mv/use_cases/data_modeling_with_materialized_views.mdKenneth Oh
10/06/2026, 5:10 AMRocky
10/06/2026, 5:10 AMdefault_wg: Used for regular queries that don't match any specific classifier. Its default concurrency_limit is 0 (unlimited).
* default_mv_wg: Used for asynchronous materialized view refresh tasks. Its default concurrency_limit is also 0 (unlimited).
Key Behaviors:
* Effectiveness: The concurrency_limit parameter only takes effect when it is set to a value greater than 0.
* System Impact: If the limit is 0 (unlimited), queries will continue to be accepted until the system runs out of physical resources (Memory/CPU), at which point they may fail with errors like Memory exceed limit or experience extreme latency.
* Queuing: If you set a concurrency_limit greater than 0, you should also consider configuring the Query Queue (enable_query_queue = true). Without a queue, any query that exceeds the concurrency limit will fail immediately with an error rather than waiting for a slot.
How to check or change it:
You can check the current limits of your groups using:
sql
SELECT * FROM information_schema.resource_groups;
To set a limit (e.g., to 100) for the default group:
sql
ALTER RESOURCE GROUP default_wg WITH ('concurrency_limit' = '100');
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• administration/management/BE_configuration.md
References
• faq/resource_isolation_faq.md
• administration/management/resource_management/resource_group.md
• administration/management/BE_configuration.mdKenneth Oh
10/06/2026, 5:49 AMRocky
10/06/2026, 5:49 AMexternal group to queue queries while leaving the default group unlimited. When the group-level queue is enabled, queries that exceed a group's concurrency_limit will wait in a "Pending" state instead of failing immediately.
Configuration Steps
1. Enable the Queue Feature (Global)
You must enable the query queue globally and specifically for workload groups:
sql
-- Enable the general queue for SELECT queries
SET GLOBAL enable_query_queue_select = true;
-- Enable the group-level logic (v3.1.4+)
SET GLOBAL enable_group_level_query_queue = true;
2. Configure the Workload Groups
Set the concurrency_limit for your external group. For the default group, a value of 0 means unlimited concurrency (no queueing will ever trigger for it).
sql
-- Set external group to 10 concurrent queries; the 11th will wait
ALTER RESOURCE GROUP external WITH ('concurrency_limit' = '10');
-- Ensure default group remains unlimited (0 is the default)
ALTER RESOURCE GROUP default_wg WITH ('concurrency_limit' = '0');
Key Behaviors to Note
* Queuing Logic: If a query enters the external group and 10 queries are already running in that group, it will wait. Queries in the default_wg will continue to run without restriction because their limit is 0.
* Global Queue Limits: While the concurrency trigger is per-group, the queue capacity and timeout are currently global session variables. These determine how many queries total can wait across the whole cluster and for how long:
* query_queue_max_queued_queries (Default: 1024): Total queries allowed to wait before new ones are rejected.
* query_queue_pending_timeout_second (Default: 300): How long a query can stay in the queue before timing out.
* Monitoring: You can see if queries are waiting by checking the IsPending column in SHOW PROCESSLIST or by monitoring the starrocks_fe_resource_group_query_queue_pending metric.
References
• administration/management/resource_management/query_queues.md
• sql-reference/System_variable.md
• faq/resource_isolation_faq.md
References
• administration/management/resource_management/query_queues.md
• faq/resource_isolation_faq.md
• sql-reference/System_variable.md
References
• administration/management/resource_management/query_queues.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md
• sql-reference/System_variable.mdKenneth Oh
10/06/2026, 8:12 AMRocky
10/06/2026, 8:12 AMiceberg.catalog.warehouse property:
sql
CREATE EXTERNAL CATALOG s3_table_catalog
PROPERTIES(
"type"="iceberg",
"iceberg.catalog.type" = "rest",
"iceberg.catalog.uri" = "<https://glue>.<region>.<http://amazonaws.com/iceberg|amazonaws.com/iceberg>",
"iceberg.catalog.rest.sigv4-enabled" = "true",
"iceberg.catalog.rest.signing-name" = "glue",
"iceberg.catalog.warehouse" = "<aws_account_id>:s3tablescatalog/<table_bucket_name>",
"aws.s3.region" = "<region>",
"aws.s3.access_key" = "<your_access_key>",
"aws.s3.secret_key" = "<your_secret_key>"
);
2. Sinking Data
Once the catalog is created, you can sink data directly using INSERT INTO or CREATE TABLE AS SELECT (CTAS).
Create a table in the bucket:
sql
USE s3_table_catalog.default;
CREATE TABLE target_iceberg_table (
id BIGINT,
data STRING,
event_time DATETIME
) PARTITION BY (data);
Sink data from a StarRocks table:
sql
INSERT INTO target_iceberg_table
SELECT id, data, event_time
FROM internal_db.source_table;
Key Requirements
* Permissions: You must configure AWS Lake Formation to allow external engines (like StarRocks) to access the S3 table bucket.
* Region: The Glue REST endpoint is region-specific (e.g., `https://glue.us-east-1.amazonaws.com/iceberg`).
* Format: Currently, StarRocks supports sinking data into Parquet-formatted Iceberg tables.
References
• data_source/catalog/iceberg/iceberg_rest_s3.md
• data_source/catalog/iceberg/DML.md
• data_source/feature-support-data-lake-analytics.md
References
• data_source/catalog/iceberg/iceberg_rest_s3.md
• data_source/catalog/iceberg/DML.md
• data_source/feature-support-data-lake-analytics.md
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/catalog/iceberg/iceberg_rest_s3.md
• quick_start/iceberg.md
• data_source/catalog/iceberg/DML.mdKenneth Oh
10/06/2026, 8:12 AMRocky
10/06/2026, 8:13 AMINSERT INTO (appends) and INSERT OVERWRITE (replaces whole partitions, a form of Copy-on-Write). Support for row-level DML (which generates MoR delete files) is a newer capability:
* INSERT INTO: Works with S3 table buckets as of v3.4.0. It simply appends new data files to the table.
* DELETE (Merge-on-Read): Starting with v3.4 and further optimized in v4.0/v4.1, StarRocks supports native SQL DELETE for Iceberg. When you perform a DELETE, StarRocks writes position delete files (MoR) rather than rewriting the entire data file. This ensures high-performance deletions that are interoperable with other engines like Spark and Trino.
* UPDATE / MERGE INTO: Support for UPDATE and MERGE INTO on Iceberg tables is part of the v4.x roadmap. For most v3.x versions, you cannot perform row-level UPDATE or MERGE directly on an Iceberg catalog; these workloads are typically handled by sinking data from StarRocks to an intermediate location and using an engine like Spark, or by using StarRocks' Asynchronous Materialized Views to bridge the gap.
Summary for S3 Table Buckets
If you are using v3.4.0 or later:
* You can sink (INSERT) data into Iceberg tables in S3 table buckets.
* You can DELETE rows using a WHERE clause, which will generate MoR position delete files.
* You can read any MoR data (even if written by other engines) using the Glue/REST catalog integration.
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/catalog/iceberg/DML.md
• data_source/feature-support-data-lake-analytics.md
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/catalog/iceberg/DML.md
• data_source/feature-support-data-lake-analytics.md
References
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/catalog/iceberg/DML.md
• data_source/feature-support-data-lake-analytics.md
• table_design/table_types/primary_key_table.md
References
• table_design/table_types/primary_key_table.md
• data_source/catalog/iceberg/iceberg_catalog.md
• data_source/feature-support-data-lake-analytics.mdKenneth Oh
10/11/2026, 10:41 PMRocky
10/11/2026, 10:41 PMidentity, year, month, day, and hour.
* Unsupported: The bucket and truncate transforms are currently not supported for partition-aligned refresh.
* Workaround: If your dimensional table is partitioned by a STRING or INT column (identity), you can align the MV to that column (supported since v3.1.4/v3.2). If you specifically need the Iceberg bucket transform, the MV currently treats it as an unpartitioned table, requiring a full refresh.
2. The Solution: Incremental MVs (StarRocks 4.0/4.1)
To address the exact problem you described—large tables where query patterns don't follow date fields—StarRocks is moving away from "Partition-aligned refresh" toward Incremental View Maintenance (IVM).
* StarRocks 4.1 introduces Incremental Materialized Views for Iceberg.
* Why this matters for you: Unlike partition-based refresh (which would require a full refresh of a bucket if any row changed), IVM tracks changes at the Iceberg snapshot/row level.
* Capability: It allows the MV to refresh only the delta (new data) regardless of whether the table is partitioned by date, bucketed by ID, or even unpartitioned. This makes MVs on large dimensional tables much more efficient.
Summary Table for Iceberg MVs
Feature | v3.2 - v3.4 | v3.5 | v4.1+
Identity/Date Partitioning | Supported | Supported | Supported
Multi-Column Partitioning | No | Supported | Supported
Iceberg Bucket Transform | No | No | Supported via IVM
Incremental Refresh | Partition-based | Partition-based | Row/Delta-based (IVM)
Recommendation
* If you are on v3.x: If possible, change the Iceberg base table to use identity partitioning on your dimensional key (e.g., user_id as a STRING or INT) instead of the bucket transform. This will allow StarRocks to use partition-level incremental refresh.
* If you need Hash Buckets: Plan an upgrade to v4.1 to leverage the new Incremental MV engine, which is designed specifically for these "non-date" data lakehouse patterns.
References
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
References
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.mdKenneth Oh
10/11/2026, 10:44 PMRocky
10/11/2026, 10:44 PMbucket() or truncate() transforms for partition-level refresh.
* Result: If your base table uses these transforms, the MV is forced to perform a full refresh every hour, which is inefficient for large gold-level tables.
The Solution in StarRocks 4.1: Incremental View Maintenance (IVM)
Starting with version 4.1, StarRocks introduces a new refresh_mode called INCREMENTAL. This is a major architectural shift that enables row-level/snapshot-level incremental updates independent of partitioning.
1. How to Define the MV
When creating your gold-level MV, you specify the refresh_mode in the properties. This allows the system to track only the changed snapshots in Iceberg and apply the delta to your MV.
sql
CREATE MATERIALIZED VIEW gold_incremental_mv
PARTITION BY (dt) -- You can still partition the MV for query performance
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
PROPERTIES (
"refresh_mode" = "INCREMENTAL"
)
AS
SELECT
id,
COUNT(orders) as total_orders,
dt
FROM iceberg_catalog.silver_db.bucketed_table
GROUP BY id, dt;
2. Key Benefits for Your Workflow
* Supports Bucket Transforms: Since the refresh logic tracks snapshot deltas rather than partition state, it bypasses the "bucket transform" limitation.
* Reduced Resource Consumption: Instead of scanning the entire silver table every hour, StarRocks only processes the data that landed in the last hour.
* Gold Table Freshness: You can maintain a "gold" table with low latency (hourly or even faster) without the cost of a full re-computation.
Summary of Requirements
* Version: StarRocks v4.1+.
* Catalog: Works with Iceberg (REST, Glue, Hive catalogs).
* Configuration: Ensure the base Iceberg table is v2 if you are performing row-level deletes/updates, though standard appends work for v1.
Recommendation: If you are currently on v3.x and cannot upgrade immediately, the only workaround is to change your silver table's partitioning to use identity (e.g., a raw string or date column) instead of bucket(), which would allow version 3.5 to perform partition-level incremental refreshes.
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md
• using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
References
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• using_starrocks/async_mv/Materialized_view.mdKenneth Oh
10/11/2026, 10:45 PMRocky
10/11/2026, 10:45 PMINCREMENTAL refresh mode (IVM) in StarRocks 4.1 currently has a critical limitation for Iceberg: it only supports append-only base tables.
* How it works: In v4.1, the incremental engine tracks new Iceberg snapshots. If it detects a snapshot that contains an APPEND operation, it can incrementally update the Materialized View (MV).
* The Problem: If you perform a DELETE, UPDATE, or MERGE on any of your dimensional or fact tables, the INCREMENTAL refresh will fail. The current engine does not yet support "retractable" changes (deleting or updating existing rows in the MV based on base table changes).
2. Joins and Star Schemas
The good news is that the IVM engine does support Inner Joins. You can join a large fact table with multiple dimensional tables in an incremental MV. However, the "append-only" rule applies to all tables in that join. If even one of your small dimensional tables is updated (e.g., a SCD Type 1 update), the incremental refresh of the Gold MV will be blocked.
3. The "Hash Bucket" Conflict
As we discussed previously, your use of Iceberg hash buckets creates a secondary issue:
* INCREMENTAL Mode: Blocked because your tables are not append-only.
* Partition-Level Refresh (PCT): This is the fallback for non-append-only data, but it is blocked because StarRocks does not yet support aligning partitions based on the Iceberg bucket() transform. It only supports identity, year, month, day, and hour.
Summary for Your Gold Table
For a gold-level table that is (1) a Join, (2) Non-append-only, and (3) Hash-bucketed, here is the status:
Feature | Support in v4.1
Incremental Join (IVM) | ✅ Supported (Inner Join)
Updates/Deletes (IVM) | ❌ Not Supported (Requires Append-Only)
Hash Bucket Alignment | ❌ Not Supported
Refresh Strategy | Full Refresh Required
Recommended Path Forward
Since a full refresh of a large gold table every hour is likely too expensive, consider these alternatives:
1. Change Partitioning (If Possible): If you can change your base Iceberg tables from bucket(id) to identity(id) (if the ID is a string or int and not too high cardinality), you can use Partition-level refresh (PCT) in v3.5/v4.1. This would allow you to refresh only the "partitions" (IDs) that changed, even with updates.
2. View-based Gold Layer: Instead of a Materialized View, use a standard VIEW for the Join. StarRocks' query engine is extremely fast at Iceberg Joins. With features like Data Cache and Global Dictionary, you might find that the raw query performance on the "Silver" tables is sufficient without materializing the "Gold" layer.
3. Wait for v4.2/v4.x: Support for non-append-only IVM (handling deletes and updates) is a high-priority item on the roadmap following the initial v4.1 release.
References
• using_starrocks/async_mv/feature-support-asynchronous-materialized-views.md
• sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• data_source/catalog/iceberg/iceberg_catalog.md
References
• release_notes/release-4.0.md
• release_notes/release-3.2.mdRocky
10/11/2026, 10:45 PMKenneth Oh
10/11/2026, 11:19 PMRocky
10/11/2026, 11:19 PMSET_VAR query hint to temporarily disable the enable_materialized_view_rewrite session variable for that specific statement.
Syntax
Add the following hint immediately after the SELECT keyword:
sql
SELECT /*+ SET_VAR(enable_materialized_view_rewrite = false) */
column1,
column2
FROM your_base_table
WHERE ...;
Key Details
* Scope: This hint only affects the query it is attached to. It does not change your global or session-level settings.
* Function: It forces the StarRocks optimizer to ignore any available asynchronous materialized views and query the base tables directly.
* Use Case: This is useful for troubleshooting performance issues, verifying the latest data in base tables (if the MV is stale), or comparing execution plans between the MV and base tables.
Alternative (Session Level)
If you want to disable it for several queries in a row without adding hints to each, you can change the variable at the session level:
sql
SET enable_materialized_view_rewrite = false;
-- Your queries here
SET enable_materialized_view_rewrite = true;
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• best_practices/query_tuning/query_hint.md
• using_starrocks/async_mv/Materialized_view.md
References
• using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
• using_starrocks/async_mv/Materialized_view.md
• sql-reference/System_variable.md
• best_practices/query_tuning/query_hint.md