Francisco Silva
06/24/2026, 12:43 PMRocky
06/24/2026, 12:44 PMtablet_sched_max_scheduling_tablets = 0, etc.) prevents the Frontend (FE) from triggering automatic replica repair or rebalancing while a Backend (BE) is offline.
* Availability: Writes (including CDC from Flink, Routine Load, or Stream Load) will continue to succeed as long as a quorum of replicas is maintained. For a default 3-replica table, a write is successful if at least 2 replicas acknowledge it.
* Behavior: When you stop a BE for an upgrade, that replica is temporarily "down." Because you disabled tablet clone, the FE will not try to create a new 3rd replica on a different node. It simply waits.
* Catch-up: Once you bring the BE back up with the new version and re-enable tablet clone, the FE will detect that the replica is lagging and trigger an "incremental clone" to sync the data missed during the downtime.
2. Can you just stop and boot with new Docker images?
Yes, provided you have configured persistent volumes for your data and metadata.
For a Docker-based upgrade (Compose or standalone):
1. Preparation: Run the SQL commands to disable tablet clone and balancing.
2. Rolling Upgrade (BEs first):
* Stop the BE container.
* Update the image tag (e.g., from 3.2.x to 3.3.x) in your docker-compose.yml or docker run command.
* Start the container. The BE will use the same data on the host volume but run the new binary.
* Verify the BE is Alive in SHOW BACKENDS before moving to the next one.
3. Upgrade FEs: Follow the same process (Followers first, Leader last).
4. Post-Upgrade: Re-enable tablet clone and balancing.
Warning: Never "stop and boot" if your StarRocks data is stored inside the container's ephemeral layer. Always ensure /opt/starrocks/fe/meta and /opt/starrocks/be/storage (or your custom paths) are mapped to host directories.
Summary of SQL commands to disable/enable:
To Disable:
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_scheduling_tablets" = "0");
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_balancing_tablets" = "0");
ADMIN SET FRONTEND CONFIG ("disable_balance" = "true");
ADMIN SET FRONTEND CONFIG ("disable_colocate_balance" = "true");
To Enable (after upgrade):
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_scheduling_tablets" = "10000");
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_balancing_tablets" = "500");
ADMIN SET FRONTEND CONFIG ("disable_balance" = "false");
ADMIN SET FRONTEND CONFIG ("disable_colocate_balance" = "false");
References
• deployment/upgrade.md
• deployment/sr_operator.md
• developers/build-starrocks/Build_in_docker.md
• release_notes/release-3.4.mdFrancisco Silva
06/24/2026, 2:35 PMRocky
06/24/2026, 2:36 PM/metrics endpoints.
1. Read QPS (Query Performance)
The most common metric for read QPS is based on the total number of queries executed by the Frontend (FE).
* Metric Name: starrocks_fe_query_total
* How to use (PromQL):
promql
irate(starrocks_fe_query_total[1m])
This calculates the per-second rate of queries over the last minute.
* Alternative (per Resource Group): starrocks_fe_query_resource_group allows you to track read QPS broken down by specific resource groups.
2. Write QPS (Ingestion Performance)
Since StarRocks handles writes as transactions (loads), "Write QPS" is usually measured by the number of successful transactions or load requests.
* Metric Name: starrocks_fe_txn_total_latency_ms_count
* How to use (PromQL):
promql
sum(irate(starrocks_fe_txn_total_latency_ms_count[1m])) by (type)
_The type label allows you to see the "QPS" for different load methods like stream_load, routine_load, or insert_into._
* Row-level throughput: If you want to track the number of rows written per second (rather than the number of requests):
* `starrocks_fe_routine_load_rows`: Total rows loaded via Routine Load.
* `starrocks_be_load_rows`: Total rows loaded at the BE level.
Summary Table
Goal | Metric Name | Source
Read QPS | starrocks_fe_query_total | FE
Write QPS | starrocks_fe_txn_total_latency_ms_count | FE (Leader)
Error Rate | starrocks_fe_query_err | FE
Write Throughput | starrocks_be_load_bytes_total | BE
Note: For the transaction metrics (starrocks_fe_txn_*), they are only reported by the Leader FE. If you are querying a Follower FE's metrics endpoint, these specific write counts may show as zero.
References
• administration/management/monitoring/metrics.md
• administration/management/monitoring/Monitor_and_Alert.md
References
• administration/management/monitoring/metrics.mdFrancisco Silva
06/24/2026, 4:39 PMRocky
06/24/2026, 4:40 PM0 or true.
1. Check your current configuration
Run these commands to see if balancing is disabled:
sql
ADMIN SHOW FRONTEND CONFIG LIKE "%balance%";
ADMIN SHOW FRONTEND CONFIG LIKE "%tablet_sched%";
2. How to fix it (Re-enable Balancing)
To allow StarRocks to start migrating tablets to the new nodes, run the following commands:
sql
-- Allow the scheduler to schedule tablets
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_scheduling_tablets" = "10000");
-- Allow the balancer to move tablets between nodes
ADMIN SET FRONTEND CONFIG ("tablet_sched_max_balancing_tablets" = "500");
-- Enable general and colocate balancing
ADMIN SET FRONTEND CONFIG ("disable_balance" = "false");
ADMIN SET FRONTEND CONFIG ("disable_colocate_balance" = "false");
3. What to expect after enabling
* Gradual Migration: StarRocks will not move everything at once to avoid impacting query performance. You will see TabletNum on the new nodes start to increase slowly.
* Monitor Progress: You can monitor the migration by running:
sql
SHOW PROC '/tablet_scheduler';
Look at the Pending, Running, and Finished counts to see the active balancing tasks.
* Disk Usage: As TabletNum increases on the new nodes, you will also see DataUsedCapacity start to rise and UsedPct on the older, crowded nodes start to decrease.
Note: If you haven't manually disabled these settings, ensure that your High Warehouse or Storage settings aren't preventing movement (though with 0 tablets on new nodes, it is almost certainly the configuration mentioned above).
References
• administration/management/Control_balance.md
• administration/management/monitoring/metrics.md
• deployment/upgrade.md
• administration/management/Resource_management.md
• sql-reference/sql-statements/cluster-management/nodes-processes/SHOW_BACKENDS.md
• administration/Maintenance.md
• administration/Configuration.md
• sql-reference/sql-statements/cluster-management/nodes-processes/SHOW_PROC.mdFrancisco Silva
06/24/2026, 4:41 PMRocky
06/24/2026, 4:41 PMSHOW BACKENDS output, your scheduling and balancing are technically "enabled" (disable_balance = false), but there is one specific configuration likely blocking the movement:
1. The Colocate Balance Setting
In your configuration output, I see:
tablet_sched_disable_colocate_overall_balance | true
If your tables use the Colocate Join feature (defined with a COLOCATE_WITH property), they will not balance across the new nodes while this is true. Even if disable_colocate_balance is false, this "overall" setting often prevents the cluster from redistributing colocate groups to newly added backends.
Try setting this to false:
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_disable_colocate_overall_balance" = "false");
2. Check the Scheduler Status
To see exactly what the StarRocks balancer is thinking, run this command:
sql
SHOW PROC '/tablet_scheduler';
* Look at `PendingTaskNum`: If this is 0, the FE isn't even trying to move tablets.
* Look at HistoryFinishedNum vs `HistoryFailedNum`: If failures are high, there might be a network or disk issue on the new nodes.
3. Check for Colocation Group instability
If your tables are colocated, check if the groups are "unstable":
sql
SHOW PROC '/colocation_group';
If the IsStable column is false, the balancer might be stuck trying to fix a replica issue before it moves data to new nodes.
4. Disk Safe Threshold
Your tablet_sched_balance_load_disk_safe_threshold is 0.5.
* Logic: StarRocks considers a cluster balanced if all nodes are below this threshold.
* Your Case: Your old nodes are at 63% - 76% (above 0.5), and your new nodes are at 5%. This should trigger a balance because the difference is > 10% and nodes are above the safe threshold. However, if you recently changed other settings, it may take a few minutes for the "Checker" (tablet_sched_checker_interval_seconds = 20) to initiate the next round.
Summary Checklist:
1. Set tablet_sched_disable_colocate_overall_balance to false.
2. Run SHOW PROC '/tablet_scheduler'; to see if tasks start appearing in the Running or Pending columns.
3. Ensure the new BEs can actually communicate with the Leader FE (check BE logs for report_tablet errors). Since Alive is true, heartbeat is fine, but the BE must also be able to successfully finish a report_tablet task.
References
• administration/management/FE_configuration.md
• sql-reference/sql-statements/cluster-management/nodes-processes/SHOW_PROC.md
• administration/management/Control_balance.md
• administration/Maintenance.md
• faq/operation_maintenance_faq.md
• administration/Configuration.md
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.mdFrancisco Silva
06/24/2026, 4:43 PMRocky
06/24/2026, 4:44 PMSHOW BACKENDS output, here are the most likely reasons the two new nodes are sitting at 0 tablets:
1. tablet_sched_disable_colocate_overall_balance is true
In your ADMIN SHOW FRONTEND CONFIG output, this setting is explicitly true.
* Why it matters: In StarRocks 4.x, if your tables are part of Colocation Groups (used for Colocate Joins), this parameter prevents the cluster from redistributing those groups to newly added nodes. Even if disable_balance is false, the colocate groups will stay on the original nodes.
* Fix:
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_disable_colocate_overall_balance" = "false");
2. Multi-Warehouse Assignment (New in 4.1)
StarRocks 4.1 introduces the Warehouse architecture. Tablets are often associated with a specific Warehouse. If your new nodes haven't been assigned to the default warehouse (or whichever warehouse your data belongs to), the FE will not schedule tablets on them.
* Check existing warehouses:
sql
SHOW WAREHOUSES;
* Check backend-to-warehouse mapping:
Check if the new Backend IDs (12713022, 12712988) are listed as part of the warehouse.
* Fix (if missing): You may need to add them to the warehouse:
sql
ALTER WAREHOUSE <warehouse_name> ADD BACKENDS ("10.74.0.108:9050", "10.74.0.94:9050");
3. Metadata and Data Layout Changes
The release notes for 4.1 explicitly mention that the upgrade involves internal changes to data layout and tablet splitting mechanisms.
* Metadata Synchronization: After an upgrade, the FE may be busy performing a background metadata migration or consistency check before it resumes balancing.
* Tablet Size: 4.1 introduced "Large-Capacity Tablet Support." The balancer's logic might now prioritize splitting large tablets before moving them to new nodes.
4. How to Verify the Blocker
To find out exactly why the FE is skipping these nodes, use these diagnostic commands:
1. Check the Scheduler's current plan:
sql
SHOW PROC '/tablet_scheduler';
Look for PendingTaskNum. If it’s 0, the FE isn't trying to move anything. If it's high but RunningTaskNum is low, check the ErrMsg in the history:
sql
SHOW PROC '/cluster_balance/history_tablets';
2. Check Colocation Group stability:
If your tables are colocated, balancing won't happen if the group is "Unstable":
sql
SHOW PROC '/colocation_group';
Recommended Next Steps:
1. Set tablet_sched_disable_colocate_overall_balance to false.
2. Run SHOW WAREHOUSES to ensure the new nodes are part of the active compute/storage group.
3. Wait 10–15 minutes, as the tablet_sched_checker_interval_seconds is 20 seconds, but the initial load score calculation after an upgrade can take a few cycles.
References
• administration/management/FE_configuration.md
• administration/management/Control_balance.md
• sql-reference/sql-statements/cluster-management/nodes-processes/SHOW_PROC.md
• deployment/upgrade.md
• administration/management/resource_management/Replica.md
• using_starrocks/Colocate_join.md
References
• administration/management/Scale_up_down.mdFrancisco Silva
06/24/2026, 4:45 PMRocky
06/24/2026, 4:46 PMADMIN SHOW FRONTEND CONFIG output, there is a specific blocker:
tablet_sched_disable_colocate_overall_balance | true
If your tables use the COLOCATE_WITH property (very common for join performance), StarRocks will not move these tables to new nodes while this setting is true. Even though disable_balance is false, this "overall" flag prevents the migration of entire colocation groups to newly added backends.
Fix:
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_disable_colocate_overall_balance" = "false");
2. Multi-Warehouse Membership (StarRocks 4.x Change)
StarRocks 4.1 deepens the Warehouse architecture. In this version, backends must be part of a Warehouse to receive data/compute tasks. If you added the nodes to the cluster but did not add them to the default (or specific) warehouse, the scheduler will ignore them.
* Check existing warehouses:
sql
SHOW WAREHOUSES;
* Check if the new nodes are listed: Look for Backend IDs 12713022 and 12712988.
* Add them if missing:
sql
ALTER WAREHOUSE default_warehouse ADD BACKENDS ("10.74.0.108:9050", "10.74.0.94:9050");
3. Disk Safe Threshold Trigger
Your tablet_sched_balance_load_disk_safe_threshold is set to 0.5.
* Your existing nodes are between 63% and 76% usage.
* Because they are above 0.5, they are considered "High Load."
* However, after an upgrade to 4.1, the FE performs a metadata consistency check. If it finds any tablet versions that aren't fully synchronized (common after a version jump), it will pause balancing until the cluster is "Stable."
Diagnostic Commands to Verify:
Run these to see exactly why the scheduler is idle:
1. Check Scheduler Tasks:
sql
SHOW PROC '/tablet_scheduler';
If PendingTaskNum is 0, the FE isn't even trying.
2. Check Colocate Group Stability:
sql
SHOW PROC '/colocation_group';
If IsStable is false, balancing is likely paused while it repairs replicas elsewhere.
3. Check Balance History:
sql
SHOW PROC '/cluster_balance/history_tablets';
This will show you if the FE attempted a move and failed with a specific error (e.g., "no available backend").
Recommendation: Start by setting tablet_sched_disable_colocate_overall_balance to false and checking SHOW WAREHOUSES. If the nodes are in the warehouse and the colocate balance is on, you should see TabletNum start to rise within 5-10 minutes.
References
• administration/management/FE_configuration.md
• using_starrocks/Colocate_join.md
• sql-reference/System_variable.mdFrancisco Silva
06/24/2026, 4:46 PMRocky
06/24/2026, 4:47 PMtablet_sched_disable_colocate_overall_balance | true
If your tables were created with the COLOCATE_WITH property (to enable Colocate Joins), StarRocks will not move these tables to new nodes while this setting is true. Even if disable_balance is false, the "overall" colocate balancer is off.
Fix:
sql
ADMIN SET FRONTEND CONFIG ("tablet_sched_disable_colocate_overall_balance" = "false");
2. Disk Usage Threshold logic
Your tablet_sched_balance_load_disk_safe_threshold is set to 0.5 (50%).
* StarRocks Logic: If a BE's disk usage is below this threshold, the balancer considers it "safe" and may not aggressively move data away from it.
* However: Since your old nodes are at 63% - 76% (above 0.5) and the gap with new nodes (5%) is much larger than 10%, a balance should be triggered. If it isn't, the FE might be waiting for a "Stable" cluster state following the 4.1.1 upgrade.
3. Check for "In-Progress" Metadata Migration
StarRocks 4.1 introduced significant internal changes to data layout (including tablet splitting and distribution mechanisms). After an upgrade, the FE often performs a consistency check on all existing tablets before it begins scheduling new movements.
Run these diagnostic commands to find the blocker:
1. Check the Scheduler's current reason for idling:
sql
SHOW PROC '/tablet_scheduler';
* Look at PendingTaskNum. If it is 0, the FE isn't even trying to move tablets.
* Check HistoryFailedNum. If this is increasing, the new nodes might be rejecting the clone tasks.
2. Check Colocate Group status:
sql
SHOW PROC '/colocation_group';
* If any groups are listed as IsStable = false, the balancer will focus on fixing those replicas on the original nodes before it attempts to expand the group to new BEs.
3. Check for specific balance errors:
sql
SHOW PROC '/cluster_balance/history_tablets';
* This will list the last few attempted tablet moves. If they failed, the ErrMsg column will tell you why (e.g., "no available backend" or "timeout").
Summary
In StarRocks 4.1.1 Shared-Nothing, balancing is typically blocked by colocate settings or a cluster instability flag (often temporary after an upgrade). Start by enabling tablet_sched_disable_colocate_overall_balance and then check the /tablet_scheduler proc path to see what the FE is reporting.
References
• administration/management/FE_configuration.md
• using_starrocks/Colocate_join.md
• sql-reference/sql-statements/cluster-management/nodes-processes/SHOW_PROC.md
• administration/management/Control_balance.md
• administration/Maintenance.md
• administration/Configuration.md
References
• administration/management/FE_configuration.md
• administration/management/Control_balance.mdRocky
06/24/2026, 4:47 PMFrancisco Silva
06/24/2026, 4:58 PMRocky
06/24/2026, 4:58 PMstorage_medium property is managed at the table or partition level, not at the database level. To change an entire database from HDD to SSD, you must apply the change to each table within that database.
Prerequisites
Ensure your Backends (BEs) are actually configured with SSD storage. Check the storage_root_path in your be.conf. If your BE disks are SSD but tagged as HDD in the config, the migration will not provide performance benefits or may fail.
──────────
Step 1: Migrate Existing Data
For each table in your database, run the following command to move all existing data (tablets) from HDD to SSD:
sql
-- For a specific table, move all its partitions to SSD
ALTER TABLE <db_name>.<table_name> MODIFY PARTITION (*) SET ("storage_medium" = "SSD");
Step 2: Set the Default for Future Partitions
If you have dynamic partitioning or plan to add partitions later, you must also update the table's default property so new data is created on SSD by default:
sql
ALTER TABLE <db_name>.<table_name> SET ("default.storage_medium" = "SSD");
──────────
Monitoring the Progress
The migration happens in the background. You can monitor the status using these commands:
1. Check Tablet Distribution:
sql
SHOW TABLETS FROM <table_name>;
Look at the StorageMedium column to see if it has updated from HDD to SSD.
2. Check Migration Tasks:
You can check the BE logs or use the following to see if there are active balance/clone tasks:
sql
SHOW PROC '/tablet_scheduler';
Important Notes
* No Database-level Command: There is currently no ALTER DATABASE ... SET storage_medium command. You can script the table-level changes using a simple loop or by generating SQL from the information_schema.tables.
* Performance Impact: Migrating large amounts of data from HDD to SSD will consume disk I/O and network bandwidth. You can control the migration speed by adjusting the BE configuration storage_medium_migrate_count (default is 3).
* Strict Checking: If you want to ensure no one accidentally creates HDD tables in the future, you can set the FE config enable_strict_storage_medium_check to true. This will cause table creation to fail if the specified medium is not available on the BEs.
References
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
• administration/management/FE_configuration.md
• administration/management/BE_configuration.md
• sql-reference/sql-statements/Database/ALTER_DATABASE.md
References
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• sql-reference/sql-statements/Database/ALTER_DATABASE.md
• administration/management/FE_configuration.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.mdFrancisco Silva
06/24/2026, 4:59 PMRocky
06/24/2026, 4:59 PMstorage_medium property in SQL is mostly a metadata update.
Here is what happens internally:
1. Metadata Update
When you run ALTER TABLE ... MODIFY PARTITION (_) SET ("storage_medium" = "SSD"), the Front-end (FE) updates its internal metadata. It now knows that the tablets for this table _should* reside on SSD-tagged storage.
2. The "Migration" Process (Background)
Even if the data is physically on an SSD, StarRocks will trigger its Storage Migration logic.
* The Check: The FE instructs the BEs to ensure the tablets are on disks tagged as SSD.
* If already there: If the tablet is already stored on a path that the BE has identified as SSD, no data movement occurs. The BE simply confirms the tablet is in the correct location, and the task finishes instantly.
* If there is a mismatch: If you have multiple storage paths (some HDD, some SSD) and the tablet was sitting on an HDD-tagged path, the BE would physically copy the data to the SSD-tagged path and then delete the old copy.
3. Why it matters if you "hadn't specified it"
By default, if you don't specify storage_medium, StarRocks often defaults to HDD (unless all disks are SSDs).
* The Issue: If the metadata says HDD but the physical disk is an SSD, the system works fine, but it might not prioritize that table's I/O or could accidentally move it to a real HDD if you ever added one to the cluster.
* The Benefit: By setting it to SSD, you enable features like Storage Cooldown (if you later want to move old data to cheaper storage) and ensure the scheduler doesn't try to "balance" the data away from your fast disks to slower ones.
How to verify StarRocks knows they are SSDs
Before running the ALTER, check how StarRocks has classified your disks:
sql
SHOW BACKENDS\G
Look at the StoragePath or relevant JSON output. If it says [{"path":"/data/storage","total_capacity":...,"available_capacity":...,"is_ssd":true}], then StarRocks knows it's an SSD.
Summary: Since your data is already on physical SSDs, running the ALTER command will be a safe, metadata-heavy operation with minimal to no actual I/O overhead, as long as your BE storage_root_path is correctly identifying those paths as SSDs.
References
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
• administration/management/BE_configuration.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• administration/management/FE_configuration.md
• deployment/environment_configurations.md
References
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.md
• deployment/environment_configurations.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
• administration/management/FE_configuration.md
• faq/shared_data_faq.mdFrancisco Silva
06/24/2026, 5:16 PMRocky
06/24/2026, 5:17 PMshadow_automatic_partition (often seen as $shadow_automatic_partition in logs or SHOW PROC outputs) is an internal, system-managed partition used by StarRocks when a table has Automatic (Expression) Partitioning enabled.
It serves as a temporary "holding area" or a template for tablets before the system formally maps them to a specific time-based or value-based partition.
If you see this and want to change your storage medium to SSD, here is how it affects your migration:
1. Does the ALTER command cover it?
When you run the command to migrate existing data:
sql
ALTER TABLE <table_name> MODIFY PARTITION (*) SET ("storage_medium" = "SSD");
The (*) wildcard typically targets all named, materialized partitions. Since the shadow partition is a system internal, it may not be explicitly modified by this command, or it may not need to be because it doesn't "hold" long-term data in the same way.
2. The most important step for Automatic Partitioning
For tables using automatic partitioning, you must update the table-level default property. This ensures that every time StarRocks automatically creates a new partition (and its underlying shadow structures) for incoming data, it uses the SSD medium.
Run this command:
sql
ALTER TABLE <table_name> SET ("default.storage_medium" = "SSD");
3. Will it cause issues?
* No Manual Intervention Needed: You cannot (and should not) manually ALTER a partition named $shadow_automatic_partition. The system manages its lifecycle.
* Automatic Inheritance: Once you set the default.storage_medium at the table level, the automatic partitioning logic will respect this for all future data.
* Existing "Shadow" Data: If there is data currently "stuck" in a shadow state (usually due to a failed load or schema change), it will eventually be cleaned up or converted to a standard partition. Once it becomes a standard partition, the MODIFY PARTITION (*) command will catch it.
Summary of Action Plan
If you have a table with automatic partitioning, do the following to move to SSD:
1. Set the global default for the table (to catch future auto-partitions):
sql
ALTER TABLE <table_name> SET ("default.storage_medium" = "SSD");
2. Migrate all current partitions:
sql
ALTER TABLE <table_name> MODIFY PARTITION (*) SET ("storage_medium" = "SSD");
3. Verify: Check your existing partitions to ensure they are moving.
sql
SHOW PARTITIONS FROM <table_name>;
(The StorageMedium column should show SSD for all listed partitions. You won't see the shadow partition here; it only appears in low-level SHOW PROC or tablet debugging.)
References
• table_design/data_distribution/expression_partitioning.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• faq/operation_maintenance_faq.md
• administration/management/FE_configuration.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
References
• table_design/data_distribution/expression_partitioning.md
• table_design/data_distribution/dynamic_partitioning.md
• table_design/data_distribution/Data_distribution.mdRocky
06/24/2026, 5:17 PMFrancisco Silva
06/24/2026, 5:18 PMRocky
06/24/2026, 5:18 PMbe.conf (storage_root_path) are tagged as SSD, StarRocks will automatically set the storage_medium of all new tables to SSD by default.
* Action: Ensure every BE has its storage paths tagged correctly in `be.conf`:
bash
storage_root_path = /data1,medium:ssd;/data2,medium:ssd
2. The Table-Level Default (For Mixed Storage)
If you have a mix of HDD and SSD disks, StarRocks defaults to HDD for new tables. To make a table always use SSD for its future partitions (e.g., in dynamic or automatic partitioning), use:
sql
ALTER TABLE <table_name> SET ("default.storage_medium" = "SSD");
This property ensures that any new partition created for this specific table will inherit the SSD setting.
3. Strict Enforcement (Safety Check)
You can prevent anyone from accidentally creating tables on HDD (or ensure the system doesn't "silent fail" to HDD if SSDs are full) by enabling a strict check in the FE configuration:
sql
ADMIN SET FRONTEND CONFIG ("enable_strict_storage_medium_check" = "true");
With this enabled, if a user specifies PROPERTIES("storage_medium"="SSD") but no SSD-tagged disks are available on the BEs, the CREATE TABLE command will fail rather than falling back to HDD.
Summary
* Existing Tables: You must ALTER them individually (see previous response).
* New Tables (All SSD Cluster): Automatic. No action needed if be.conf is correct.
* New Tables (Mixed Cluster): You must specify "storage_medium" = "SSD" in the PROPERTIES of your CREATE TABLE statement.
References
• administration/management/BE_configuration.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
• faq/operation_maintenance_faq.md
• administration/management/FE_configuration.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• deployment/environment_configurations.md
References
• administration/management/BE_configuration.md
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• sql-reference/System_variable.md
• deployment/environment_configurations.mdFrancisco Silva
06/24/2026, 5:21 PMRocky
06/24/2026, 5:21 PM$shadow_automatic_partition with HDD in your information_schema confirms that your table is configured to create all future partitions on HDD by default.
To fix this for those specific tables (names 1 and 2), you need to update the table's "template" property.
The Fix
Run these two commands for each table:
1. Update the Template (Future Partitions):
This ensures the $shadow_automatic_partition (and any new partitions created by incoming data) will use SSD.
sql
ALTER TABLE ampledash_prod.`1` SET ("default.storage_medium" = "SSD");
ALTER TABLE ampledash_prod.`2` SET ("default.storage_medium" = "SSD");
2. Update Existing Partitions (Current Data):
Even if you don't see them in your current query, if there are any "real" partitions already holding data, this will migrate them.
sql
ALTER TABLE ampledash_prod.`1` MODIFY PARTITION (*) SET ("storage_medium" = "SSD");
ALTER TABLE ampledash_prod.`2` MODIFY PARTITION (*) SET ("storage_medium" = "SSD");
Why you see this
* The Shadow Partition is a Template: For tables with Automatic Partitioning, StarRocks keeps this internal "shadow" partition as a blueprint. If this blueprint says HDD, every time a new day/hour/value triggers the creation of a new partition, it will be created as HDD.
* Metadata Consistency: Since your physical disks are already SSDs, running the commands above will update the metadata. StarRocks will check the physical location, realize the data is already on an SSD-tagged path, and simply update the status to SSD without a heavy data move.
How to verify it worked
After running the ALTER ... SET ("default.storage_medium" = "SSD") command, wait a few seconds and re-run your query:
sql
SELECT DB_NAME, TABLE_NAME, PARTITION_NAME, STORAGE_MEDIUM
FROM information_schema.partitions_meta
WHERE TABLE_NAME IN ('1', '2')
AND DB_NAME = 'ampledash_prod';
The STORAGE_MEDIUM for the shadow partition should now reflect SSD.
References
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• faq/operation_maintenance_faq.md
• table_design/data_distribution/expression_partitioning.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
• administration/management/BE_configuration.md
• administration/management/FE_configuration.md
References
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.mdFrancisco Silva
06/24/2026, 5:23 PMRocky
06/24/2026, 5:24 PMstorage_medium for $shadow_automatic_partition isn't changing after you run the ALTER commands, it is usually due to one of three specific reasons:
1. Backticks are Required for Numeric Table Names
Since your table names are numbers (1 and 2), the SQL will fail or do nothing if you don't wrap them in backticks. StarRocks interprets 1 as an integer rather than a table name.
Try this exactly:
sql
ALTER TABLE ampledash_prod.`1` SET ("default.storage_medium" = "SSD");
ALTER TABLE ampledash_prod.`2` SET ("default.storage_medium" = "SSD");
2. Missing medium:ssd in BE Config
StarRocks will refuse to update the metadata to SSD if it doesn't believe you have any SSD disks available.
* Check your BEs: Run SHOW BACKENDS\G and look at the Disks column.
* If you don't see SSD listed in the disk info there, you must edit be.conf on every worker node:
bash
# Ensure your path includes ,medium:ssd
storage_root_path = /your/data/path,medium:ssd
After changing be.conf, you must restart the BEs.
3. Try Modifying the Table Property Directly
In some versions of StarRocks, the default. prefix only applies to future Dynamic partitions, while Automatic (Expression) partitioning relies on the table-level property. Try setting it without the default. prefix:
sql
ALTER TABLE ampledash_prod.`1` SET ("storage_medium" = "SSD");
4. Why the Shadow Partition is Stubborn
The $shadow_automatic_partition is a template. In certain StarRocks versions, this template is initialized when the table is created and doesn't always refresh its metadata in information_schema immediately after an ALTER.
The ultimate test:
Load a single row of data into the table that belongs to a new partition (e.g., a date that hasn't been used yet).
sql
-- Insert a dummy row for a new date
INSERT INTO ampledash_prod.`1` VALUES (...);
-- Now check the real partition created
SHOW PARTITIONS FROM ampledash_prod.`1`;
If the newly created partition shows SSD, then your ALTER worked, and the HDD you see on the shadow partition in partitions_meta is just a metadata artifact that you can safely ignore.
Summary Checklist
1. Do you have backticks around the table names? (`` 1 ``)
2. Does SHOW BACKENDS actually show SSD disks?
3. Did you try setting storage_medium without the default. prefix?
References
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• administration/management/BE_configuration.md
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/administration-management/SHOW_BACKENDS.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md
References
• faq/operation_maintenance_faq.md
• sql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md
• sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.mdFrancisco Silva
06/24/2026, 5:33 PMRocky
06/24/2026, 5:34 PMstarrocks_fe_tablet_num metric for newly added BE nodes, it is typically due to the update frequency of the FE's internal metadata or the fact that no tablets have been migrated to those nodes yet.
Here are the most likely reasons and how to check them:
1. The 5-Minute Update Interval
The FE does not poll backends for tablet statistics in real-time. It uses a configuration parameter called tablet_stat_update_interval_second, which defaults to 300 seconds (5 minutes).
* Reason: If you added the BE nodes less than 5 minutes ago, the FE simply hasn't triggered its background thread to pull the tablet counts yet.
* Verification: Run SHOW BACKENDS; in your MySQL client. If the TabletNum column shows 0 or the Status column doesn't show a recent timestamp for those nodes, the FE hasn't finished its first collection cycle.
2. No Tablets Have Been Balanced Yet
A newly added BE node starts with 0 tablets.
* Reason: Some monitoring exporters (like Prometheus) or internal StarRocks metric collectors may omit a metric series if the value is 0 or if the BE hasn't "reported in" with data yet.
* Action: Check if rebalancing is actually happening. By default, StarRocks will slowly migrate tablets to the new nodes. You can check the progress with:
sql
SHOW PROC '/cluster_balance/history';
Once the first tablet is moved to a new BE, the metric is much more likely to appear.
3. "Alive" Status Check
The FE only exports metrics for backends it considers "Alive."
* Check: Run SHOW BACKENDS; and verify the Alive column is true for the new nodes.
* Common Issue: If they are false, check the ErrMsg column. Often, a firewall is blocking the Heartbeat Port (9050) or the Thrift Port (9060), preventing the FE from communicating with the new BEs.
4. Metric Scraping and BE ID
If you are looking at a dashboard (like Grafana), the metric is grouped by backend_id.
* Reason: New BE nodes get brand new IDs. If your dashboard or Prometheus query is filtered by specific IDs or if the Prometheus target hasn't been refreshed to pick up new labels, they won't appear.
* Test: Curl the FE metrics endpoint directly from the leader FE node to see if the raw data is there:
bash
curl http://<FE_IP>:<FE_HTTP_PORT>/metrics | grep starrocks_fe_tablet_num
Summary Checklist
1. Wait 5 minutes for the tablet_stat_update_interval_second to trigger.
2. Ensure SHOW BACKENDS shows the nodes as Alive.
3. Ensure tablet_sched_disable_balance is set to false (default) so that tablets actually move to the new nodes.
4. Verify the BEs can reach the FE on its rpc_port (default 9020) and the FE can reach the BEs on their heartbeat_port (default 9050).
References
• administration/management/monitoring/metrics.md
• administration/management/FE_configuration.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_BACKENDS.md
• administration/management/BE_configuration.md
• faq/operation_maintenance_faq.md
• administration/management/monitoring/Monitor_and_Alert.md
References
• administration/management/monitoring/metrics.md
• administration/management/FE_configuration.mdRocky
06/24/2026, 5:34 PMFrancisco Silva
06/25/2026, 9:15 AMRocky
06/25/2026, 9:15 AMDelVector might be the same, the metadata and transactional overhead of creating those markers is vastly different.
Batching 10 rows into one load instead of 10 separate loads helps for several critical reasons:
1. Transactional Overhead (The "Commit" Phase)
Every single load in StarRocks is an ACID transaction. When you commit a load in a Primary Key table:
* Index Lookup/Update: The BE must load the Primary Key index (if not in memory), search for the locations of the existing keys, and update the index with new locations. Doing this once for 1,000 rows is much more efficient than doing it 1,000 times for 1 row.
* Version Creation: Every commit creates a new rowset version. StarRocks has a hard limit on the number of versions per tablet (default is 1,000). If you load once per second, you will hit the too many versions error very quickly because the background compaction cannot merge these tiny versions fast enough.
2. Metadata Amplification
Each load creates its own metadata entries.
* 10 Loads: Creates 10 Rowsets + 10 DelVector metadata objects + 10 Journal entries in the FE.
* 1 Load: Creates 1 Rowset + 1 DelVector metadata object + 1 Journal entry.
Even if the DelVector only contains 1 bit per deleted row, the file headers, checksums, and "bookkeeping" (metadata) for 10 separate files are significantly larger and more taxing on the FE/BE than one slightly larger file.
3. Compaction Pressure
The "Cost" of a load isn't just the write; it's the future Compaction required to clean it up.
* Small loads create many small files. To maintain query performance, StarRocks must constantly merge these files (compaction).
* If you load 1 row at a time, you trigger an avalanche of background IO. The system will spend 99% of its resources "cleaning up" your tiny loads instead of processing new data or queries.
* Compaction Score: As your versions grow, your "compaction score" increases. When it gets too high, StarRocks will intentionally slow down or block your loads to prevent the cluster from crashing under metadata bloat.
4. CPU and IO Efficiency
* IOPS vs. Throughput: Small writes are limited by IOPS (the number of "trips" to the disk). Large batches utilize Throughput (the "speed" of the disk).
* Networking: Each load involves a handshake between the FE and BE. For 1 row, the network overhead of the handshake is often 100x larger than the data itself.
Summary
Think of it like a delivery truck:
* Small Loads: Driving a 10-ton truck across town to deliver a single envelope 100 times. You waste a massive amount of gas, create a traffic jam (compaction score), and wear out the engine (CPU/IO).
* Batching: Putting 100 envelopes in the truck and making one trip. The truck is full, the gas usage is the same, and the "metadata" (paperwork for the trip) is done once.
For Primary Key tables, batching is the single most important optimization for ingestion stability. If you cannot batch at the source, use Stream Load with a larger buffer or Routine Load (which has built-in batching parameters like max_batch_interval and max_interval).
References
• best_practices/primarykey_table.md
• table_design/table_types/primary_key_table.mdFrancisco Silva
06/25/2026, 9:16 AMRocky
06/25/2026, 9:16 AMINSERT), StarRocks gathers all that data in memory, sorts it, and flushes it to disk as a Rowset.
* Case A (Bad): You load 1 row. StarRocks creates Rowset #1 (containing 1 row).
* Case B (Good): You load 10,000 rows. StarRocks creates Rowset #2 (containing 10,000 rows).
In both cases, the FE/BE treats the Rowset as one metadata object.
2. Why it is not an "update per row"
In a Primary Key table, an "update" is physically a Delete + Insert:
1. The new data is written into a new Rowset.
2. The Primary Key index finds where the old version of that row was (e.g., Rowset #5, Row 10).
3. A DelVector is updated to say "Row 10 in Rowset #5 is now dead."
If you batch 1,000 updates into one load:
* Rowsets Created: 1
* Metadata entries: 1
* DelVector updates: 1 batch update to the delete bitmap.
If you do 1,000 separate 1-row updates:
* Rowsets Created: 1,000
* Metadata entries: 1,000
* DelVector updates: 1,000 individual transactions.
3. The "Cost" of a Rowset
Every Rowset has a physical cost regardless of how many rows are inside:
* File Handles: Each Rowset consists of "Segment" files on disk.
* Compaction: StarRocks has to "merge" Rowsets. It is much easier for the system to merge two Rowsets of 50,000 rows than to merge 100,000 Rowsets of 1 row each.
* Version Limit: Every tablet has a limit (usually 1,000) on how many Rowsets it can track before it starts failing with a too many versions error.
Summary
The Rowset is the "envelope" that holds your data. Batching doesn't change the amount of "mail" (rows/data), but it drastically reduces the number of "envelopes" (metadata/files) the system has to carry, sort, and eventually recycle.
References
• table_design/table_types/primary_key_table.md
• best_practices/table_clustering.md
• administration/management/monitoring/metrics.md
• best_practices/primarykey_table.md
References
• table_design/table_types/primary_key_table.mdFrancisco Silva
06/25/2026, 9:41 AMRocky
06/25/2026, 9:41 AMFrancisco Silva
06/25/2026, 9:46 AMRocky
06/25/2026, 9:46 AMFrancisco Silva
06/25/2026, 1:02 PMRocky
06/25/2026, 1:03 PMtablet_sched_balance_load_score_threshold (default: 0.1). The FE considers a BE "unbalanced" if its load score (a combination of disk usage and tablet count) deviates from the cluster average by more than 10%.
Using your specific Prometheus expression:
* Ideal State: < 0.1 (10% spread). This means your cluster is perfectly tuned.
* Healthy State: 0.1 – 0.2 (10-20% spread). In this range, the FE scheduler usually considers the cluster "balanced enough" and will not aggressively move tablets.
_ _Calculation logic:* If your highest BE is +10% from average and your lowest is -10%, your formula (max-min)/avg results in 0.2.
2. Recommended Alert Thresholds
Since tablet rebalancing is a background process that can take time (especially after adding new nodes or if disks are nearly full), you should set thresholds that distinguish between "normal scheduling lag" and "genuine skew."
Severity | Threshold | Meaning
Healthy | < 0.2 | The cluster is within the standard StarRocks tolerance.
Warning | 0.3| The spread is significantly wider than the FE's 10% target. This suggests the scheduler is struggling to move data, or balancing is disabled. Critical |
0.5| High risk of performance hotspots. Some BEs are handling 50%+ more tablets than others, leading to uneven CPU/memory pressure during queries. 3. Contextual Caveats * Disk vs. Count: By default, StarRocks prioritizes disk space balance over tablet count balance. If your tablets vary significantly in size (e.g., some are 100MB and some are 10GB), your tablet counts might look "unbalanced" even while disk usage is perfectly even. * Small Clusters: If you have very few tablets or very few BE nodes, a single table with a high bucket count can cause a high "imbalance" percentage that is mathematically unavoidable. * Manual Override: If you want the FE to be more aggressive in balancing tablet counts specifically (StarRocks 3.1+), you can check the FE config
tablet_sched_num_based_balance_threshold_ratio.
Summary Recommendation
Set your alert at 0.3 (30%) for a 15-minute duration. This prevents "flapping" alerts during routine data loads or minor maintenance while catching sustained distribution issues.
References
• administration/management/FE_configuration.md
• administration/management/monitoring/metrics.md
• faq/operation_maintenance_faq.md
• administration/management/compaction.md
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.mdFrancisco Silva
06/25/2026, 1:06 PMRocky
06/25/2026, 1:07 PMmax_query_retry_time (default: 2).
* When the FE coordinator detects a failure from a BE (such as a connection reset, timeout, or node crash), it checks if it has remaining retry attempts.
* If so, it discards the current failed execution and triggers a re-plan of the query.
2. Picking New Nodes (Replica Selection)
During the re-planning phase of the retry:
* Health Status: The FE checks the status of all BE nodes. If a node has failed its heartbeat, it is marked "not alive," and the FE will exclude it from the new execution plan.
* Replica Choice: For the tablets that were on the failed node, the FE will select an alternative healthy replica (if the table has multiple replicas) on a different BE.
* Blacklisting: Starting from version 3.3.0, StarRocks includes a BE Blacklist feature. If a BE is unreachable or causes an RPC failure, the FE can automatically add it to a temporary blacklist. This ensures that the immediate retry—and subsequent queries—avoid the problematic node until it is healthy again.
3. Limitations (When it won't help)
Retries are effective for transient network issues or isolated node crashes. However, they will not help in the following cases:
* Poison Queries: If a specific SQL query is causing the BE to crash (e.g., due to a software bug or hitting a memory limit), the FE will retry the query on a different node, which may then crash that node as well. This can lead to a "rolling crash" across the cluster.
* Single Replica: If your data is stored with replication_num = 1 and that node goes down, the query cannot be retried because the data is physically unavailable.
Summary of relevant configs:
Parameter | Default | Description
max_query_retry_time | 2 | Number of times the FE will retry a query if a BE-related error occurs.
thrift_rpc_retry_times | 3 | Number of times an individual RPC call is attempted.
enable_be_blacklist | true | (v3.3+) Automatically avoids BEs that have failed connections.
If you are seeing frequent BE crashes triggered by specific queries, it is recommended to check the be.out file on the crashed node to identify the query_id and the crash stack trace.
References
• administration/management/FE_configuration.md
• administration/management/BE_configuration.md
• faq/Sql_faq.md
• administration/management/resource_management/BE_Blacklist.md
References
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.md
• administration/management/FE_configuration.md
• administration/management/resource_management/Replica.mdFrancisco Silva
06/25/2026, 1:10 PMRocky
06/25/2026, 1:10 PMmax_query_retry_time to 0, you are effectively disabling query-level retries.
Here is exactly what happens in that scenario:
1. No Automatic Re-planning
Normally, if a BE node crashes or a network connection is lost mid-query, the FE "catches" that error, picks a different replica or node, and re-executes the query from scratch. With this set to 0, that logic is skipped. The query is treated as a "one-shot" attempt.
2. Immediate Error Reporting
If a failure occurs (e.g., a BE node goes down during a scan), the FE will immediately return the error to your application or client (e.g., [5067] [Frontend] RPC error: [10005] failed to send brpc...). You will not see the typical "Query failed, retrying..." internal status.
3. Protection Against "Rolling Crashes"
Setting this to 0 is often used as a safety measure during troubleshooting. If a specific "query of death" is causing BE nodes to crash (perhaps due to a bug or memory overflow), a retry would normally send that same query to a second BE node, potentially crashing it as well. By setting retries to 0, you ensure that a problematic query only affects the specific node it was assigned to, rather than taking down multiple nodes in a chain reaction.
4. Thrift RPC vs. Query Retry
It is important to distinguish this from thrift_rpc_retry_times:
* thrift_rpc_retry_times: This is a low-level retry for the connection itself. It will still try to re-establish a "handshake" if a packet is dropped.
* max_query_retry_time: This is a high-level retry for the entire query logic. If the RPC fails after its own internal retries, the FE gives up entirely when this is 0.
Summary
* Healthy Cluster: Keep it at the default (2) to provide seamless failover for transient network blips.
* Debugging/Unstable Queries: Set it to 0 to stop the FE from automatically retrying failed queries, making it easier to isolate which specific query or node is causing issues.
To change this setting (it is a mutable FE configuration):
sql
-- This must be set in the fe.conf or via admin command (if supported for your version)
ADMIN SET FRONTEND CONFIG ("max_query_retry_time" = "0");
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/management/resource_management/BE_Blacklist.md
References
• administration/management/FE_configuration.md
• sql-reference/System_variable.md
• administration/management/BE_configuration.md
• administration/management/resource_management/query_queues.mdFrancisco Silva
06/25/2026, 1:47 PMRocky
06/25/2026, 1:47 PMbufferflush.maxbytes (your setting: ~90MB).
2. Time-based: The time elapsed since the last flush reaches bufferflush.intervalms (your setting: 15 seconds).
3. Offset-based: The Kafka Connect framework triggers an offset commit, which is controlled by the global Kafka Connect setting <http://offset.flush.interval.ms|offset.flush.interval.ms> (default is usually 60,000ms or 60 seconds).
Which one will trigger in your case?
* High Throughput: If you are ingest more than 6MB/s ($90MB \div 15s$), the bufferflush.maxbytes limit will likely trigger the flush first.
* Low Throughput: If you ingest less than 6MB/s, the bufferflush.intervalms (15s) will trigger the flush to ensure your data latency doesn't exceed 15 seconds.
Note: If you want to ensure the 15-second latency is respected, make sure the Kafka Connect worker's <http://offset.flush.interval.ms|offset.flush.interval.ms> is not set to a value smaller than 15,000, as that would force more frequent flushes regardless of your connector settings.
References
• loading/Kafka-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.mdFrancisco Silva
06/25/2026, 1:47 PMRocky
06/25/2026, 1:48 PMbufferflush.maxbytes: The buffered data reaches this size limit.
* bufferflush.intervalms: The time since the last flush reaches this interval.
* <http://offset.flush.interval.ms|offset.flush.interval.ms>: This is a global Kafka Connect setting (defaulting to 60,000ms) that can also trigger a flush during offset commits.
You can find this in the official documentation under the "Usage Notes" for the Kafka connector.
References
• loading/Kafka-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.mdFrancisco Silva
06/25/2026, 2:18 PMRocky
06/25/2026, 2:19 PM<http://offset.flush.interval.ms|offset.flush.interval.ms> = 60000.
Even if the StarRocks connector has no data to send, the Kafka Connect framework will periodically call the connector's flush() method to commit offsets. When this happens, the StarRocks connector attempts a flush, which results in the log entry you see: Stream load manager flush finished - currentCacheBytes: 0.
2. Why your 15s (bufferflush.intervalms) isn't triggering
The bufferflush.intervalms setting only triggers a flush when there is actual data in the buffer.
* If your topic is idle: The connector will not initiate a Stream Load every 15 seconds for "zero bytes." Creating empty transactions in StarRocks is expensive, so the connector waits until either data arrives OR the Kafka Connect framework forces a flush (the 60s mark).
* If data is arriving very slowly: If your ingestion rate is extremely low, the Kafka Connect worker might not be calling the connector's put() method frequently enough to check the 15s timer. In many sink connectors, the internal timer logic is only evaluated when new records are handed to the connector.
3. How to verify and fix
To see if your 15s setting is actually working, you should:
1. Check with actual data: Produce a steady stream of messages to the Kafka topic. If the data is being loaded every 15 seconds, the bufferflush.intervalms is working. If it still waits 60 seconds even with pending data, then the configuration might be misplaced.
2. Align the intervals: If you want the "heartbeat" flushes to happen every 15 seconds regardless of data, you need to change the Kafka Connect worker configuration (usually connect-distributed.properties or your environment variables):
properties
# Set this in the Kafka Connect WORKER config, not the connector JSON
<http://offset.flush.interval.ms|offset.flush.interval.ms>=15000
3. Check `currentCacheBytes`: In your logs, currentCacheBytes is 0. This confirms that at the time of the flush, there was nothing to send. If you were sending data and it was being held up, you would see a number greater than 0 during the flush start.
Summary
The logs you've shared show idle flushes triggered by the Kafka Connect framework every 60 seconds. To get a 15-second flush of actual data, the connector is already configured correctly, but it will only "fire" that 15s timer if there are bytes sitting in the buffer.
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
• loading/Spark-connector-starrocks.mdFrancisco Silva
06/25/2026, 2:20 PMRocky
06/25/2026, 2:21 PMmaxCacheBytes: 94371840. This matches the bufferflush.maxbytes value you set. This means the connector "knows" about your custom settings.
2. The 60s flushes are "Empty Heartbeats"
The timestamps in your logs (consistently ~60 seconds apart) and the message currentCacheBytes: 0 indicate that these flushes are being triggered by the Kafka Connect framework's global <http://offset.flush.interval.ms|offset.flush.interval.ms> (which defaults to 60,000ms).
* Even when a connector has no data in its buffer, Kafka Connect will force a flush every 60 seconds to attempt an offset commit.
* The StarRocks connector responds to this by "finishing" a flush of 0 bytes.
3. Why the 15s timer isn't triggering
The bufferflush.intervalms=15000 timer only triggers a flush if there is actual data in the memory buffer.
* In your logs, currentCacheBytes is 0.
* If the buffer is empty, the 15-second timer has nothing to do and will not initiate a Stream Load.
Why is the buffer empty if there is data on the topic?
If you are certain there is data actively being produced to the topic, but the connector reports currentCacheBytes: 0 every 60 seconds, it means the data is not reaching the StarRocks connector's internal buffer. Common reasons include:
* Consumer Lag / Not Consuming: The connector task might not be assigned to any partitions, or the Kafka consumer within the connector is stuck.
* Deserialization Issues: If there are errors converting the Kafka records (e.g., JSON parsing errors), the records might be dropped before they reach the buffer. Check your logs for ERROR or WARN messages related to "Conversion" or "Record."
* Filtering: If you have Kafka Connect Transforms (SMTs) or a WHERE clause logic that is filtering out all records, the buffer will remain empty.
How to troubleshoot:
1. Check Task Status: Run GET /connectors/[connector_name]/status on your Kafka Connect REST API. Ensure the tasks are RUNNING and assigned partitions.
2. Verify Consumption: Check the Kafka Consumer Lag for the consumer group connect-[connector_name]. If the lag is high and not moving, the connector is not consuming.
3. Search for Errors: Look further back in the logs for messages like Failed to convert record or Stream load failed.
4. Test the 15s Trigger: To see the 15s timer in action, try producing a single large batch of records to the topic. You should see a log entry with currentCacheBytes > 0 approximately 15 seconds after the records are picked up.
Summary: The 60s interval you see is a fallback mechanism for idle connectors. The 15s interval isn't firing because the connector currently "thinks" it has 0 bytes to send.
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
• loading/Spark-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.mdFrancisco Silva
06/25/2026, 2:25 PMRocky
06/25/2026, 2:26 PMcurrentCacheBytes: 0 despite data actually being loaded.
1. The Logs show "Framework Flushes," not "Timer Flushes"
The log entries you see every 60 seconds (e.g., 14:13:17, 14:14:17) are triggered by the Kafka Connect framework calling the connector's flush() method to commit offsets (<http://offset.flush.interval.ms|offset.flush.interval.ms>).
* When Kafka Connect calls this method, the StarRocks connector must ensure all buffered data is sent.
* If your 15s timer (bufferflush.intervalms) is working correctly, it will have already flushed the data to StarRocks.
* By the time the 60s framework flush happens, the buffer is empty. This is why the log reports currentCacheBytes: 0.
* In many versions of the connector, the internal timer-based flushes are performed by a background thread and may be logged at the DEBUG level or under a different log message, whereas the 60s "offset flush" is logged at INFO by the StreamLoadManagerV2.
2. How to confirm the 15s interval is working
To verify that your data is actually loading every 15 seconds (and not just every 60 seconds), you should check the database side rather than the connector logs:
Run this query in StarRocks:
sql
SELECT label, status, create_time, load_bytes
FROM information_schema.loads
WHERE table_name = 'your_table_name'
ORDER BY create_time DESC
LIMIT 20;
* If the create_time timestamps are ~15 seconds apart: Your configuration is working perfectly. The 60s logs you see in the connector are just "heartbeat" checks finding an empty buffer.
* If the create_time timestamps are ~60 seconds apart: The 15s timer is indeed being ignored.
If it is being ignored (60s loads only):
If the database shows loads are only happening every 60s, check the following:
1. Placement of config: Ensure bufferflush.intervalms is at the top level of your connector JSON config, not inside a sink.properties block.
* Correct: "bufferflush.intervalms": "15000"
* Incorrect: "sink.properties.bufferflush.intervalms": "15000"
2. Data Volume: The timer-based flush is often evaluated when the put() method is called (when new records arrive). If your data is trickling in extremely slowly (e.g., one message every 30 seconds), the connector might not "check" the timer until the next record or the 60s framework flush occurs.
3. Connector Version: Older versions of the connector had inconsistencies with the internal timer logic. Ensure you are using a recent version of the starrocks-kafka-connector.
Summary: currentCacheBytes: 0 at the 60s mark is a *good sign*—it means the connector already cleared the buffer (likely via your 15s timer) before the framework forced a check. Check information_schema.loads to confirm the actual ingestion frequency.
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
• loading/Spark-connector-starrocks.md
References
• loading/Kafka-connector-starrocks.md
• loading/Flink-connector-starrocks.md
• administration/management/monitoring/metrics.mdFrancisco Silva
06/25/2026, 2:28 PMRocky
06/25/2026, 2:28 PMcurrentCacheBytes: 0) are triggered by the Kafka Connect framework's 60-second "heartbeat" flush. If you see data in the DB, it means your 15-second timer (bufferflush.intervalms) is likely working and clearing the buffer before the 60-second mark, leaving 0 bytes for the framework flush to handle.
1. SQL: Use information_schema.loads
The most accurate way to confirm your load timeframe is to query the history of successful transactions in StarRocks. This will show you exactly how many seconds pass between each "commit."
Run this query to see the interval between the last 20 loads for your table:
sql
SELECT
label,
status,
create_time,
TIMESTAMPDIFF(SECOND, LAG(create_time) OVER (ORDER BY create_time), create_time) AS seconds_since_last_load,
sink_rows,
scan_bytes
FROM information_schema.loads
WHERE table_name = 'your_table_name'
AND type = 'STREAM'
ORDER BY create_time DESC
LIMIT 20;
* If seconds_since_last_load is consistently ~15s, your configuration is working correctly.
* If it is consistently ~60s, the connector is ignoring your 15s timer (see troubleshooting below).
2. Metrics: Kafka Connect JMX
The StarRocks Kafka connector doesn't expose a dedicated "timer-count" metric, but you can monitor the standard Kafka Connect Task Metrics via JMX.
* Metric: kafka.connect:type=connector-task-metrics,connector="{connector}",task="{task}"
* Attribute: offset-commit-success-percentage or batch-size-avg.
* If your loads are happening every 15 seconds, you will see your StarRocks BE metrics (like starrocks_be_stream_load_sink_pull_data_count) incrementing at that higher frequency.
3. Log Levels
The "Stream load manager flush finished" message at the 60s mark is logged at the INFO level by the Kafka Connect framework's offset management.
* The timer-based flushes (the ones every 15s) are often logged by the internal Stream Load SDK used by the connector.
* To see the internal 15s triggers, you can set the connector's log level to DEBUG. In your log4j.properties (or your Kafka Connect logging config), add:
properties
log4j.logger.com.starrocks.connector.kafka=DEBUG
With DEBUG enabled, you should see logs from the StreamLoadManagerV2 or the StreamLoader specifically mentioning when a flush is triggered by the "interval" policy.
Why you see currentCacheBytes: 0 in the logs:
Kafka Connect has a mandatory flush every 60s (<http://offset.flush.interval.ms|offset.flush.interval.ms>).
1. At T+15s: Your 15s timer fires, sends data to StarRocks, and clears the buffer.
2. At T+30s: Your 15s timer fires again, sends more data, clears buffer.
3. At T+60s: Kafka Connect forces a flush. It checks the buffer, sees 0 bytes (because you just flushed at T+45s and T+60s), and logs: Stream load manager flush finished - currentCacheBytes: 0.
Summary: Your logs are showing "empty" framework flushes. Use the SQL query above to verify that the real data loads are happening every 15 seconds.
References
• sql-reference/information_schema/stream_loads.md
• sql-reference/information_schema/loads.md
• loading/loading_introduction/troubleshooting_loading.mdFrancisco Silva
06/25/2026, 2:37 PMRocky
06/25/2026, 2:37 PMSTATE, not status.
Here is the corrected query for information_schema.loads (available in StarRocks v3.1 and later). This will show you exactly how many seconds pass between each load:
sql
SELECT
LABEL,
STATE,
CREATE_TIME,
TIMESTAMPDIFF(SECOND, LAG(CREATE_TIME) OVER (ORDER BY CREATE_TIME), CREATE_TIME) AS seconds_since_last_load,
SINK_ROWS,
SCAN_BYTES
FROM information_schema.loads
WHERE TABLE_NAME = 'your_table_name'
AND TYPE = 'STREAM'
ORDER BY CREATE_TIME DESC
LIMIT 20;
If you are on a version earlier than v3.1:
Use the stream_loads table instead. Note that this table uses milliseconds for its time columns:
sql
SELECT
LABEL,
STATE,
FROM_UNIXTIME(CREATE_TIME_MS / 1000) AS create_time,
(CREATE_TIME_MS - LAG(CREATE_TIME_MS) OVER (ORDER BY CREATE_TIME_MS)) / 1000 AS seconds_since_last_load,
NUM_ROWS_NORMAL AS sink_rows
FROM information_schema.stream_loads
WHERE TABLE_NAME = 'your_table_name'
ORDER BY CREATE_TIME_MS DESC
LIMIT 20;
What to look for:
* 15s Intervals: If seconds_since_last_load is around 15, your bufferflush.intervalms is working.
* 60s Intervals: If it is consistently 60, the connector is ignoring your timer and only flushing when the Kafka Connect framework forces it.
Other ways to monitor:
1. FE/BE Logs: You can search the StarRocks BE (Backend) logs (<http://be.INFO|be.INFO>) for the string label or Stream load. Every successful load from the connector creates a log entry on the BE that received the data, including a timestamp.
2. Metrics: If you use Prometheus/Grafana, monitor the metric starrocks_be_stream_load_put_data_bytes. A "sawtooth" pattern or regular spikes every 15 seconds confirms the timer is triggering the loads.
References
• sql-reference/information_schema/loads.md
• sql-reference/information_schema/stream_loads.md
• administration/management/monitoring/metrics.mdFrancisco Silva
06/25/2026, 2:38 PMRocky
06/25/2026, 2:38 PMinformation_schema.loads, but the underlying metadata is changing so rapidly that the planner cannot get a stable "snapshot" to finish its work.
Since your Kafka connector is loading data every 15 seconds (and possibly for multiple tables), new load records are being written to the FE's internal metadata catalog constantly. This "metadata churn" triggers the error.
How to resolve this:
1. Retry the query: This is a transient error. Simply running the query again often works if you catch a small gap between load commits.
2. Add a WHERE filter: Instead of scanning the entire load history, restrict the query to a specific label or time range. This reduces the amount of metadata the planner has to lock and process.
sql
SELECT * FROM information_schema.loads
WHERE TABLE_NAME = 'your_table'
AND CREATE_TIME > NOW() - INTERVAL 5 MINUTE
ORDER BY CREATE_TIME DESC;
3. Use SHOW LOAD (Alternative): If the information_schema table continues to fail, use the standard SHOW LOAD command. It bypasses the complex SQL planning phase and pulls directly from the metadata:
sql
SHOW LOAD FROM your_database_name WHERE Label LIKE "starrocks-kafka-connector%" ORDER BY CreateTime DESC LIMIT 10;
4. Check FE Load: If this error happens frequently, your Frontend (FE) might be under high CPU/Memory pressure or experiencing frequent GC (Garbage Collection). Monitor your FE logs (fe.log and fe.gc.log) to see if the metadata management is struggling.
Why is the schema "updating"?
In StarRocks, the information_schema.loads table is a "virtual" table. When you query it, the FE must gather information from its internal memory structures. If a load job finishes and updates that structure while the planner is still calculating the query plan, the version mismatch triggers this safeguard to prevent inconsistent results.
References
• sql-reference/information_schema/loads.md
• sql-reference/sql-statements/data-manipulation/SHOW_LOAD.md
• loading/loading_introduction/troubleshooting_loading.md
-loading)
References
• sql-reference/information_schema/loads.md
• sql-reference/information_schema/load_tracking_logs.md
• loading/loading_introduction/troubleshooting_loading.mdFrancisco Silva
06/25/2026, 3:03 PMRocky
06/25/2026, 3:05 PM[], it indicates a systemic issue where your metadata is updating faster than the FE can process even simple query plans.
Why this is happening
1. Too Frequent Loads: Each time the StarRocks Kafka Connector (or any Stream Load) commits, it increments the table's "visible version." If you have multiple tables being loaded every few seconds, the FE is constantly invalidating its internal metadata cache.
2. Information Schema Sensitivity: You specifically mentioned querying information_schema.loads. This table is dynamically generated from the load metadata. If new loads are finishing while you are querying the load history, the schema "version" for that virtual table is constantly shifting.
3. FE Performance / GC: If your FE is experiencing high CPU usage or frequent Garbage Collection (GC) pauses, the time it takes to "plan" a query increases. The longer the planning phase, the higher the chance a load will commit and invalidate the plan mid-way.
Immediate Solutions
1. Increase Query Retries
You can tell the FE to be more persistent before throwing this error. Run this command to increase the retry limit from the default (2) to 10:
sql
ADMIN SET FRONTEND CONFIG ("max_query_retry_time" = "10");
(This takes effect immediately without a restart.)
2. Slow Down the Ingestion (Batching)
If your Kafka Connector is flushing every few seconds or for very small amounts of data, you are putting extreme pressure on the FE metadata.
* Increase bufferflush.intervalms in your connector config (e.g., set it to 30000 for 30 seconds).
* Increase bufferflush.maxbytes to ensure you are sending fewer, larger batches.
3. Avoid information_schema during high-load
If you need to check load status, use the SHOW LOAD command instead of querying information_schema.loads. SHOW LOAD is a metadata-direct command and is less likely to trigger the planning version error:
sql
SHOW LOAD FROM your_db WHERE Label LIKE "your_label%" ORDER BY CreateTime DESC LIMIT 10;
4. Check FE Health
Check your FE logs (fe.gc.log and fe.log). If you see "Full GC" or "Long GC" messages, you may need to increase the FE JVM heap size in fe.conf (e.g., change -Xmx8g to -Xmx16g).
Summary for your Kafka Connector
The error confirms your connector is likely pushing data too frequently. By increasing the batching interval and the FE retry count, you give the system the "stable window" it needs to plan queries.
References
• administration/management/FE_configuration.md
• sql-reference/sql-statements/data-manipulation/SHOW_LOAD.md
• faq/Sql_faq.md
References
• administration/management/FE_configuration.md
• administration/management/BE_configuration.mdFrancisco Silva
06/25/2026, 3:08 PMmax_query_retry_time can be 0? or needs to be 1?Francisco Silva
06/25/2026, 3:10 PMRocky
06/25/2026, 3:10 PMFrancisco Silva
06/25/2026, 3:10 PMmax_query_retry_time can be 0? or needs to be 1?Rocky
06/25/2026, 3:10 PMmax_query_retry_time can be set to 0, but you probably don't want to do that in your current situation.
Here is how the values work:
* 0: Disables retries entirely. The query will fail immediately the very first time it encounters a metadata change during planning.
* 1: Allows exactly one retry attempt if the first one fails.
* 2 (Default): Allows two retry attempts.
Why you should NOT set it to 0
Since you are seeing the "schema had been updated frequently" error, it means StarRocks is already exhausting the default 2 retries and still failing. Setting it to 0 will make the error occur more frequently because the FE won't even try to recover from a version mismatch; it will just throw the error instantly.
Recommendation
If you are trying to stop the "schema updated frequently" error, you should increase the value rather than decreasing it.
* To fix the error: Set it to 5 or 10.
* To disable retries: Set it to 0.
sql
-- To give the query more chances to succeed despite background loads:
ADMIN SET FRONTEND CONFIG ("max_query_retry_time" = "10");
Note: If setting this to a high number still doesn't help, the root cause is likely that your Kafka batches are too small and frequent. You should increase the bufferflush.intervalms in your Kafka Connector configuration to reduce the metadata update frequency.
References
• administration/management/FE_configuration.md
• faq/Sql_faq.mdFrancisco Silva
06/25/2026, 3:12 PMRocky
06/25/2026, 3:13 PMbufferflush.intervalms: Increase this from the default 1000 (1 second) to 5000 or 10000 (10 seconds).
* bufferflush.maxbytes: Increase this to ensure you only flush when a significant amount of data is ready (e.g., 67108864 for 64MB).
* <http://offset.flush.interval.ms|offset.flush.interval.ms>: (Kafka Connect worker setting) Ensure this isn't forcing flushes too frequently.
2. Enable "Merge Commit"
StarRocks has a feature specifically designed to combat metadata churn from high-concurrency small loads. It merges multiple small Stream Load requests into a single transaction commit.
You can pass this configuration through the Kafka Connector's `sink.properties`:
properties
# Enable merging multiple small loads into one transaction
sink.properties.enable_merge_commit = true
# Wait up to 5 seconds to group loads together
sink.properties.merge_commit_interval_ms = 5000
Note: This is supported in newer versions of the connector and StarRocks (v3.2+ recommended).
3. Use Multi-Table Load (if applicable)
If you are loading many small tables, every single table update triggers a global metadata version change in the FE's catalog. In StarRocks 4.0+, the Stream Load Transaction Interface supports multi-table transactions, which allows you to commit updates to multiple tables in a single atomic action, drastically reducing the total number of metadata updates.
4. Optimize Metadata Table Queries
The error "schema of [] had been updated" often happens when you query information_schema.loads. This table is a virtual view of the FE's internal state.
* *Avoid `SELECT `: Querying all columns of information_schema.loads is expensive.
* Filter by Label or Time: WHERE CREATE_TIME > ... narrows the metadata scan.
* Use SHOW LOAD: This command is "lighter" than querying the information_schema table and is less likely to fail during high-frequency ingestion.
5. FE Hardware & JVM
If your metadata is large, the FE needs enough "breathing room" to process plan generations before the next version commit.
* JVM Heap: Ensure your FE has at least 16GB-32GB of heap (-Xmx) if you have thousands of tablets and high ingestion.
* CPU: Metadata planning is CPU-bound. If FE CPU is at 80%+, planning takes longer, increasing the chance of a version mismatch mid-plan.
Summary of "Ideal" Configuration
Setting | Recommended Value | Why?
bufferflush.intervalms | 5000 - 30000 | Fewer commits = more stable metadata.
enable_merge_commit | true | Groups concurrent small loads into one version increment.
max_query_retry_time | 10 | Gives the FE more "tries" to catch a stable version.
By moving from "1-second flushes" to "10-second flushes," you reduce the metadata pressure by 90%, which usually makes this error disappear entirely.
References
• administration/management/compaction.md
• administration/management/FE_configuration.md
• best_practices/partitioning.mdFrancisco Silva
06/26/2026, 10:56 AMRocky
06/26/2026, 10:56 AMSHOW DATA command, as it reports the total physical disk usage across all replicas.
Instead, use one of the following methods to find the size of a single copy of your data, which is what you'll need to compare against backup sizes:
Method 1: Using information_schema.partitions_meta (Recommended)
This is the most accurate way to query the logical size of your data. The DATA_SIZE column in this view represents the size of exactly one replica.
sql
SELECT
DB_NAME,
SUM(DATA_SIZE) / 1024 / 1024 / 1024 AS size_gb_logical
FROM information_schema.partitions_meta
WHERE DB_NAME = 'your_database_name'
GROUP BY DB_NAME;
Method 2: Using SHOW PARTITIONS
If you want to see the logical size for a specific table, you can run:
sql
SHOW PARTITIONS FROM your_table_name;
Look at the DataSize column. This value represents the size of one replica for each partition. Summing these values gives you the table's logical size.
Why this is different from SHOW DATA
* SHOW DATA: Reports the Total Physical Size. If you have a 1GB table with a replication factor of 3, SHOW DATA will report 3GB.
* BACKUP: When StarRocks performs a backup, it takes a snapshot of the unique data (one replica). Therefore, the size of your backup files in S3/HDFS will match the Logical Size (1GB in the example above), not the total replicated size.
Summary Comparison
Command / View | Size Reported | Use Case
SHOW DATA | Total (including all replicas) | Checking disk/quota usage.
information_schema.partitions_meta | Logical (one replica) | Comparing to backup sizes.
SHOW BACKUP | Logical | Verifying the actual size written to storage.
Note: In shared-data clusters (compute-storage separated), the DATA_SIZE and STORAGE_SIZE in partitions_meta will likely be identical because there is only one copy of data stored in the object warehouse.
References
• sql-reference/sql-statements/Database/SHOW_DATA.md
• sql-reference/information_schema/partitions_meta.md
• sql-reference/sql-statements/table_bucket_part_index/SHOW_PARTITIONS.md
• faq/Sql_faq.md
References
• sql-reference/information_schema/partitions.md
• sql-reference/information_schema/partitions_meta.md
• sql-reference/sql-statements/table_bucket_part_index/SHOW_PARTITIONS.mdFrancisco Silva
06/26/2026, 4:34 PMfor (int i = 0; i < Config.max_query_retry_time; ++i) {
if (!isSchemaValid) {
planStartTime = OptimisticVersion.generate();
reAnalyzeStmt(queryStmt, session, plannerMetaLocker);
defend yourselfRocky
06/26/2026, 4:35 PM0 means "Zero Retries" (1 Total Attempt)
The code snippet you provided is typically part of a "retry block" that executes after an initial planning failure or as a mechanism to handle optimistic version mismatches.
* Initial Attempt: The query is analyzed and a plan is attempted. If the schema version changes during this window, isSchemaValid becomes false.
* The Loop: The loop for (int i = 0; i < Config.max_query_retry_time; ++i) represents the additional attempts allowed.
* If you set it to 0, the loop condition 0 < 0 is false. The code skips the retry block entirely. The query fails immediately on the first mismatch.
* If you set it to 1, the loop runs once (i=0), allowing for one retry.
* The default is 2, meaning the loop can run twice, allowing for a total of 3 attempts (1 initial + 2 retries).
2. Can it be 0?
Yes, it can be set to 0.
However, some versions of StarRocks (and derived distributions) include a safety check in the configuration loader that treats non-positive values as 1 or 0 depending on whether the variable represents "total attempts" or "additional retries."
In the specific context of the "schema had been updated frequently" error:
* Setting it to 0 is valid SQL syntax, but it will make your error much worse. It tells the FE: "If the schema changes even once while I'm thinking, just give up immediately."
* If you are already seeing the error with the default (2), it means StarRocks has already tried 3 times and failed every time. Setting it to 0 or 1 will only cause the query to fail faster.
Summary of Values
Value | Meaning | Result for your "frequent schema update" error
0 | 0 Retries (1 attempt) | Error happens almost immediately.
1 | 1 Retry (2 attempts) | Error happens very frequently.
2 (Default) | 2 Retries (3 attempts) | You are currently seeing failures here.
5+ | 5+ Retries | Recommended to "wait out" the metadata churn.
Recommendation
If your goal is to stop the errors, you should set it to a higher number (e.g., 10) to give the FE more chances to "catch" a stable metadata version between your Kafka commits.
sql
-- Increase retries to handle high-frequency commits
ADMIN SET FRONTEND CONFIG ("max_query_retry_time" = "10");
References
• administration/management/FE_configuration.md
• faq/Sql_faq.md
References
• administration/management/FE_configuration.md
• administration/management/BE_configuration.mdFrancisco Silva
06/26/2026, 4:36 PMpublic static ExecPlan createQueryPlanWithReTry(QueryStatement queryStmt,
ConnectContext session,
TResultSinkType resultSinkType,
PlannerMetaLocker plannerMetaLocker,
long planStartTime) {
QueryRelation query = queryStmt.getQueryRelation();
List<String> colNames = query.getColumnOutputNames();
// 1. Build Logical plan
ColumnRefFactory columnRefFactory = new ColumnRefFactory();
boolean isSchemaValid = true;
int sourceTablesCount = collectSourceTablesCount(session, queryStmt);
// TODO: double check relatedMvs for OlapTable
// only collect once to save the original olapTable info
// the original olapTable in queryStmt had been replaced with the copied olapTable
Set<OlapTable> olapTables = collectOriginalOlapTables(session, queryStmt);
for (int i = 0; i < Config.max_query_retry_time; ++i) {
if (!isSchemaValid) {
planStartTime = OptimisticVersion.generate();
reAnalyzeStmt(queryStmt, session, plannerMetaLocker);
colNames = queryStmt.getQueryRelation().getColumnOutputNames();
isSchemaValid = true;
}
try {
LogicalPlan logicalPlan;
MVTransformerContext mvTransformerContext = MVTransformerContext.of(session, true);
try (Timer ignored = Tracers.watchScope("Transformer")) {
// get a logicalPlan without inlining views
TransformerContext transformerContext =
new TransformerContext(columnRefFactory, session, mvTransformerContext);
logicalPlan = new RelationTransformer(transformerContext).transformWithSelectLimit(query);
}
boolean isShortCircuit =
ShortCircuitPlanner.checkSupportShortCircuitRead(logicalPlan.getRoot(), session);
OptExpression optimizedPlan;
try (Timer ignored = Tracers.watchScope("Optimizer")) {
OptimizerContext optimizerContext = OptimizerFactory.initContext(session, columnRefFactory);
// 2. Optimize logical plan and build physical plan
// FIXME: refactor this into Optimizer.optimize() method.
// set query tables into OptimizeContext so can be added for mv rewrite
if (Config.skip_whole_phase_lock_mv_limit >= 0) {
optimizerContext.setQueryTables(olapTables);
}
if (isShortCircuit) {
optimizerContext.setOptimizerOptions(OptimizerOptions.newShortCircuitOpt());
}
optimizerContext.setMvTransformerContext(mvTransformerContext);
optimizerContext.setStatement(queryStmt);
optimizerContext.setSourceTablesCount(sourceTablesCount);
Optimizer optimizer = OptimizerFactory.create(optimizerContext);
optimizedPlan = optimizer.optimize(logicalPlan.getRoot(), new PhysicalPropertySet(),
new ColumnRefSet(logicalPlan.getOutputColumn()));
}
try (Timer ignored = Tracers.watchScope("ExecPlanBuild")) {
// 3. Build fragment exec plan
// SingleNodeExecPlan is set in TableQueryPlanAction to generate a single-node Plan,
// currently only used in Spark/Flink Connector
// Because the connector sends only simple queries, it only needs to remove the output fragment
ExecPlan plan = PlanFragmentBuilder.createPhysicalPlan(
optimizedPlan, session, logicalPlan.getOutputColumn(), columnRefFactory, colNames,
resultSinkType,
!session.getSessionVariable().isSingleNodeExecPlan(), isShortCircuit);
isSchemaValid = checkOlapTableSchemaValid(olapTables, planStartTime);
if (isSchemaValid) {
plan.setLogicalPlan(logicalPlan);
plan.setColumnRefFactory(columnRefFactory);
return plan;
}
}
} catch (RuntimeException exception) {
isSchemaValid = checkOlapTableSchemaValid(olapTables, planStartTime);
if (isSchemaValid) {
throw exception;
}
}
}
List<String> updatedTables = Lists.newArrayList();
for (OlapTable olapTable : olapTables) {
if (!OptimisticVersion.validateTableUpdate(olapTable, planStartTime)) {
updatedTables.add(olapTable.getName());
}
}
throw new StarRocksPlannerException(ErrorType.INTERNAL_ERROR,
"schema of %s had been updated frequently during the plan generation", updatedTables);
}
private static boolean checkOlapTableSchemaValid(Set<OlapTable> olapTables, long planStartTime) {
return olapTables.stream().allMatch(t -> OptimisticVersion.validateTableUpdate(t, planStartTime));
}
public static Set<OlapTable> collectOriginalOlapTables(ConnectContext session, StatementBase queryStmt) {
Set<OlapTable> olapTables = Sets.newHashSet();
PlannerMetaLocker locker = new PlannerMetaLocker(session, queryStmt);
try {
// Need lock to avoid olap table metas ConcurrentModificationException
lock(locker);
AnalyzerUtils.copyOlapTable(queryStmt, olapTables);
return olapTables;
} finally {
unLock(locker);
}
}
you sure?Rocky
06/26/2026, 4:36 PMmax_query_retry_time.
Here is the logic breakdown based on that source code:
1. If max_query_retry_time is 0
* The loop for (int i = 0; i < 0; ++i) will not execute at all.
* The code immediately jumps to the end, calculates updatedTables, and throws the exception.
* This explains why a user might see schema of [] had been updated (with an empty list) if they set the config to `0`—it never even tried to plan.
2. If max_query_retry_time is 1 (The "No Retry" mode)
* The loop runs once (i = 0).
* It performs the first plan attempt.
* At the end of the attempt, it checks checkOlapTableSchemaValid.
* If the schema changed while it was planning, isSchemaValid becomes false.
* The loop ends, and the exception is thrown.
* Result: You get one attempt and zero retries.
3. If max_query_retry_time is 2 (The StarRocks Default)
* Iteration 0: First attempt. If schema becomes invalid, isSchemaValid is set to false.
* Iteration 1: !isSchemaValid is now true. It calls reAnalyzeStmt (the retry) and tries to plan again.
* If it's still invalid at the end of Iteration 1, the loop ends.
* Result: You get two total attempts (1 initial + 1 retry).
What this means for your cluster
If you are currently getting this error, it means StarRocks has exhausted all max_query_retry_time attempts because the metadata version changed every single time it tried to build the plan.
To fix this, you must increase the value:
If your Kafka Connector is committing every 1 second and your plan takes 1.1 seconds, a value of 2 will never be enough. You should set it to a higher buffer:
sql
-- Set this in your FE config (fe.conf) or via admin command
ADMIN SET FRONTEND CONFIG ("max_query_retry_time" = "10");
This gives the query 10 "lives" to find a stable window where no commits happen.
Why "defend" the code?
The code uses Optimistic Locking. Instead of locking the metadata (which would stop all loads and freeze the cluster), it assumes the metadata is fine, does the work, and checks at the end. If it's wrong, it tries again. This is great for performance but fails under the "heavy fire" of constant small commits.
Thank you for providing the code snippet—it makes it clear that Config value == Total attempts.Francisco Silva
07/10/2026, 9:54 AMRocky
07/10/2026, 9:54 AMquery_timeout globally in StarRocks, you use the SET GLOBAL command. This will apply the new timeout to all new connections established after the command is run.
1. Set for the entire cluster
Run the following SQL as a user with administrative privileges (e.g., root):
sql
-- Example: Set global query timeout to 1 hour (3600 seconds)
SET GLOBAL query_timeout = 3600;
* Unit: Seconds.
* Default: 300 seconds (5 minutes).
* Scope: All new sessions. Existing active sessions will retain their old timeout value.
──────────
2. Set for a specific user
If you only want a specific user to have a different global default, you can set it as a user property (supported in v3.3.3+):
sql
ALTER USER 'username' SET PROPERTIES ('session.query_timeout' = '600');
──────────
3. Important Note on Write Operations (v3.4.0+)
Starting from v3.4.0, the query_timeout variable no longer applies to write operations (like INSERT, UPDATE, DELETE, CTAS, or Materialized View refreshes). These operations now use a separate variable:
sql
-- Set global insert/write timeout
SET GLOBAL insert_timeout = 3600;
Verification
You can verify the current global and session values using:
sql
-- Check current session value
SHOW VARIABLES LIKE 'query_timeout';
-- Check global value
SHOW GLOBAL VARIABLES LIKE 'query_timeout';
References
• sql-reference/System_variable.md
• administration/management/resource_management/Query_management.md
References
• sql-reference/System_variable.md
• faq/operation_maintenance_faq.md
• administration/management/resource_management/query_queues.md
• administration/management/resource_management/Query_management.md
• administration/management/FE_configuration.mdFrancisco Silva
07/10/2026, 9:56 AMRocky
07/10/2026, 9:56 AMquery_timeout in the fe.conf file.
In StarRocks, query_timeout is a session variable, not an FE configuration parameter. These are two different categories of settings:
1. FE Configuration (fe.conf): These are process-level parameters (e.g., JVM heap size, metadata port, max_query_retry_time). You view these with ADMIN SHOW FRONTEND CONFIG.
2. Session Variables: These control the behavior of SQL execution (e.g., query_timeout, enable_spill). You view these with SHOW VARIABLES.
Why you don't need fe.conf for this
The SET GLOBAL command is actually more persistent than fe.conf for this specific setting:
* When you run SET GLOBAL query_timeout = 3600;, the value is written to StarRocks' internal metadata (BDB JE).
* Because it is in the metadata, the value is synchronized across all FEs and persists even after a full cluster restart.
* Any new connection established after the command is run will automatically inherit the new value.
Exceptions (Things you can set in fe.conf)
While query_timeout isn't there, you can set these related timeouts in `fe.conf`:
* `insert_load_default_timeout_second`: The default timeout for INSERT statements.
* `brpc_send_plan_fragment_timeout_ms`: The RPC timeout when sending the plan to BEs.
Summary: Use SET GLOBAL query_timeout = <seconds>; from your SQL client. It is the official and persistent way to handle this globally.
References
• administration/management/FE_configuration.mdFrancisco Silva
07/10/2026, 10:02 AMRocky
07/10/2026, 10:03 AMquery_timeout being ignored or queries being killed unexpectedly when using ProxySQL, it is usually due to one of the following three reasons:
1. The ProxySQL "Internal" Timeout
ProxySQL has its own global timeout that is independent of StarRocks. If this is shorter than your StarRocks session timeout, ProxySQL will kill the connection before StarRocks finished the query.
* Variable: mysql-default_query_timeout
* Default: 36,000,000ms (10 hours).
* Conflict: If a previous administrator lowered this to something like 30000 (30s), your StarRocks setting of SET query_timeout = 60 will be irrelevant because ProxySQL will kill the query at 30 seconds.
2. Multiplexing "State Loss"
ProxySQL's most powerful feature is multiplexing, where it shares a small pool of backend connections among many frontend clients.
* The Issue: When you run SET query_timeout = 600;, StarRocks sets that for the current backend connection.
_ If ProxySQL then decides to move your next query to a _different* backend connection (multiplexing), that new connection might still have the default 300s timeout.
* Result: You think you set a long timeout, but your actual query runs on a connection that doesn't have it.
How to fix:
* Option A: Use SET GLOBAL query_timeout = 600; in StarRocks. This ensures every backend FE/connection has the same base value, so switching connections doesn't matter.
* Option B: Disable multiplexing for the session. Running certain commands (like SET @variable = 1) usually tells ProxySQL to "pin" that session to one backend connection, but query_timeout is a StarRocks variable that ProxySQL's parser might not recognize as a "pinning" command.
3. Parser Recognition
ProxySQL tracks session variables by parsing the SQL statements. Because query_timeout is specific to StarRocks and not a standard MySQL variable (like wait_timeout or sql_mode), older versions of ProxySQL might not "see" the change and thus won't know they need to "replay" that setting if they switch backend connections.
Recommendations for ProxySQL + StarRocks
1. Prefer `SET GLOBAL`: To avoid multiplexing issues, set your desired timeout globally in StarRocks.
2. Verify ProxySQL version: Use ProxySQL 2.0.x or 3.x. Version 3.0.8+ introduced improved session-state tracking (mysql-session_track_variables) which is much more reliable at catching session changes.
3. Check `mysql_query_rules`: If you must use different timeouts for different workloads, use ProxySQL Query Rules to route long-running queries to a specific "Long Query" hostgroup where you have set a specific mysql-default_query_timeout in ProxySQL.
Summary Checklist
Issue | Symptom | Fix
ProxySQL Timeout | Query dies at a fixed time regardless of session setting. | Update mysql-default_query_timeout in ProxySQL.
Multiplexing | Long queries work sometimes, fail others. | Use SET GLOBAL query_timeout in StarRocks.
Old ProxySQL | Session variables are simply ignored. | Upgrade ProxySQL or use SET GLOBAL.
References
• administration/management/resource_management/query_queues.md
• sql-reference/System_variable.mdFrancisco Silva
07/10/2026, 4:12 PMFrancisco Silva
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMquery_timeout and ProxySQL, let me know if you have more questions on that or anything else!Rocky
07/10/2026, 4:17 PMSELECT, INSERT, UPDATE, DELETE, SHOW, SET, etc.
* Best use case: This is the primary metric for calculating QPS (Queries Per Second). If you want to know how many actual business requests or commands are being executed, use this.
2. starrocks_fe_request_total
* Definition: This metric counts the total number of MySQL protocol requests received by the FE.
* What it includes: In addition to all the SQL queries mentioned above, it includes protocol-level commands that are not "queries" in the SQL sense. This includes:
* Connection pings/heartbeats (e.g., from a connection pool checking if a connection is alive).
* USE database commands (COM_INIT_DB).
* Metadata requests from drivers (e.g., getting a list of columns or field descriptions).
* Authentication and connection termination requests.
* Best use case: Monitoring the total network/protocol load on the FE.
Summary of Differences
Metric | Scope | Relationship | Typical Use
starrocks_fe_query_total | SQL Commands | query_total is a subset of request_total. | Calculating QPS / Business throughput.
starrocks_fe_request_total | All Protocol Packets | Usually higher than query_total. | FE network load / Connection pool activity.
Note: If you see starrocks_fe_request_total rising significantly while starrocks_fe_query_total stays flat, it often indicates that a connection pool (like HikariCP or ProxySQL) is sending frequent "pings" or heartbeats to keep connections alive, or that a client is frequently switching databases with the USE command.
References
• administration/management/monitoring/metrics.md
• administration/management/monitoring/Metric_items.md
• administration/management/resource_management/resource_group.md
• administration/management/resource_management/Query_management.md
• administration/management/monitoring/Prometheus_Grafana.md
• administration/management/FE_configuration.md
• administration/management/BE_configuration.md
• administration/management/FE_configuration.md
• administration/management/monitoring/metrics.md
• sql-reference/System_variable.md
• administration/management/resource_management/query_queues.md
• administration/management/resource_management/Query_management.md
• faq/operation_maintenance_faq.md
• administration/management/FE_configuration.md
• administration/management/FE_configuration.mdRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMRocky
07/10/2026, 4:17 PMFrancisco Silva
07/10/2026, 4:21 PMRocky
07/10/2026, 4:21 PMtestOnBorrow=true and a very high concurrency, but the application isn't actually running queries, the pool may be spamming COM_PING (a protocol-level heartbeat).
* Why it shows up this way: A MySQL "Ping" increments starrocks_fe_request_total because it is a valid protocol request, but it does not increment starrocks_fe_query_total because no SQL string was parsed or executed.
* What to check: Look at your app's idleTimeout and maxLifetime. If connections are expiring every few seconds, the pool will constantly hammer the FE with login/logout/ping requests to refill the pool.
2. Rapid Connection "Churn" (Handshake Storm)
If your application is not using a persistent pool and instead creates a "new connection per request":
* The Issue: A single MySQL login involves multiple protocol packets (Handshake Initialization, SSL Request, Auth Response, etc.).
* The Math: If your app logic is looping or retrying, it might be attempting thousands of logins per second. Even if the login fails (e.g., wrong password or blocked IP), each attempt counts toward request_total.
* What to check: Check your fe.log for a flood of Connection refused or Access denied messages.
3. Driver "Metadata Discovery"
Some ORMs (like Hibernate or Prisma) and BI tools perform a "Schema Discovery" when they first start up.
* The Issue: They send thousands of COM_FIELD_LIST or COM_TABLE_STATISTICS requests to understand your table structures.
* Why it shows up this way: These are internal MySQL protocol commands. They are not SQL strings like SELECT * FROM..., so they hit the Request metric but bypass the Query metric.
How to Diagnose and Fix
1. Check the Audit Log:
Open the FE audit log (fe/log/fe.audit.log).
* If the audit log is empty or very slow, the 50k requests are definitely Pings or Failed Auths (which aren't logged as queries).
* If the audit log is full of SET or SHOW commands, then your driver is doing heavy session initialization.
2. Monitor TCP Connections:
Run this on your FE leader:
bash
netstat -n | grep :9030 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr
If you see one specific App IP with thousands of connections, that client is the source of the churn.
3. Adjust ProxySQL (if using it):
If ProxySQL is between your app and StarRocks, check the mysql-ping_interval_server_msec and mysql-ping_timeout_server. ProxySQL itself might be aggressive in pinging the StarRocks backends to ensure they are alive.
Recommendation: Check your application's connection string and pool settings. Ensure you are using a long-lived connection pool and that "test on borrow" is not set to an unnecessarily high frequency.Francisco Silva
07/10/2026, 4:23 PMRocky
07/10/2026, 4:23 PMmysql-ping_interval_server_msec) are for keeping idle connections alive in the pool. However, ProxySQL has a separate Monitor module that checks if StarRocks is healthy.
* Variable to check: mysql-monitor_enabled and mysql-monitor_ping_interval.
* The Issue: If ProxySQL thinks a StarRocks FE is "shunned" or flapping, it may aggressively retry pings or connection tests. Each attempt creates protocol traffic.
2. Connection Handshake "Death Loop"
StarRocks' fe_request_total increments for every protocol packet that constitutes a request (e.g., Auth, Pings, Init DB).
* If your application is failing to authenticate or being rejected (e.g., due to max_user_connections or IP whitelisting), it might be retrying thousands of times a second.
* Why QPS stays low: Since the connection never completes or the SQL is never reached, fe_query_total remains near zero.
* Check: Look at fe.log for Access denied or Too many connections errors.
3. JDBC Driver / Application Pool "Test-on-Borrow"
If your app is "turned on" and has a pool size of, say, 100, but is configured with testOnBorrow=true and a very aggressive `validationQueryTimeout`:
* The pool may be spamming COM_PING requests through ProxySQL to StarRocks.
* ProxySQL multiplexing can sometimes amplify this: if ProxySQL doesn't have an available backend connection, it may trigger a new backend connection attempt for every frontend "borrow" request.
4. ProxySQL "Fast Forward" Mode
If you have configured mysql_query_rules with `fast_forward=1`:
* ProxySQL stops being a "proxy" and becomes a "pass-through" at the protocol level.
* In this mode, ProxySQL doesn't parse the SQL, so it may pass through every single protocol heartbeat from your application's pool directly to StarRocks. If your app pool is aggressive, StarRocks sees it all.
How to confirm the source
Run this on your StarRocks FE node to see exactly what those 50k requests are:
1. Identify the IP: Use netstat -anp | grep :9030 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c to see if the traffic is coming from ProxySQL or directly from the App.
2. Inspect the Packets: If you have access, a quick tcpdump can show if the packets are Login Request, COM_PING, or `COM_QUIT`:
bash
tcpdump -i any port 9030 -X -s 0 -c 100
(Look for text like select, show, or just hex pings).
Recommendation:
Increase mysql-ping_interval_server_msec to 30000 (30s) and check your application pool's Idle Connection Validation settings. If the spike happens exactly when the app starts, it is almost certainly a Connection Storm caused by the app pool trying to initialize all connections simultaneously and failing or over/retrying.Francisco Silva
07/15/2026, 6:25 PMRocky
07/15/2026, 6:25 PMscanner_thread_pool_thread_num is located in the BE Configuration section of the StarRocks documentation.
Details for scanner_thread_pool_thread_num
* Description: This parameter specifies the number of threads in the BE's scanner thread pool used for concurrent data scanning from storage volumes.
* Default Value: 48
* Type: Int
* Is mutable: Yes (It is a dynamic parameter; you can change it without restarting the BE).
* Module: Query Engine
How to modify it
Since it is a dynamic BE configuration, you can modify it using the following SQL syntax:
sql
-- Modify on a specific BE
ALTER SYSTEM MODIFY CONFIG ("scanner_thread_pool_thread_num" = "64");
Context & Usage
This parameter is often tuned when you observe high CPU idle time despite having a large amount of data to scan, or conversely, if you are seeing too much thread contention during heavy scan-intensive queries.
In newer versions (v3.x+), if you are using the Pipeline Execution Engine, you might also want to look at pipeline_connector_scan_thread_num_per_cpu, which handles scanning for external tables (Data Lake).
Reference Link: StarRocks BE Configuration Parameters
References
• administration/management/BE_configuration.mdFrancisco Silva
07/17/2026, 3:49 PMRocky
07/17/2026, 3:49 PMrun_mode = shared_data), it is designed to use CN nodes only. In this mode, adding BE nodes is explicitly not recommended.
How to Add Both
You can add them using different SQL commands:
* To add a BE: ALTER SYSTEM ADD BACKEND "IP:PORT";
* To add a CN: ALTER SYSTEM ADD COMPUTE NODE "IP:PORT";
To ensure queries actually utilize the CN nodes, you often need to adjust session variables:
sql
-- Allow the cluster to prefer CN nodes for compute tasks
SET GLOBAL prefer_compute_node = true;
SET GLOBAL use_compute_nodes = -1; -- Use all available CNs
Summary Recommendation
* Use BEs only if your workload is primarily internal StarRocks tables and you want the best performance via local storage.
* Mix BEs and CNs if you have a "Hybrid" workload: use BEs for your high-performance internal OLAP tables and CNs to scale compute for your Data Lake (S3/HDFS) queries.
* Use CNs only if you are moving to a full Shared-Data architecture (Cloud Native), where all internal data is stored in object storage.
Warning from Docs: Adding CN nodes to shared-nothing clusters is not recommended for core internal storage operations and may lead to "unknown behaviors" if not managed carefully via Resource Groups.
References
• introduction/Architecture.md
• deployment/deploy_manually.md
• quick_start/shared-data.mdFrancisco Silva
07/17/2026, 3:56 PMRocky
07/17/2026, 3:56 PMbi_user go to rg1.
* Query Type: e.g., only SELECT statements or only INSERT statements.
* Source IP: e.g., traffic coming from a specific application server.
* Database: e.g., any query hitting the financial_db.
──────────
Core Parameters
When you create a resource group, you define its "budget" using these primary metrics:
* cpu_weight (Soft Limit): A relative weight for CPU scheduling. If the cluster is under-utilized, a group can exceed this; if the cluster is busy, resources are allocated proportionally to the weights.
* exclusive_cpu_cores (Hard Limit): (Available from v3.3.5+) Reserves a specific number of CPU cores exclusively for that group.
* `mem_limit`: The percentage of BE node memory available to this group (e.g., 20%).
* `concurrency_limit`: Limits the number of concurrent queries allowed in the group to prevent overloading.
* `big_query_cpu_second_limit`: Automatically kills queries that exceed a specific CPU time, protecting the cluster from "queries from hell."
──────────
System Groups
Every StarRocks cluster comes with two built-in groups:
1. `default_wg`: The "catch-all" group for any query that doesn't match a custom classifier.
2. `default_mv_wg`: Specifically for background asynchronous Materialized View (MV) refreshes.
Example Configuration
To create a group for your Data Analytics team that is limited to 30% memory and has a lower CPU priority:
sql
CREATE RESOURCE GROUP analytics_rg
TO (user='alice', role='analyst')
WITH (
"cpu_weight" = "10",
"mem_limit" = "30%",
"concurrency_limit" = "10"
);
Requirements
* Pipeline Engine: Resource groups require the Pipeline Execution Engine to be enabled (SET GLOBAL enable_pipeline_engine = true;), which is the default in modern StarRocks versions.
* Node Support: They apply to both BE (Backend) nodes and CN (Compute Nodes) in shared-data architectures.
Reference Link: StarRocks Resource Group Documentation
References
• deployment/deploy_manually.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_COMPUTE_NODES.md
• deployment/upgrade.md
• sql-reference/sql-statements/cluster-management/nodes_processes/ALTER_SYSTEM.md
• sql-reference/System_variable.mdFrancisco Silva
07/17/2026, 3:56 PMRocky
07/17/2026, 3:56 PMuser: Matches the username.
* role: Matches the user's role.
* db: Matches the database name being accessed.
* query_type: Currently supports SELECT and INSERT.
* source_ip: Matches the client's IP address (supports CIDR notation).
* plan_cpu_cost_range: Matches the FE's estimated CPU cost (e.g., (0, 1000]).
* plan_mem_cost_range: Matches the FE's estimated memory cost.
Example Syntax:
sql
CREATE RESOURCE GROUP analytic_team
TO
(user='alice', role='analyst'), -- Classifier 1
(db='reporting_db', query_type in ('select')) -- Classifier 2
WITH (
"cpu_weight" = "10",
"mem_limit" = "20%"
);
2. Manual Assignment (Session Variable)
You can override the automatic classifier by manually specifying a resource group for your current session. This is useful for temporary high-priority tasks or testing.
sql
SET resource_group = 'high_priority_rg';
3. Matching Logic & Fallbacks
* Best Match: If a query matches multiple classifiers, StarRocks uses a "degree of matching" score. Classifiers with more specific conditions (e.g., matching both user AND IP) or more accurate IP ranges score higher.
* Default Group: If a query doesn't match any custom classifiers, it is assigned to the default_wg.
* Background Tasks: Asynchronous Materialized View refreshes are automatically assigned to default_mv_wg.
4. How to Verify
To check which resource group a query actually used, you can:
1. Check Audit Logs: Look at the ResourceGroup column in fe.audit.log.
2. Explain the Query: Run EXPLAIN VERBOSE <your_query>; and look for the Resource Group field in the output.
Reference Link: StarRocks Resource Group - Classifiers
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/nodes_processes/ALTER_SYSTEM.md
• sql-reference/System_variable.md
• deployment/deploy_manually.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_COMPUTE_NODES.md
• deployment/upgrade.md
-upgrade)
• introduction/Architecture.md
-1)
• quick_start/shared-data.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_COMPUTE_NODES.md
• sql-reference/sql-statements/cluster-management/nodes_processes/ALTER_SYSTEM.md
• sql-reference/System_variable.md
• administration/management/BE_configuration.mdRocky
07/17/2026, 3:56 PMFrancisco Silva
07/17/2026, 3:58 PMRocky
07/17/2026, 3:58 PMOlapScanNode (reading the data from disk into memory). A CN node has no local copy of that data and cannot "reach into" a BE's disk.
2. The Compute Phase (BE + CN): Once the BE has scanned the data, the query is often broken into "fragments" (e.g., Shuffles, Joins, Aggregations). These fragments can be scheduled on CN nodes. The BE nodes "push" the scanned data over the network (via an EXCHANGE node) to the CN nodes for processing.
The "Computation Fragment Scheduling Policy"
StarRocks (starting around v3.2.7) introduced a session variable that controls this behavior:
* SET computation_fragment_scheduling_policy = 'all_nodes';
* This tells the FE (Frontend) that it is allowed to schedule non-scan fragments on both BE and CN nodes.
* If set to compute_nodes_only (the default in some scenarios), the system will try to offload as much non-scan work as possible to the CNs.
Why use CNs for internal data?
If you are already using BEs, why add CNs?
* Heavy Compute: If your queries involve massive joins or complex window functions that are CPU-bound, CN nodes can provide "extra brains" to process the data after the BEs have finished the IO-heavy scanning.
* Workload Isolation: You can use Resource Groups to force specific "heavy" users to use only CN nodes for their computation, ensuring they don't starve the BEs' CPU and memory which are needed for real-time data ingestion (Stream Loads).
Summary Table
Task | BE Node | CN Node
Scan Internal Data (Disk) | ✅ Yes | ❌ No
Scan External Data (S3/HDFS) | ✅ Yes | ✅ Yes
Perform Join/Agg (Shuffle) | ✅ Yes | ✅ Yes (Data sent from BE)
Data Ingestion (Storage) | ✅ Yes | ❌ No
The Bottom Line: You aren't restricted to "different data only." A CN node can process data from your internal BE-hosted databases, but it relies on the BEs to act as the "data source" that reads the blocks from disk and sends them over the network to the CN.
References
• introduction/Architecture.md
• sql-reference/System_variable.md
• deployment/deploy_manually.md
• table_design/table_design.md
• using_starrocks/Colocate_join.md
• quick_start/shared-data.md
References
• table_design/table_design.md
• introduction/Architecture.mdFrancisco Silva
07/21/2026, 11:07 AMRocky
07/21/2026, 11:07 AMSHOW PROC '/frontends'; and look at the Role column.
* Common Leader-only CPU consumers:
* Tablet Report Processing: The Leader receives reports from every BE node about every tablet (data shard). In large clusters, diffing this with metadata is CPU-intensive.
* Metadata Checkpointing: The Leader (by default in older versions) creates and saves the metadata image.
* Catalog Metadata Sync: If using external catalogs (Hive, Iceberg), the Leader is responsible for periodic metadata refreshes.
* Routine Load Management: Scheduling and monitoring Routine Loads (Kafka) happens on the Leader.
2. Compare Query Complexity (not just QPS)
Load balancers (like F5 or Nginx) usually distribute connections or requests equally, but they don't know the cost of a query. One node might be handling 10 "small" queries while another handles 10 "massive" queries.
* How to check: Analyze the fe.audit.log on that specific node.
* Look for:
bash
# Find queries with high CPU cost on this node
grep "QueryId" fe.audit.log | awk -F'|' '{print $1, $6}' | sort -nk2 | tail -n 20
* Check the CpuCostNs field. If this node happens to be receiving queries with more complex execution plans (many joins, large metadata lookups), its CPU will spike.
3. Investigate JVM and Garbage Collection (GC)
High CPU on an FE (which is a Java process) is frequently caused by GC pressure. If one node's JVM heap is nearly full, it will spend more time running "Stop-the-World" or concurrent GC cycles, which drives CPU usage to 100%.
* How to check: Look at fe.out for GC logs or use jstat -gcutil <pid> 1000.
* Symptoms: If FGC (Full GC count) is increasing or GCT (total GC time) is high, the CPU usage is likely spent on memory management, not SQL processing.
4. Identify the hot threads
To see exactly what the CPU is doing at this moment, take a thread dump while the usage is at 100%.
* Command: jstack -l <pid> > high_cpu_jstack.txt
* Search for: RUNNABLE threads.
* If you see many threads in com.starrocks.sql.optimizer, the FE is struggling with SQL Optimization/Planning.
* If you see com.starrocks.catalog, it is likely busy with Metadata/Tablet management.
* If you see org.apache.hadoop, it may be hanging on External Catalog (HDFS/S3) calls.
Summary Troubleshooting Checklist
Step | Action | Command
1 | Verify Role | SHOW PROC '/frontends';
2 | Check GC | jstat -gcutil <pid> 2000 10
3 | Profile Queries | Check CpuCostNs in fe.audit.log
4 | Inspect Threads | top -Hp <pid> then jstack <pid>
Pro Tip: If you are on v3.4.0+, you can offload checkpointing from the Leader by setting checkpoint_only_on_leader = false in fe.conf to balance the load more evenly across Followers.
References
• administration/management/FE_configuration.md
• knowledge/trouble_shooting/fe_memory_problems.md
• administration/management/logs.md
• deployment/deploy_manually.md
• faq/operation_maintenance_faq.md
• knowledge/trouble_shooting/fe_deadlock_problems.mdRocky
07/21/2026, 11:07 AMFrancisco Silva
07/24/2026, 11:17 AMRocky
07/24/2026, 11:17 AMPREPARE, COMMITTED, VISIBLE).
* Even if you send your write request to a Follower FE, that Follower must communicate with the Leader to "Begin" the transaction and eventually "Commit" it.
2. Metadata Authority (The Source of Truth)
StarRocks stores its metadata (table schemas, tablet locations, and version numbers) in a distributed log called BDBJE (Berkeley DB Java Edition).
* Single Writer: Only the Leader FE is permitted to write to this log.
* Version Tracking: When a write finishes, the "version" of the affected data tablets must be incremented (e.g., from version 10 to 11). Since this version change is a metadata update, only the Leader can perform it.
3. The "Publish" Phase (Data Visibility)
A write isn't "done" just because the data is on the BE disks. It only becomes visible to queries once the Leader FE completes the Publish Version task.
* After the BEs report that they have successfully written the data replicas, the Leader FE updates the global metadata to reflect the new version.
* Once the Leader records this in the edit log and synchronizes it to a majority of Followers, it sends an "Ack" back to the client. Only then is the data searchable.
4. Avoiding "Split Brain"
If every FE node could independently decide that a write was successful and increment version numbers, the cluster's metadata would quickly diverge (a "Split Brain" scenario). Centralizing the final acknowledgment on the Leader ensures that every node in the cluster sees exactly the same state of data at the same time.
Summary: Control Path vs. Data Path
* Data Path (BEs): Large volumes of data flow from your client/Kafka to the BE nodes. The Leader FE is not involved in moving these bytes.
* Control Path (Leader FE): The "handshake" (Begin Transaction) and the "signature" (Commit/Publish) must go through the Leader to ensure the write is persistent and consistent.
References
• introduction/Architecture.md
• loading/Stream_Load_transaction_interface.md
• administration/management/monitoring/metrics.md
• loading/StreamLoad.md
• faq/operation_maintenance_faq.md
• administration/management/FE_configuration.md
• administration/management/compaction.md
References
• loading/StreamLoad.md
• administration/management/FE_configuration.md
• loading/Stream_Load_transaction_interface.md