RonJay
04/05/2026, 6:41 AMRocky
04/05/2026, 6:42 AMreplicas in the CN Deployment/StatefulSet.
2. Pod Creation: Kubernetes creates 3 new CN pods.
3. Operator Observation: The StarRocks Operator (specifically the starrocks-controller) watches for Pod events in the namespace. It notices new pods belonging to the StarRocksCluster resource.
4. Health Check: The Operator waits until the new CN pods are in a Ready state (meaning the CN process has started and passed its readiness probe).
5. SQL Registration: Once ready, the Operator connects to the Leader FE (via the FE service) and automatically executes the following SQL command for each new pod:
sql
ALTER SYSTEM ADD COMPUTE NODE "cn_pod_ip:9050";
6. FE Heartbeat: The FE then adds these IPs to its internal memory and starts sending heartbeats to the new CNs. Once the heartbeat is successful, the FE includes these 3 new CNs in the query plan for subsequent incoming queries.
3. Example Configuration (HPA for CN)
To enable this behavior, you define an autoScalingPolicy in your StarRocksCluster YAML. The Operator uses this to create the HPA resource for you.
Example Snippet:
yaml
apiVersion: <http://starrocks.com/v17|starrocks.com/v17>
kind: StarRocksCluster
metadata:
name: starrockscluster-sample
spec:
starRocksCnSpec:
image: starrocks/cn-ubuntu:latest
requests:
cpu: 4
memory: 16Gi
autoScalingPolicy:
minReplicas: 3
maxReplicas: 10
hpaPolicy:
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
4. How to Verify the Detection
You can check if the FE has successfully detected and registered the new CN pods by running this SQL command in your MySQL client:
sql
-- This shows all registered compute nodes and their status
SHOW PROC '/compute_nodes';
-- OR
SHOW COMPUTE NODES;
What happens during Scale-In?
When the load drops and HPA scales the CNs back down from 6 to 3:
1. The Operator detects the deletion of pods.
2. It executes ALTER SYSTEM DROP COMPUTE NODE "cn_pod_ip:9050"; on the FE.
3. The FE removes them from the query plan to avoid "node not found" errors.
Summary of Logic
Component | Role
HPA | Decides when to scale based on metrics (e.g., 70% memory).
K8s Controller | Spins up or shuts down the actual Pod containers.
StarRocks Operator | Monitors the Pods and runs ALTER SYSTEM SQL to register/unregister them in the FE.
FE Pod | Maintains the node list and dispatches query fragments to active CNs.
If you are not using the StarRocks Operator (e.g., manual K8s manifests), you would have to manually run the ALTER SYSTEM commands every time a pod's IP changes or when you scale out. This is why using the Operator is highly recommended for CN-recommended for HPA.P recommended for HPA scenarios!
References
• deployment/sr_operator.mdRonJay
04/05/2026, 6:47 AMRocky
04/05/2026, 6:48 AMADD logic)
As you found, the CN Pod handles its own registration.
* The Script: When a CN pod starts, it executes /opt/starrocks/cn_entrypoint.sh.
* The Logic: This script uses a function (often called add_self) that waits for the FE service to be reachable. Once reachable, it logs into the FE via the MySQL protocol (using the FE_SERVICE_NAME or FE_IP) and runs:
sql
ALTER SYSTEM ADD COMPUTE NODE "current_pod_ip:9050";
* Why this way? This allows the CN to register itself the moment its internal process is actually ready to receive traffic, rather than the Operator guessing based on the Pod's Ready state.
2. Operator Reconciliation (The SyncComputeNodesInFE logic)
The StarRocks Operator's SyncComputeNodesInFE function acts as a "garbage collector" or "reconciler." Its logic works like this:
1. Fetch FE Nodes: It queries the FE for the current list of registered Compute Nodes (equivalent to SHOW COMPUTE NODES).
2. Fetch K8s Pods: It gets the list of actual CN Pods currently running in the Kubernetes cluster.
3. The Reconcile/DROP Step:
* If a node is in the FE list but NOT in the K8s Pod list, the Operator assumes the pod has been scaled down or deleted.
* It then executes: ALTER SYSTEM DROP COMPUTE NODE "missing_pod_ip:9050";.
3. Addressing the Race Condition
You asked: Could the Operator drop a newly registered CN before it's recognized in the replica count?
No, because of the reconciliation direction.
The Operator does not drop nodes that are in the "transition" state of being created. Here is why:
* The "Check" is K8s-First: The Operator's logic is: "Is this node in the FE still alive in K8s?"
* If a new Pod (Pod-4) is created by HPA, it immediately appears in the K8s Pod list.
* When the Operator runs SyncComputeNodesInFE, it sees Pod-4 in K8s. Even if Pod-4 hasn't registered itself in the FE yet, the Operator will not issue a DROP command for it because Pod-4 is a valid, existing resource in Kubernetes.
* The Operator only issues a DROP for IPs that are in the FE but missing from the current K8s namespace.
Summary Flow (HPA Scale-out 3 -> 6)
1. HPA increases replicas to 6.
2. K8s starts 3 new Pods (CN-4, CN-5, CN-6).
3. CN Pods start up and run cn_entrypoint.sh. They execute ADD COMPUTE NODE.
4. Operator runs its sync loop. It sees 6 Pods in K8s and 6 Nodes in the FE (or 3 in FE and 3 in K8s pending registration). Since all 6 Pods exist in K8s, it does nothing.
5. Queries begin hitting all 6 CNs.
Summary Flow (HPA Scale-in 6 -> 3)
1. HPA decreases replicas to 3.
2. K8s terminates Pods (CN-4, CN-5, CN-6).
3. Operator runs its sync loop. It sees 6 Nodes registered in the FE but only 3 Pods in K8s.
4. Operator executes DROP COMPUTE NODE for the 3 missing IPs to clean up the FE's metadata.
This "Self-Add, Operator-Drop" pattern is a common design in Kubernetes operators to ensure that the application (StarRocks) is responsible for its own readiness while the Operator handles the infrastructure-infrastructure level cleanup.
Document Reference for Logic
While the source code is the ultimate truth, you can find the high-level description of this behavior in the StarRocks Operator Architecture Documentation and the CN HPA Scaling Guide.
ReferencesRocky
04/05/2026, 6:48 AMRonJay
04/05/2026, 6:54 AMRocky
04/05/2026, 6:54 AMkill query if planning uses 2GB) for FE memory per query in current versions. However, you have two powerful levers to prevent the FE from OOMing:
* Switch to Distributed Planning (Crucial):
By default, the FE parses Iceberg manifest files locally. For tables with 100k+ files, this is what kills your FE. You should change the `plan_mode`:
sql
-- Set globally or for the specific heavy user's session
SET GLOBAL plan_mode = 'distributed';
Logic: In distributed mode, the FE only fetches the Iceberg "Snapshot." It then dispatches the task of reading and filtering manifest files/data files to the CN nodes. The CNs process the metadata in parallel and return only the filtered file list to the FE. This offloads the massive memory pressure from the FE to the 6 CNs.
* Cap Iceberg Metadata Cache:
The FE caches Iceberg metadata in memory by default. If you have many large tables, this cache grows until OOM.
sql
-- Default is 512MB. If your FE has 32GB, you might want to cap this
-- to ensure there is room for the actual planning objects.
-- Edit fe.conf:
iceberg_metadata_memory_cache_capacity = 2147483648 -- (e.g., 2GB)
* Mandatory -Xmx Setting:
You mentioned no explicit -Xmx is set. In a 32GB container, the JVM might try to claim more than the container's cgroup limit if not configured, leading to an OOMKill by the OS. You must set this in fe.conf:
bash
# Set to ~75-80% of container memory
JAVA_OPTS="-Xmx24g -Xms24g"
──────────
2. Query Admission Control and Resource Groups
Does concurrency_limit apply at the FE planning stage?
Yes.
* The Logic: Starting from StarRocks v3.1.4, the Leader FE manages a centralized Query Queue. When a query arrives:
1. The FE identifies the user's Resource Group.
2. It checks the concurrency_limit for that group.
3. If the limit is reached, the query is queued or rejected before it proceeds to the heavy Cost-Based Optimizer (CBO) and Distributed Planning phases.
* Recommendation: Create a specific Resource Group for this heavy user with a low concurrency_limit (e.g., 1 or 2). This prevents them from firing multiple heavy Iceberg scans simultaneously that would collectively crush the FE heap.
sql
CREATE RESOURCE GROUP heavy_iceberg_user_group
PROPERTIES (
"concurrency_limit" = "1",
"user" = "heavy_user_name"
);
──────────
3. FE HA and Leader Takeover
Is FE HA the recommended mitigation?
It is recommended for Availability, but it is not a fix for OOM queries.
* The Problem: If a "poison pill" query (one that requires 40GB of metadata) OOMs the Leader FE, the cluster will failover. However, when the user or their BI tool automatically retries that same query against the new Leader, the new Leader will also OOM. You will end up in a "death spiral" where FEs crash one after another.
* Takeover Time:
* StarRocks uses Berkeley DB (BDBJE) for metadata replication and leader election.
* The timeout is controlled by bdbje_heartbeat_timeout_second (default 30 seconds).
* Typically, it takes 30 to 45 seconds for a Follower to realize the Leader is gone, elect a new Leader, and replay the last few edit logs to become functional.
Rocky’s Final Recommendations:Rocky
04/05/2026, 6:54 AMJAVA_OPTS="-Xmx24g -Xms24g" in fe.conf.
2. Architectural Fix: Use SET GLOBAL plan_mode = 'distributed'; to move Iceberg metadata processing to the CNs.
3. Governance Fix: Put that user in a Resource Group with concurrency_limit = 1.
4. Availability: Deploy 3 FEs (1 Leader + 2 Followers) to ensure the cluster doesn't go down completely when one node fails.
Does this help clarify the "planning vs execution" boundary" for your cluster?
References
• administration/management/FE_configuration.md
• administration/Meta_recovery.mdRonJay
04/05/2026, 7:02 AMRocky
04/05/2026, 7:03 AMauto vs. distributed
In StarRocks 4.0.x, plan_mode determines where the manifest files of an Iceberg table are parsed.
* auto (Default): The FE looks at the table's statistics and the number of manifest files. If the metadata is "small" (based on internal thresholds), it parses it locally in the FE's JVM to save network overhead. If the metadata is "large" or the number of files exceeds a threshold, it should automatically switch to distributed.
* Why auto might fail for you: If your table has tens of thousands of partitions but the manifest files themselves aren't individually "huge," the FE might still try to parse them locally. In your case, the cumulative memory of many small manifest files is likely what hits the 32GB heap limit.
* `distributed`: This forces the FE to send the "Manifest Scan" task to the 6 CN nodes. The CNs read and filter the manifests in parallel and only return the final list of data files to the FE. For your scenario, distributed is the only safe setting.
──────────
2. Making plan_mode Persistent
You are correct that plan_mode is a System Variable, not a static fe.conf parameter. This is why ADMIN SHOW FRONTEND CONFIG does not show it.
* The "Reset" Issue: If SET GLOBAL plan_mode = 'distributed'; resets to auto after a restart, it usually means your deployment (likely Kubernetes) has an initialization script or a Sidecar that runs a "reset to defaults" SQL command, or the FE's metadata volume is not persistent (unlikely for a working cluster).
* Persistence Solution (Version 4.0+): You can bind this variable to the specific "heavy user" so it is always applied whenever they connect, regardless of global settings:
sql
-- This persists in the FE metadata and survives restarts
ALTER USER 'heavy_user_name' SET DEFAULT ROLE ALL; -- Ensure roles are correct
ALTER USER 'heavy_user_name' SET ('plan_mode' = 'distributed');
* Check Persistence: Run SHOW GLOBAL VARIABLES LIKE 'plan_mode'; after a restart to verify if the global setting was truly lost.
──────────
3. iceberg_metadata_memory_cache_capacity = 0
In StarRocks 4.0.x, a value of 0 means Unlimited (No Cap).
By default, the FE will try to cache every Iceberg Table object and its metadata in the heap. If you have thousands of files and snapshots, this cache grows until the JVM hits the cgroup limit and OOMs.
* Recommendation for 32GB FE:
I recommend capping this at 2GB to 4GB (2,147,483,648 to 4,294,967,296 bytes). This leaves plenty of room for the "working memory" needed during actual query planning.
In `fe.conf`:
properties
iceberg_metadata_memory_cache_capacity = 2147483648
Note: If you have a high volume of unique Iceberg tables, setting this too low will cause "cache thrashing" (slow planning), but it will save your FE from OOM.
──────────
4. Additional FE Protection Parameters for 4.0.4
There are three specific parameters in 4.0.4 that act as "guardrails" for the FE planner:
1. parse_tokens_limit (FE Config):
This limits how complex a single SQL statement can be (to prevent deep recursion OOMs).
properties
# Default is usually 10,000,000.
# If the user sends a query with 50,000 items in an IN clause, lower this.
parse_tokens_limit = 3500000
2. max_partitions_per_query (System Variable):
This is the "nuclear option." If a user tries to query more than X partitions in one go, the FE will reject the query before it even starts heavy planning.
```sql
SET GLOBAL max_partitions_per_query = 5000;Rocky
04/05/2026, 7:03 AM3. *`enable_iceberg_metadata_cache` (System Variable):*
If the FE continues to OOM even with a capacity cap, you can disable the cache entirely for the heavy user's session:
```sql
SET GLOBAL enable_iceberg_metadata_cache = false;
This forces the FE/CN to fetch metadata from S3 every time, which is slower but uses significantly less persistent heap memory.
Summary Checklist for v4.0.4:
1. FE Heap: Set JAVA_OPTS="-Xmx24g -Xms24g" (leaving 8GB for the OS/Container).
2. Plan Mode: Use SET GLOBAL plan_mode = 'distributed'; and verify persistence.
3. Metadata Cap: Set iceberg_metadata_memory_cache_capacity = 2147483648 in fe.conf.
4. Governance: Use Resource Groups with concurrency_limit = 1 for that specific user.
Does this help you lock down that "poison pill" user?
References
• data_source/catalog/iceberg/iceberg_catalog.md
• sql-reference/System_variable.md
• best_practices/query_tuning/query_planning.mdRonJay
04/05/2026, 7:40 AMRocky
04/05/2026, 7:41 AMiceberg_metadata_memory_cache_capacity acts as a "parent" limit for an LRU cache that stores several types of Iceberg-related JVM objects:
* Table Objects (org.apache.iceberg.Table): The core entry point for a table, including its schema and current state.
* Snapshots (BaseSnapshot): Metadata about a specific point-in-time of the table. This is often the biggest memory hog because each snapshot references a "Manifest List."
* Manifest Lists and Manifest Files: These are the most critical. When the FE parses an Avro manifest file to find data files, it converts the Avro records into Java DataFile objects.
* Column Statistics: By default, StarRocks caches the min/max values and null counts for columns within those DataFile objects. This allows the Optimizer to perform "Data Pruning" (skipping files) without re-reading S3.
2. Sizing Formula & Rule of Thumb
The memory consumption is primarily driven by the number of Data Files across all active snapshots in the cache.
* The Rule of Thumb: In the JVM heap, a deserialized DataFile object with typical statistics consumes between 1 KB and 2 KB of memory.
* Your Scenario Calculation:
* 100 tables × 100,000 data files per table = 10,000,000 data files.
* 10M files × 1.5 KB (average) = 15 GB of Heap Memory.
* The Verdict: If you allow the cache to be "unlimited" (0), your FE will attempt to hold all 15GB in its 24GB heap. Once you add the JVM's own overhead, the StarRocks FE internal metadata (tablets/schemas), and the memory needed during query planning (CBO), you easily cross the 24GB -Xmx threshold, triggering the OOM.
3. Recommended Ratio to JVM Heap
For an FE with a 24GB heap (-Xmx24g), the goal is to prevent the metadata cache from "crowding out" the Query Optimizer.
* Recommended Ratio: 10% to 15% of the total heap.
* Recommended Value: For your 24GB heap, setting iceberg_metadata_memory_cache_capacity to 2 GB to 3 GB (2,147,483,648 to 3,221,225,472 bytes) is a very safe starting point.
* Why this ratio? The FE needs the remaining ~20GB for:
1. CBO Optimizer: Heavy queries on 10,000 partitions require significant temporary memory to build the "Plan Tree."
2. Internal Metadata: StarRocks' own internal table metadata.
3. Connection Overhead: Each MySQL connection/session uses memory.
4. Garbage Collection Room: The JVM needs "breathing room" to avoid constant Full GCs.
4. What happens if you set it "too low"?
If you set the cache to 2GB but your queries actually need 15GB of metadata:
* Safety: Your FE will not OOM. The LRU will evict older table metadata to make room for new ones.
* Performance: Queries for "cold" tables will be slower because the FE will have to re-fetch and re-parse manifest files from S3.
* Mitigation: This is exactly why I previously recommended plan_mode = 'distributed'. When you use distributed planning, the CN nodes handle the manifest parsing. The FE only needs to hold the final filtered file list, which drastically reduces the memory pressure on the FE cache.
Summary Checklist for your 4.0.6 FE:
1. -Xmx: 24GB (Confirmed).
2. iceberg_metadata_memory_cache_capacity: 2147483648 (2GB).
3. Refinement: If you notice query planning is slow for frequently accessed tables, check the FE starrocks_fe.warn.log. If you see frequent "Cache Eviction" messages, consider bumping the cache to 4GB, but do not exceed 25% of your heap.Rocky
04/05/2026, 7:41 AMRonJay
04/05/2026, 7:59 AMRocky
04/05/2026, 7:59 AMmaxReplicas immediately, then never scale back down.
* Recommendation:
* If you use Memory for HPA, set the threshold to 75-80%.
* However, for shared-data workloads, Memory is a poor indicator of load. CPU is much more accurate.
2. Metric Strategy: CPU-only, Memory-only, or Both?
For Iceberg workloads (which involve heavy Parquet decompression and distributed manifest parsing), CPU is the primary bottleneck and the best metric for HPA.
* CPU-only (Recommended): Use a threshold of 60-70%. This ensures that as the "Distributed Planning" and "Scan" tasks increase, the cluster adds more compute power.
* Why not both? If you use both, the HPA will scale up if either metric is hit. Since the Data Cache will eventually push memory usage high, the memory metric often triggers "false positive" scale-ups.
* Refinement: Use CPU as your scaling trigger and treat Memory limits (-Xmx and query_mem_limit) as your per-node protection.
3. Safe Scale-Down and Stabilization
To prevent killing CNs mid-query and to avoid "flapping" (scaling up and down rapidly), you should configure the behavior section of your HPA.
A. The "Graceful Exit" Mechanism (v4.0.x)
In version 4.0.6, when the Operator scales down a CN, it sends a SIGTERM. The CN then:
1. Enters an "Exiting" state.
2. Tells the FE to stop sending it new query fragments.
3. Continues to process existing fragments for a period defined by loop_count_wait_fragments_finish (default 20s).
4. Only shuts down once all fragments are done or the timeout is reached.
B. Recommended HPA Behavior Policy
To make this production-ready, use a Stabilization Window. This forces the HPA to "wait and see" if the load stays low before removing a node.
yaml
autoScalingPolicy:
maxReplicas: 10
minReplicas: 3
hpaPolicy:
metrics:
- type: Resource
resource:
name: cpu
target:
averageUtilization: 70
type: Utilization
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # Scale up immediately when busy
policies:
- type: Percent
value: 100 # Allow doubling the cluster size if needed
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300 # Wait 5 minutes of low load before scaling down
policies:
- type: Pods
value: 1 # Remove only 1 node at a time
periodSeconds: 60
4. Critical "Alignment" Checklist
To ensure no queries fail during scale-down, you must align three timeouts:
1. StarRocks Graceful Exit: In cn.conf, ensure loop_count_wait_fragments_finish is set (default is 2, which is 20s). For heavy Iceberg queries, you might bump this to 6 (60s).
2. Kubernetes Termination Grace Period: In your StarRocksCluster YAML under starRocksCnSpec, set terminationGracePeriodSeconds to be longer than the StarRocks exit timeout.
yaml
starRocksCnSpec:
terminationGracePeriodSeconds: 90 # Give the CN 90s to finish queriesRocky
04/05/2026, 7:59 AMDECOMMISSION node.
Rocky’s Summary Recommendation:
* Metric: Use CPU @ 70% as the only scaling metric.
* Scale-Down: Set a stabilizationWindowSeconds of 300 (5 minutes).
* Graceful Shutdown: Set terminationGracePeriodSeconds: 90 in your Pod spec to protect those "hundreds of thousands of files" Iceberg scans that might take a minute to finish.
Does this give you a safer path for your CN auto-scaling?
References
• administration/management/graceful_exit.md
• administration/management/resource_management/Query_management.md