GregM
05/19/2026, 2:19 PMNo route to host, how do i fix thatRocky
05/19/2026, 2:19 PMNo route to host is a networking issue indicating that the FE node trying to send the heartbeat cannot reach the destination IP address. This is usually caused by network configuration changes, firewall rules, or incorrect IP identification.
1. Check for IP Changes
If your nodes are in a dynamic environment (like K8s or a cloud provider without static IPs) and you haven't configured priority_networks, a node might have restarted with a new IP while the cluster metadata still points to the old one.
• Run SHOW FRONTENDS; (or SHOW BACKENDS; if the error is regarding a BE) to see the IP address the cluster is trying to reach.
• Compare this with the actual IP of the target machine (ifconfig or ip addr).
• Fix: Define priority_networks in fe.conf (and be.conf) to ensure nodes bind to the correct network interface/subnet.
properties
# Example: 192.168.1.0/24
priority_networks = 192.168.1.0/24
2. Verify Port Connectivity
The heartbeat might be blocked by a firewall (iptables, firewalld) or a Cloud Security Group.
• FE to FE heartbeat typically uses the edit_log_port (default 9010).
• FE to BE heartbeat uses the heartbeat_service_port (default 9050).
• Test connectivity from the FE machine reporting the error to the target node:
bash
telnet <target_ip> <port>
# OR
nc -zv <target_ip> <port>
If this fails with No route to host, the issue is at the OS/Network routing layer, not within StarRocks itself.
3. Check Routing and Hosts
• Ensure there isn't a stale entry in /etc/hosts if you are using hostnames.
• Check if the target node is actually up and the process is running. If the process is down, you might get Connection refused, but if the entire VM/container is gone or the network path is broken, you get No route to host.
4. Metadata Sync (If IP changed)
If the IP of an FE node actually changed and you need to update the cluster:
1. Start the FE with the new IP.
2. Use ALTER SYSTEM DROP FOLLOWER/OBSERVER "old_ip:edit_log_port";
3. Use ALTER SYSTEM ADD FOLLOWER/OBSERVER "new_ip:edit_log_port"; to re-register it.
If the issue persists, check the fe.log on the leader FE for more specific details on which node it's failing to contact.
References
• faq/operation_maintenance_faq.md
• administration/Meta_recovery.md
• administration/management/BE_configuration.md
• administration/management/FE_configuration.md
• faq/Deploy_faq.mdGregM
05/19/2026, 2:21 PMRocky
05/19/2026, 2:21 PMfe-proxy via Helm typically triggers a rolling restart of your FE (and possibly BE) pods to apply the new configuration. The No route to host error in your heartbeats is a classic symptom of a networking mismatch following such a restart.
1. Pod IP Change (Most Likely)
In a Kubernetes environment, if your FEs are registered in the cluster by their IP addresses (rather than FQDNs), a pod restart will likely assign a new IP to the FE.
* The Symptom: The Leader FE is still trying to send heartbeats to the old IP address that no longer exists in the VPC/Subnet.
* How to verify:
1. Run SHOW FRONTENDS; in your MySQL client. Look at the IP column for the FE that is "down."
2. Check the actual current IP of your FE pods: kubectl get pods -o wide.
* The Fix: If the IPs don't match, the Leader is trying to talk to a "ghost" node.
* If you are using the StarRocks Operator, it should typically handle this. If it hasn't, ensure the Operator is running and healthy.
* If you are managing the cluster manually within K8s, you may need to ALTER SYSTEM DROP FOLLOWER/OBSERVER "<old_ip>:<edit_log_port>"; and then add the new IP.
2. Service/Network Conflict
The fe-proxy (which uses Nginx) introduces a new Service and potentially a LoadBalancer.
* Check `priority_networks`: If you have this set in your fe.conf, ensure the new proxy's CIDR hasn't confused the FE into binding to the wrong network interface. FEs should bind to the internal pod network, not the proxy's network.
* Port Blocking: The fe-proxy handles traffic on the HTTP/MySQL ports (default 8080 or 9030), but heartbeats happen on edit_log_port (9010) and heartbeat_service_port (9050). The proxy should not sit in between these, but if you modified the service definitions in Helm, you might have inadvertently blocked internal traffic.
3. StarRocks Operator version
If you are using an older version of the Helm chart or Operator, there was a known issue where adding the fe-proxy could cause internal DNS resolution issues for FEs if a custom DNS setup was used.
* Check Logs: Run kubectl logs <leader-fe-pod-name> and look for entries related to FrontendService or BDBJE. If you see "UnknownHostException" or "Connection refused" alongside "No route to host," it confirms the Leader cannot find its peers.
Recommended Steps:
1. Compare IPs: Use SHOW FRONTENDS; vs kubectl get pods -o wide.
2. Verify Pod Health: Check if the affected FE pod is actually Running and Ready. If it's in a CrashLoopBackOff, the fe-proxy config might have a typo in the fe.conf template.
3. Connectivity Test: From inside one FE pod, try to ping or telnet the IP of the problematic FE on port 9010. If you get No route to host there, your K8s CNI (network plugin) might be having trouble routing traffic to the new pod.
Summary: The fe-proxy itself doesn't handle heartbeats, but the deployment process of standing it up likely changed your FE's internal IP address, and the cluster metadata hasn't caught up.
References
• quick_start/helm.md
• administration/Meta_recovery.md
• faq/operation_maintenance_faq.md
References
• quick_start/helm.md
• faq/operation_maintenance_faq.md
• administration/management/BE_configuration.md
• administration/Meta_recovery.md
• faq/Deploy_faq.mdGregM
05/19/2026, 2:25 PMRocky
05/19/2026, 2:25 PMTERMINATING, no one is left to finish the job.
The most likely reason for this is Finalizers. StarRocks uses finalizers to ensure that metadata and network state are cleaned up before a pod disappears. If the network or the Operator itself is failing, these finalizers never get removed.
Step 1: Force-Clear the Operator
The Operator needs to be healthy to manage the FEs. If it’s stuck, you usually have to manually remove its finalizer so Kubernetes can kill the old instance and let a new one start (or let your Helm command finish).
1. Find the Operator's name:
kubectl get pods -n <your-namespace> | grep starrocks-operator
2. Patch it to remove the finalizer:
bash
kubectl patch pod <operator-pod-name> -n <your-namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
If it's a Deployment/StatefulSet stuck, you may need to patch the resource itself:
bash
kubectl patch deployment starrocks-operator -n <your-namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
Step 2: Clear the Stuck FE Node
Once the Operator is out of the way (or restarted), the FE pod might still be stuck.
1. Check if the FE pod has a finalizer:
kubectl get pod <fe-pod-name> -o yaml | grep -A 5 "finalizers"
2. Force-delete it:
If the Operator doesn't clean it up automatically after restarting, run:
bash
kubectl delete pod <fe-pod-name> --grace-period=0 --force
3. Patch the FE pod (if force-delete fails):
bash
kubectl patch pod <fe-pod-name> -p '{"metadata":{"finalizers":null}}' --type=merge
Step 3: Fix the "No Route to Host" (Post-Recovery)
Once the pods are gone, Helm or the Operator should recreate them. The No route to host error you originally saw happened because the Cluster Leader was trying to reach an FE pod that was already in a "Terminating" (partially deleted) state.
* Watch the new IPs: When the FE comes back up, run kubectl get pods -o wide.
* Update StarRocks Metadata: If the FE pod gets a new IP and doesn't join the cluster automatically, the Leader might still be looking for the "old" dead IP.
* Connect to the Leader FE via MySQL.
* Run SHOW FRONTENDS;.
* If the IP doesn't match the new Pod IP, you must:
ALTER SYSTEM DROP FOLLOWER "<old_ip>:9010";
ALTER SYSTEM ADD FOLLOWER "<new_ip>:9010";
Why did this happen?
When you enabled fe-proxy, Helm attempted to update the StarRocksCluster CRD. This triggered a rolling restart. If your network/CNI had a hiccup or the Operator was busy, the pods got stuck in the "handshake" phase of termination.
Recommendation: After you get the pods running again, ensure your fe.conf has priority_networks set to your Pod CIDR. This prevents FEs from getting confused about which IP to use for heartbeats when multiple network interfaces (like the proxy's) are present.GregM
05/19/2026, 2:27 PMRocky
05/19/2026, 2:27 PMhelm upgrade (to enable fe-proxy) triggered a rolling update of the Operator itself.
The old Operator is stuck in "Terminating" because it’s likely waiting for a Finalizer to complete a cleanup task, but since the new Operator has already taken over (or the network state is inconsistent), the old one is "zombified."
Why this is happening
1. Rolling Update: Helm replaced the Operator deployment. Kubernetes starts a new pod and then tries to shut down the old one.
2. The "Terminating" Lock: The old Operator pod has a metadata field called a finalizer. Kubernetes won't actually delete the pod from the API server until that field is cleared. If the pod is unresponsive or the CNI (network) isn't letting it talk to the API server, it stays there forever.
3. Heartbeat Link: While that old Operator is stuck, it might still be holding onto "leases" or lock files, and your FEs (which were also likely restarted by the same Helm command) are caught in the middle of a networking change, leading to that No route to host error.
How to Fix It
1. Force-kill the Terminating Operator
Since you have a new one "Running," you can safely clear out the old one.
bash
# Get the name of the TERMINATING pod
kubectl get pods -n <namespace>
# Patch it to remove finalizers (this is the most effective way)
kubectl patch pod <old-operator-pod-name> -n <namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
If it still won't go away, use:
kubectl delete pod <old-operator-pod-name> -n <namespace> --grace-period=0 --force
2. Check the "Terminating" FE Node
If your FE node is also stuck in TERMINATING, follow the same steps:
bash
kubectl patch pod <stuck-fe-pod-name> -n <namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
3. Update the Cluster Metadata (The original error)
Once the pods are gone and the new FEs are Running, check your IPs. The No route to host happened because the StarRocks Leader was trying to reach the old IP of that terminating FE.
1. Check Pod IPs: kubectl get pods -o wide
2. Check StarRocks Metadata:
Log into your StarRocks cluster (via MySQL) and run:
sql
SHOW FRONTENDS;
3. Fix Mismatches: If the IP in SHOW FRONTENDS for the "bad" node is different from the actual Pod IP you see in `kubectl`:
sql
-- Drop the ghost record
ALTER SYSTEM DROP FOLLOWER "old_ip:9010";
-- Add the new pod
ALTER SYSTEM ADD FOLLOWER "new_ip:9010";
Summary
The fe-proxy deployment updated your StarRocksCluster spec, which forced a restart of the FEs and the Operator. The "Terminating" pods are just a side effect of K8s being unable to finish the cleanup. Clear the finalizers, verify the new IPs match your SHOW FRONTENDS output, and your heartbeats should turn green again.GregM
05/19/2026, 2:32 PMRocky
05/19/2026, 2:33 PMTERMINATING and a standard patch didn't work, you are likely hitting a "Finalizer Chain" issue where the StarRocksCluster (SRC) custom resource is blocking the deletion of the pods, or your Kubernetes Node itself is unresponsive.
Follow these steps in order to break the deadlock:
1. Patch the StarRocksCluster (SRC) Resource
The Operator adds a finalizer to the main StarRocksCluster resource. If this resource is stuck in a reconcile loop (because the FE-proxy change failed), it will prevent the pods from being removed.
bash
# List your cluster name
kubectl get starrockscluster -n <namespace>
# Remove finalizer from the cluster object itself
kubectl patch starrockscluster <cluster-name> -n <namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
2. Use a JSON Patch for the Pods
Sometimes the --type=merge patch fails to clear the finalizer array if the field is protected. Use a JSON Patch instead, which is more explicit:
bash
# Force clear finalizers for the Operator pod
kubectl patch pod <operator-pod-name> -n <namespace> --type json -p='[{"op": "remove", "path": "/metadata/finalizers"}]'
# Force clear finalizers for the stuck FE pod
kubectl patch pod <fe-pod-name> -n <namespace> --type json -p='[{"op": "remove", "path": "/metadata/finalizers"}]'
3. Check for Stuck PVCs
If your FE is using Persistent Volumes (standard for StarRocks), the <http://kubernetes.io/pvc-protection|kubernetes.io/pvc-protection> finalizer on the PVC can sometimes prevent the pod from detaching and terminating.
bash
kubectl get pvc -n <namespace>
# If any are stuck in Terminating:
kubectl patch pvc <pvc-name> -n <namespace> -p '{"metadata":{"finalizers":null}}' --type=merge
4. Verify the Node Status
You mentioned "that node" is status TERMINATING. If you mean the Kubernetes Worker Node (not just the FE node), then the pods on it will never terminate gracefully because the Kubelet (the agent on the node) is gone.
* Check nodes: kubectl get nodes
* If a node is NotReady or Terminating, Kubernetes will wait indefinitely for the node to confirm the pod is dead.
* The Fix: Once you've removed the finalizers (Step 2), run the force delete again:
kubectl delete pod <pod-name> -n <namespace> --grace-period=0 --force
Why did fe-proxy trigger this?
When you enabled fe-proxy, Helm tried to update the Cluster spec. If there was a network mismatch or a resource conflict, the update hung. Because the Operator manages the FEs, and the FEs were in the middle of a restart, the "No route to host" error happened because the Leader FE was trying to talk to the old pod IP while the new pod was failing to start, leading to the current mess.
Once the pods are gone:
1. Helm will likely try to recreate them.
2. If the Operator restarts successfully, it should reconcile the cluster.
3. Verify the FE IPs with SHOW FRONTENDS; as soon as you can log in.GregM
05/19/2026, 2:56 PMStartup probe failed: Get "<http://myip:8030/api/health>": dial tcp myip:8030: connect: connection refused
i do have a load balancer, i wonder if that matters hereRocky
05/19/2026, 2:56 PMkubectl logs <fe-pod-name> -c fe.
* Look for: wait for fe to be ready or transfer of image failed.
* The Fix: Go to your MySQL client (connected to the Leader) and run SHOW FRONTENDS;. If the new FE’s IP isn't there, you must manually add it: ALTER SYSTEM ADD FOLLOWER "<new-pod-ip>:9010";.
2. IP Binding Issue (priority_networks)
Since you are using a Load Balancer and fe-proxy, the FE pod might have multiple network interfaces or a confusing IP environment. If StarRocks picks the wrong IP to bind to, the K8s probe (which uses the Pod IP) will get "Connection Refused."
* Check the logs for: Use IP init local addr, IP: /1.2.3.4.
* Verify: Does that IP match the Pod IP shown in kubectl get pod -o wide?
* The Fix: In your Helm values.yaml or StarRocksCluster CRD, set the priority_networks to match your Kubernetes Pod CIDR (e.g., 10.x.x.0/24). This forces the FE to bind to the correct internal IP.
3. Metadata/BDBJE Recovery
If the FE was force-deleted or the node was terminated, the metadata on the persistent volume might be corrupted or undergoing a "replay."
* Check the logs for: BDBJE errors or REPLICA_STATE transitions.
* The Fix: If the log says it can't join the cluster due to a "mismatched version" or "name already exists," you may need to clear the FE's metadata directory (/opt/starrocks/fe/meta) and let it re-sync from the Leader.
Does the Load Balancer/fe-proxy matter?
* The fe-proxy is a separate Nginx pod. It shouldn't break the internal health check of the FE pod itself, but it can complicate the network if you've configured the FE to use the Load Balancer's IP for its own rpc_port or edit_log_port.
* Probes: Ensure the startupProbe in your Kubernetes manifest is still pointing to the Pod IP, not the Load Balancer IP. Kubernetes probes are local to the node; they don't go through your external LB.
Next Step: Please run kubectl logs <fe-pod-name> -c fe | tail -n 50 and paste the last few lines. This will tell us exactly which stage of the "Handshake/Recovery" it's stuck in.
References
• faq/operation_maintenance_faq.md
• administration/management/FE_configuration.md
• deployment/deploy_manually.md
• administration/management/BE_configuration.mdGregM
05/19/2026, 3:00 PMcp: cannot create regular file '/opt/starrocks-artifacts/fe/conf/fe.conf': Read-only file system
cp: cannot create regular file '/opt/starrocks-artifacts/udf-jars/starrocks-geo-udf-all-1.1.jar': Permission denied
[Tue May 19 14:54:45 UTC 2026] Process conf file fe.conf ...
[Tue May 19 14:54:45 UTC 2026] start fe with exist meta.
[Tue May 19 14:54:45 UTC 2026] start with meta run start_fe.sh with additional options: ' --host_type FQDN'Rocky
05/19/2026, 3:01 PMPermission denied when trying to create `/opt/starrocks-artifacts/fe/conf/fe.conf`—occurs because the StarRocks container's entrypoint script is attempting to prepare the final configuration files and UDF jars in a working directory that is currently locked or non-writable.
In the StarRocks Kubernetes environment (especially when using the Operator), the container script copies the "base" configuration (from a ConfigMap) and any UDFs into /opt/starrocks-artifacts before starting the Java process.
The Cause
There are two likely reasons this is happening after your fe-proxy change:
1. readOnlyRootFilesystem is enabled: You likely have readOnlyRootFilesystem: true set in your Helm values.yaml or the StarRocksCluster CRD security context. StarRocks FE containers (as of current versions) do not support a read-only root filesystem because they need to write temporary configurations and process metadata during startup.
2. Volume Mount Conflict: The fe-proxy feature adds a sidecar container and potentially modifies volume mounts. If /opt/starrocks-artifacts is being mounted from a ConfigMap directly (which are read-only in K8s) instead of being an emptyDir that the script copies files into, the cp command will fail.
How to Fix
1. Disable Read-Only Root Filesystem
Check your values.yaml for the following setting and ensure it is set to `false`:
yaml
starrocksFESpec:
containerSecurityContext:
readOnlyRootFilesystem: false # Must be false
2. Check for Manual Volume Mounts
If you manually added any volumeMounts for configurations or UDFs in your StarRocksCluster spec, ensure they aren't mounting directly onto /opt/starrocks-artifacts. This directory is intended to be a writable space for the entrypoint script.
3. Verify User Permissions
The Permission denied on the .jar file suggests the container is running as a non-root user (e.g., UID 1000) but the directory /opt/starrocks-artifacts was created by root or has restricted permissions.
* If you are using a custom runAsUser, ensure that the volumes have the correct fsGroup so the StarRocks user can write to them.
yaml
starrocksFESpec:
podSecurityContext:
fsGroup: 1000 # Matches the user ID of the starrocks user in the container
Why this happened with fe-proxy?
The fe-proxy update likely triggered a recreation of the FE pods using the latest template from the Operator. If your configuration was previously relying on default (writable) behavior and the update applied a stricter security policy or changed how volumes are mapped, the "startup script" (which runs before the actual FE process) is now hitting a wall.
Recommendation: Set readOnlyRootFilesystem: false and restart the cluster. This should allow the script to assemble the fe.conf and the FE to start, finally, start and start its HTTP services on port 8030.
References
• administration/Meta_recovery.md
• faq/operation_maintenance_faq.mdGregM
05/19/2026, 3:07 PMreadOnlyRootFilesystem for both the operator and the fe, do i just put it as false for the fe?Rocky
05/19/2026, 3:07 PMfalse for the FE.
The FE process (and its startup script) is not designed to run in a fully read-only root environment. As you saw in your logs, the FE needs to write to /opt/starrocks-artifacts/ to prepare its configuration and move UDF jars before the Java process actually starts.
Guidance for each:
* For the FE (and BE/CN):
Must be false. StarRocks components write logs, metadata, temporary files, and configuration updates to their local filesystem. If you set this to true, the pod will fail at the entrypoint script every time.
yaml
starrocksFESpec:
containerSecurityContext:
readOnlyRootFilesystem: false
* For the Operator:
Can usually stay true, but it's safer to set to false if you are troubleshooting. The StarRocks Operator is a Go-based application that primarily talks to the Kubernetes API. However, if it tries to write local logs or temporary certificates and fails, it will crash. If your Operator is currently running and "Healthy" (not in a CrashLoop), you can leave it as is. If you see similar "Read-only" errors in the Operator's logs, change it to false as well.
Summary of what to do now:
1. Update your values.yaml or StarRocksCluster CRD to set readOnlyRootFilesystem: false for the FE.
2. Apply the change (helm upgrade or kubectl apply).
3. The FE pod should restart, successfully run the cp command, and then reach the stage where port 8030 is open.
Note: Once the "Read-only" error is gone, if you still get "Connection Refused," it means the FE process has started but is waiting to join the cluster (check kubectl logs for "wait for leader").
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/Meta_recovery.mdGregM
05/19/2026, 3:13 PMRocky
05/19/2026, 3:13 PMreadOnlyRootFilesystem: false for both FE and CN.
Why it must be false for FE and CN
The StarRocks entrypoint scripts (the code that runs just before the actual database starts) perform several write operations that require a writable root filesystem:
1. Configuration Injection: The scripts copy your fe.conf or cn.conf from a read-only ConfigMap into a local, writable directory (typically /opt/starrocks-artifacts/). They then "process" these files—injecting environment variables like your Pod IP or hostnames—to create the final configuration the process will use.
2. UDF Management: The script attempts to copy or link User-Defined Function (UDF) jars (e.g., starrocks-geo-udf.jar) into the StarRocks directory structure.
3. Local "Working" Files: Even though your data and logs are usually on Persistent Volumes (PVs), the processes often write small temporary files or create symlinks in their home directories (/opt/starrocks/fe or /opt/starrocks/cn) during the initialization phase.
What about the Operator?
The Operator can typically stay as readOnlyRootFilesystem: true. The Operator is a Go binary that interacts with the Kubernetes API; it doesn't need to "prepare" its own environment in the same way the FE and CN database engines do.
Summary of Actions
* FE: Set to false. Required for configuration processing and metadata init.
* CN: Set to false. Required for configuration processing (CN uses a similar entrypoint script to the FE).
* BE: Also set to false. BEs handle data and logs and require local write access for process-level operations.
If you must use a read-only root for security compliance:
You would need to manually define emptyDir volumes and mount them over every path StarRocks tries to write to (like /opt/starrocks-artifacts/, /opt/starrocks/fe/conf/, etc.). This is complex and not currently the standard deployment pattern in the StarRocks Helm charts. Stick Helm charts or Operator.
Verification
Once you set these to false and the pods restart, the cp: cannot create regular file error should disappear, and your FE should proceed to its "Handshake" or "Leader election" phase.
References
• administration/management/FE_configuration.md
• faq/operation_maintenance_faq.md
• administration/Meta_recovery.md
• administration/management/BE_configuration.md
• deployment/deploy_manually.md
References
• administration/management/FE_configuration.mdGregM
05/19/2026, 3:44 PMRocky
05/19/2026, 3:44 PM-Xmx).
* Recommendation: If you notice FE JVM Heap Stat consistently exceeding 80% on the remaining nodes, you should increase the -Xmx in fe.conf or restore the 3rd FE as soon as possible to distribute the planning load.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• faq/Others.md
• deployment/plan_cluster.md
• faq/Deploy_faq.md
• deployment/deploy_manually.mdGregM
05/19/2026, 3:50 PMRocky
05/19/2026, 3:50 PM-Xmx to 70% of total memory (~11.2GB for a 16GB pod).
* Total RAM > 32GB: Set -Xmx to 80% of total memory.
3 Ways to check if you need more heap
1. Check the "Old Gen" usage (Most Reliable)
Run this command inside your FE pod to see the real-time health of the Java heap:
bash
jstat -gcutil $(pgrep -f StarRocksFE) 1000 5
* Look at the O column (Old Generation): If this is consistently above 70-80%, your heap is getting full.
* Look at the FGC column (Full GC count): If this number is increasing frequently, the FE is struggling to reclaim memory and you need a larger heap immediately.
2. Check your Tablet Count
The FE stores metadata for every "tablet" in the cluster in its heap.
* 1 million tablets typically require at least 16GB of heap.
* If you have 8GB of heap, you should ideally stay well below 500k tablets. You can check your total tablets with:
sql
SHOW PROC '/statistic';
3. Use jmap for a quick object summary
If you want to see what is actually eating the memory without a heavy "dump," run:
bash
jmap -histo:live $(pgrep -f StarRocksFE) | head -n 20
This shows the top objects in memory. If com.starrocks.catalog.Tablet or similar metadata objects are at the top, it confirms your metadata is outgrowing your 8GB heap.
Why not give it all 16GB?
You must leave about 20-30% of the pod's memory (3-4GB in your case) for:
1. Direct Memory: Used for certain networking operations and metadata persistence (BDBJE).
2. Thread Stacks: Each connection to the FE uses a thread, which consumes memory outside the heap.
3. The OS/Container Runtime: To prevent the Linux OOM-killer from killing the entire pod.
Recommendation: Update your fe.conf (via Helm JAVA_OPTS) to set -Xmx11g -Xms11g. If the pod still hits OOM after that, you will need to increase the total pod memory to 32GB.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/Others.md
• administration/Meta_recovery.md
• faq/fe_mem_faq.md
References
• faq/Others.md
• administration/management/FE_configuration.md
• faq/fe_mem_faq.md
• knowledge/trouble_shooting/fe_memory_problems.md
• administration/Meta_recovery.mdGregM
05/19/2026, 3:52 PMRocky
05/19/2026, 3:52 PMIN lists can spike memory during the planning phase.
* Connections: Every open JDBC/MySQL connection consumes memory.
* Statistics: The FE keeps table statistics (histograms, row counts) in memory to make join decisions.
* Audit Logs & Temporary Objects: High-concurrency environments create a lot of "garbage" that needs to be cleared.
2. Why you should increase it to 11GB/12GB anyway
Since your pods already have 16GB of total memory, you are currently leaving 8GB (50%) to the operating system and non-heap tasks. This is overkill for a container.
* The 70% Rule: For a 16GB pod, the standard recommendation is to give 70% to the heap.
* Safety Margin: By moving to -Xmx11g, you give the JVM more room to handle "spikes" (like a heavy query or a node failure causing a tablet report storm) without triggering a Full GC.
* Full GC Risk: In an 8GB heap, if you hit 6GB of usage, the JVM starts working very hard to reclaim space. In an 11GB heap, that same 6GB of data is "comfortable," and the JVM can perform much faster, less intrusive Minor GCs.
Summary
* Is 8GB "ok"? Yes, for 94k tablets, it is technically enough to keep the cluster running.
* Is it "stable"? It is less stable than 11GB. If you have 16GB available in the pod, there is no downside to increasing the heap to 11GB (-Xmx11g -Xms11g). It will reduce the risk of the "domino effect" where one FE gets slow and causes the others to fail.
Check your current usage:
Run SHOW PROC '/statistic'; to confirm your tablet count hasn't spiked, and use the jstat -gcutil command mentioned earlier to see if your "Old Gen" (O column) is frequently above 75%. If it's consistently low (e.g., <40%), you can stay at 8GB, but 11GB is the "set it and forget it" choice.
References
• faq/Others.md
• faq/Deploy_faq.md
• administration/management/FE_configuration.md
• administration/Meta_recovery.md
• faq/operation_maintenance_faq.mdGregM
05/19/2026, 5:02 PMJAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xmx8192m -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"
into
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms12g -Xmx12g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"Rocky
05/19/2026, 5:02 PMJAVA_OPTS to:
bash
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms11g -Xmx11g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"
Why it was OOM'ing during startup
When an FE starts up, it must perform a Metadata Replay:
1. It loads the latest image file (a snapshot of your 94k tablets and schema).
2. It replays all the edit logs (transactions) that happened after that snapshot.
3. The Spike: During this process, the FE often needs significantly more memory than it does during idle operation because it is building large object maps in memory. If your heap is capped at 8GB, it can easily run out of space before it even finishes joining the cluster.
A Note on the GC Log parameter
In your proposed string:
-Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time
* Warning: Make sure the $DATE variable is actually defined in your environment before the FE starts. If it's not, the FE might fail to start because it can't create the log file.
* Alternative: Most StarRocks Kubernetes deployments handle logging via stdout/console or a fixed filename. If you are using the official Helm chart or Operator, ensure you aren't overriding the internal log rotation logic.
Summary of Safety Checks
1. Set to 11GB: This gives the JVM 11GB and leaves 5GB for the OS and off-heap tasks.
2. Match Xms and Xmx: You correctly set both to the same value. This prevents the JVM from pausing to resize the heap during the intensive startup phase.
3. Monitor: Once the FE is up, run jstat -gcutil <pid> 1000 5. If the O (Old Gen) column is still above 80% with an 11GB heap, you will need to increase the Pod's total memory to 32GB and then set the heap to 22-24GB.
References
• faq/Others.md
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• administration/Meta_recovery.md
• administration/management/FE_configuration.md
References
• faq/Others.md
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/operation_maintenance_faq.md
• faq/fe_mem_faq.md
• administration/Meta_recovery.md
• administration/management/FE_configuration.mdGregM
05/19/2026, 5:05 PMRocky
05/19/2026, 5:12 PM-Xmx24g. However:
* If you set a 24Gi heap but only request 16Gi, Kubernetes might schedule your pod on a node that doesn't actually have 24Gi of spare room.
* If the FE tries to use that extra memory and the node runs out, the Linux OOM-Killer will kill the entire pod instantly.
* Verdict: Don't go above 12Gi unless your jstat logs show your "Old Gen" is consistently above 70% after the pod has started.
Updated Config safely:
Your proposed change is safe, but I recommend 11g just to be conservative with the 16Gi request. If you want to use 12g, just ensure your cluster nodes aren't overcommitted.
Updated JAVA_OPTS:
bash
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms11g -Xmx11g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"
Summary of why this is better:
* Xms = Xmx: Setting these equal (e.g., both 11g) is best practice. It prevents the FE from pausing to resize the heap while it's trying to load metadata.
* Startup overhead: Metadata replay creates many short-lived objects. A larger "Young Gen" (which comes with a larger total heap) will make the startup much faster and prevent the OOM you encountered.
How to verify after the change:
Once the FE is up, run this command to see how much of that 11GB is actually being used:
bash
# O column is Old Gen usage percentage
jstat -gcutil $(pgrep -f StarRocksFE) 1000 5
If O is low (e.g. 20-30%), you are in the "Green Zone" and 11GB is perfect.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• faq/Others.md
• administration/management/FE_configuration.md
• administration/Meta_recovery.md
• faq/operation_maintenance_faq.md
References
• faq/Others.md
• administration/management/FE_configuration.md
• quick_start/helm.md
• faq/fe_mem_faq.md
• deployment/plan_cluster.mdGregM
05/19/2026, 5:21 PMS0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT
0.00 95.79 22.95 81.72 99.35 96.96 256 4.984 15 19.988 37 0.055 25.026
0.00 95.79 22.95 81.72 99.35 96.96 256 4.984 15 19.988 37 0.055 25.026
0.00 95.79 23.10 81.72 99.35 96.96 256 4.984 15 19.988 37 0.055 25.026
0.00 95.79 23.10 81.72 99.35 96.96 256 4.984 15 19.988 37 0.055 25.026
0.00 95.79 23.26 81.72 99.35 96.96 256 4.984 15 19.988 37 0.055 25.026Rocky
05/19/2026, 5:21 PMjstat output confirms that your FE is in a critical state. Here is the breakdown of why it is struggling:
1. Metaspace is Full (M = 99.35%): This is your biggest problem. Metaspace stores class metadata. When it hits ~99%, the JVM triggers a Full GC to try and reclaim space by unloading classes. This is likely why you see 15 Full GCs (FGC).
2. Old Gen is High (O = 81.72%): Your long-lived data (tablet metadata, cache, stats) is filling up more than 80% of your current heap. Once the Old Gen crosses 75-80%, the G1 collector has to work much harder, leading to the "Stop-the-World" pauses you are seeing in the FGCT column (nearly 20 seconds total).
3. Full GC loop: With 15 Full GCs taking 20 seconds, your FE is "freezing" frequently. This causes heartbeats to fail and can lead to the FE being kicked out of the cluster or the cluster becoming unresponsive.
Recommendation: Increase Heap and Metaspace
Since your Pod has a 32Gi Limit, you should use it. Your current 8GB/11GB heap is clearly not enough for your specific metadata load (even with "only" 94k tablets, you likely have many table versions or heavy query planning happening).
Update your JAVA_OPTS to this:
bash
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms16g -Xmx16g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"
Why these specific changes?
* -Xms16g -Xmx16g: This doubles your current capacity. It moves your 6.5GB–9GB of "live" data down to ~40-50% utilization, which is the "healthy zone" for the G1 collector.
* -XX:MaxMetaspaceSize=1g: Your current Metaspace is nearly full. Explicitly giving it 1GB (and starting it at 512MB) will stop the JVM from constant Full GCs trying to "clean" the class metadata.
* Request/Limit Note: Since you are increasing the heap to 16Gi, I strongly recommend updating your Helm requests to match:
yaml
resources:
requests:
cpu: 8
memory: 20Gi # 16Gi heap + 4Gi headroom
limits:
cpu: 16
memory: 32Gi
How to verify the fix:
After restarting with these settings, run the same jstat -gcutil command. You want to see:
* O (Old Gen): Under 60%.
* M (Metaspace): Lower percentage (since we increased the denominator).
* FGC (Full GC): This should stay at 0 or a very low number. If this number keeps climbing, you still have a memory pressure issue.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• administration/Meta_recovery.md
• faq/fe_mem_faq.md
• faq/Others.md
• faq/operation_maintenance_faq.mdGregM
05/19/2026, 5:24 PMRocky
05/19/2026, 5:24 PMINSERT INTO ... VALUES)
Unlike Stream Load (which is handled primarily by BEs), every JDBC INSERT is treated by the FE as a full SQL query:
* SQL Parsing & Planning: The FE must parse the SQL, analyze the schema, and generate an execution plan for every single insert. If NiFi is sending many small batches (e.g., 1–100 rows per insert), the FE spends more time planning than the BE spends writing.
* Metaspace Pressure: High-concurrency planning creates a lot of short-lived internal class metadata. This explains why your Metaspace (M) is at 99.35%. Each concurrent thread and its associated query plan objects push the Metaspace to its limit, triggering the frequent Full GCs you observed.
2. Primary Key Table Overhead
PK tables are significantly more demanding on the FE than Duplicate Key tables during writes:
* Transaction Coordination: For PK tables, the FE must coordinate the "Publish" and "Apply" phases of the transaction more strictly to ensure uniqueness.
* Lock Contention: PK tables use row-level or partition-level metadata locks. If you have many concurrent JDBC inserts hitting the same PK table, the FE becomes a bottleneck managing these locks, causing thread stacks to grow and memory usage to climb.
3. Why Metaspace specifically is failing
Metaspace stores the "blueprint" of your Java classes. In StarRocks FE:
* Prepared Statements: If NiFi or the JDBC driver is creating many unique prepared statements without closing them, or if the SQL strings are unique (no parameterization), the FE can leak or bloat Metaspace.
* Connection Overhead: Each JDBC connection has its own session state and metadata cache in the FE.
Recommendation
Beyond increasing the memory to 16Gi/32Gi (as discussed), you should:
1. Batching in NiFi: Ensure NiFi is not doing "one-row-per-insert." Increase the batch size in your PutDatabaseRecord or ExecuteSQL processors to at least 1,000–5,000 rows per transaction. This reduces the planning frequency.
2. Move more to Stream Load: The Duplicate Key table migration was the right move. If possible, move all high-concurrency writes to Stream Load. It bypasses the FE's SQL planner and is the only "safe" way to do high-frequency ingestion in StarRocks.
3. Fix Metaspace in JAVA_OPTS: Ensure you explicitly set the Metaspace limit so the JVM doesn't wait until the last second to clean it:
JAVA_OPTS="... -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g ..."
4. Check for "Transaction Overlap": Run SHOW PROC '/transactions'; in MySQL. If you see hundreds of PREPARE or COMMITTED transactions that haven't moved to VISIBLE, your FE is struggling to finish the "Publish" phase, which holds memory hostage.
Summary: Your 94k tablets only need ~2GB of RAM. The other 6GB+ is being "burnt" by the overhead of managing hundreds of small JDBC transactions.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• best_practices/primarykey_table.md
• loading/StreamLoad.md
• table_design/table_types/primary_key_table.md
References
• best_practices/primarykey_table.mdRocky
05/19/2026, 5:24 PMGregM
05/19/2026, 5:31 PMRocky
05/19/2026, 5:31 PMO) is fixed
Your Old Gen usage dropped from 81% to 7%.
* This means the heap increase worked perfectly.
* The JVM now has plenty of room to handle your metadata and query planning without struggling.
* Notice your FGC (Full GC) count is now 0. This is exactly what we want to see.
The Critical Issue: Metaspace (M) is at 99.20%
Even though your heap is healthy, your Metaspace is at the breaking point.
* What this means: Metaspace stores class metadata. When it hits ~99-100%, the JVM will trigger a Full GC to try and "clean up" old classes, even if your heap is empty.
* Why it's happening: Your concurrent JDBC inserts are the cause. Each SQL plan and JDBC connection creates metadata objects. Since you are doing many small inserts, you are generating class metadata faster than the JVM cleans it up.
Final Step to Stability
You need to explicitly give the Metaspace more room so it stops hovering at 99%. Since your Pod has a 32Gi limit, adding 1GB for Metaspace is very safe.
Update your JAVA_OPTS to include these two flags:
bash
# Keep your 16g heap, but add these Metaspace limits
JAVA_OPTS="-Xms16g -Xmx16g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g ..."
Why this matters:
If you don't set MaxMetaspaceSize=1g, the JVM will likely stay at its current default limit (which appears to be small in your environment). As soon as Metaspace hits 100%, your FGC count will start climbing again, and the FE will become unresponsive, regardless of how much heap memory you have.
Summary: You solved the "Heap OOM," now you just need to solve the "Metaspace OOM" by giving it that 1GB buffer. After you apply this, you should see the M percentage drop to around 30-50%.
References
• faq/Others.md
• knowledge/trouble_shooting/fe_memory_problems.md
• administration/Meta_recovery.md
• administration/management/FE_configuration.md
• faq/fe_mem_faq.md
• administration/management/monitoring/Monitor_and_Alert.mdGregM
05/19/2026, 5:33 PMJAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms16g -Xmx16g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"Rocky
05/19/2026, 5:34 PMO) dropped to 7% but your Metaspace (M) is still at 99.20%, it indicates that the FE is still hitting the ceiling for class metadata, but the behavior of the jstat command can be slightly misleading.
1. Understanding the M Column
In jstat -gcutil, the M column shows the percentage of the current allocated capacity, not necessarily the MaxMetaspaceSize.
* Because you set -XX:MetaspaceSize=512m, the JVM starts with 512MB.
* If your workload has already used ~508MB, jstat will show 99%.
* The JVM should then expand that capacity toward your 1GB limit.
To see if you are actually out of room, run this command to see the absolute MBs:
bash
jstat -gc <pid> | awk 'NR==1{for(i=1;i<=NF;i++) if($i=="MC" || $i=="MU" || $i=="MCMX") col[i]=$i} NR>1{for(i in col) printf "%s: %.2fMB ", col[i], $i/1024; print ""}'
* MC (Current Capacity): If this is 512MB and MU is ~510MB, the JVM is just about to grow.
* MCMX (Max Capacity): This should show 1024MB.
* MU (Used): This is the real amount of metadata you are storing.
2. Why is Metaspace so high with NiFi/JDBC?
Even with your "noisiest" job moved to Stream Load, the remaining JDBC inserts are likely the cause.
* Query Template Bloat: Every INSERT INTO ... VALUES requires the FE to generate a unique query plan. If NiFi is sending non-parameterized SQL or unique batches, the FE generates thousands of internal objects and metadata classes to handle the planning and execution.
* Primary Key Coordination: PK tables require the FE to manage transaction states and lock metadata. High-concurrency JDBC inserts force the FE to maintain a massive amount of active "session" and "transaction" metadata in memory.
3. Immediate Recommendations
A. Move ALL JDBC writes to Stream Load
StarRocks documentation explicitly states: "Do not use INSERT INTO VALUES() for production data ingestion" (it is intended only for testing/small manual inserts).
* JDBC inserts are handled by the FE (SQL Layer).
* Stream Load is handled by the BEs (Storage Layer), bypassing the FE's planning/metaspace bottleneck.
* In NiFi, replace the PutDatabaseRecord processor with a processor that calls the StarRocks Stream Load API (e.g., using InvokeHTTP or a custom StarRocks NiFi connector).
B. Verify NiFi Batching
If you cannot move to Stream Load yet, ensure your NiFi JDBC processor is batching heavily.
* Check the Statement Batch Size in NiFi. If it is small (e.g., 100), the FE is being hammered with metadata requests. Increase it to 5,000+.
C. Increase Metaspace further if needed
If the absolute MU (from step 1) is consistently hitting 1GB, you should increase the limit further. Given your 32Gi Pod limit and 16Gi heap, you can safely go to 2GB:
bash
-XX:MetaspaceSize=1g -XX:MaxMetaspaceSize=2g
D. Check Version-Specific Improvements
If you are on an older version (e.g., < 3.2.x), there were several metadata memory leaks fixed in later 3.x releases (such as Image streaming read and Active tablet report optimizations). If the memory doesn't stabilize after the changes above, consider an upgrade to the latest LTS.
Summary Checklist
1. Use jstat -gc to confirm if you are hitting the 512MB start or the 1GB max.
2. If MU is near 1GB, increase MaxMetaspaceSize to 2g.
3. Prioritize moving remaining JDBC INSERT tasks to Stream Load.
References
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/Others.md
• loading/StreamLoad.md
• faq/fe_mem_faq.md
• administration/management/FE_configuration.md
References
• faq/Others.mdRocky
05/19/2026, 5:34 PMGregM
05/19/2026, 5:37 PMRocky
05/19/2026, 5:37 PMjps (Recommended)
jps is a Java-specific tool that lists all running Java processes. This is the cleanest way to find it.
bash
jps -m | grep StarRocksFE
The number at the beginning of the output is your <pid>.
2. Using ps
If jps is not in your path, use the standard Linux process search:
bash
ps aux | grep StarRocksFE | grep -v grep | awk '{print $2}'
3. Check the .pid file
StarRocks FE usually creates a PID file when it starts. By default, it is located in the bin directory of your FE home:
bash
cat /path/to/starrocks/fe/bin/fe.pid
──────────
Pro-Tip: Run the command in one go
You can combine these into a single command so you don't have to manually copy-paste the PID:
bash
# This automatically finds the PID and runs your jstat analysis
PID=$(jps -m | grep StarRocksFE | awk '{print $1}')
jstat -gc $PID | awk 'NR==1{for(i=1;i<=NF;i++) if($i=="MC" || $i=="MU" || $i=="MCMX") col[i]=$i} NR>1{for(i in col) printf "%s: %.2fMB ", col[i], $i/1024; print ""}'
What to look for in the results:
* MC (Metaspace Capacity): This is what the JVM has currently reserved. If this is ~512MB and your MU is also ~512MB, the 99% you saw in jstat -gcutil just means the JVM needs to expand to the next chunk.
* MCMX (Max Metaspace Capacity): Since you added -XX:MaxMetaspaceSize=1g, this should show 1024.00MB.
* MU (Metaspace Used): This is the most important number. If this is over 800-900MB, you are truly running out of space for metadata, and you should increase the max to 2g.
References
• deployment/deploy_manually.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROC.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_FRONTENDS.mdGregM
05/19/2026, 5:38 PMRocky
05/19/2026, 5:38 PMjstat, but it also reveals that your configuration change might not have been applied correctly.
1. The Good News: You are NOT out of memory
Your MU (Metaspace Used) is only 158.31 MB.
* This is a very small and healthy amount of metadata.
* The "99%" you saw earlier is simply because the JVM has only "rented" 159.62 MB (MC) from the OS so far. Since 158 is almost 159, it looks full, but the JVM will just "rent" more from the OS as needed until it hits your 1GB limit.
2. The Discrepancy: Why is MC only 159MB?
You mentioned you set -XX:MetaspaceSize=512m. If that setting were active, your MC (Capacity) would be at least 512MB.
Since it is only 159MB, one of three things happened:
1. The FE was not restarted: The JVM is still running with its old settings.
2. Duplicate JAVA_OPTS: If you have two JAVA_OPTS lines in your fe.conf, the last one wins and overrides the first one.
3. Syntax Error: If there was a typo in the fe.conf, the FE might have ignored the new flags or failed to load the variable.
How to verify what the FE is actually using:
Run this command to see the actual flags the running process is using:
bash
ps aux | grep StarRocksFE
Look through the output for -XX:MetaspaceSize. If you don't see 512m or 1g in that string, the configuration change didn't take effect.
Verdict:
* Is it "better numbers"? Yes. Your actual usage (158MB) is very low.
* Are you safe? Yes, because you are nowhere near your 1GB limit (MU 158MB vs Max 1024MB).
* Action Item: Check fe.conf for duplicate JAVA_OPTS lines and ensure you perform a full restart (bin/stop_fe.sh then bin/start_fe.sh) to make sure that 512MB floor is set. This will stop the "99%" warnings from appearing in your monitoring.
References
• administration/management/FE_configuration.md
• faq/Others.md
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• administration/management/monitoring/Monitor_and_Alert.mdGregM
05/19/2026, 5:42 PMstarroc+ 34 57.9 19.5 28872932 12899596 ? Sl 17:28 6:21 /lib/jvm/java-17-openjdk/bin/java -Dlog4j2.formatMsgNoLookups=true -Xms16g -Xmx16g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -Xlog:gc*:/opt/starrocks-artifacts/fe/log/fe.gc.log.20260519-172837:time com.starrocks.StarRocksFE --host_type FQDNRocky
05/19/2026, 5:42 PMps aux output confirms that your settings are correctly applied. The flags -XX:MetaspaceSize=512m and -XX:MaxMetaspaceSize=1g are active in the running process.
Here is why your jstat numbers (MU: 158.31MB, MC: 159.62MB) look the way they do:
1. MetaspaceSize is a threshold, not a "floor"
Unlike -Xms (which forces the JVM to grab all that heap memory immediately), -XX:MetaspaceSize in modern Java (like the Java 17 you are using) acts as the high-water mark for the first Metaspace Garbage Collection.
* The JVM starts with a very small amount of Metaspace (usually ~20MB).
* It expands in small chunks as the FE loads classes.
* It is currently at 159MB (MC) because that is all the FE has needed to load its current code.
* By setting it to 512MB, you have told the JVM: "Don't even bother trying to run a Metaspace GC until you have grown to at least 512MB."
2. Why jstat -gcutil still shows 99%
This is a common point of confusion with jstat. The percentage in the M column is calculated as:
*`(Used / Current Capacity) * 100`*
In your case: (158.31 / 159.62) * 100 = 99.1%.
jstat is simply telling you that the JVM has filled up the current 159MB chunk it grabbed from the OS. As soon as the FE needs one more class, the JVM will grab another chunk (e.g., growing to 180MB), and that percentage will temporarily drop.
3. Verdict: Your FE is very healthy
* Actual Usage: Your FE is only using 158MB of metadata. This is a very safe and low number for a StarRocks FE.
* Headroom: You have a limit of 1024MB (1GB). You are only using ~15% of your total allowed Metaspace.
* Conclusion: You can ignore the "99%" in jstat -gcutil. Your FE is not in danger of a Metaspace OOM.
Next Steps
Since your Metaspace is stable at 158MB and your Old Gen dropped significantly after your previous changes, your FE is in a good state.
If you want to stop seeing the "99%" alerts in monitoring tools that rely on jstat, you could technically set -XX:MaxMetaspaceSize and -XX:MetaspaceSize to the same value (e.g., 1g), which forces the "Capacity" to stay large, but this is usually unnecessary given your current low usage.
References
• administration/management/FE_configuration.md
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• faq/Others.md
• administration/Meta_recovery.md
References
• using_starrocks/async_mv/materialized_view_task_run_details.md
• using_starrocks/Materialized_view-single_table.md
• using_starrocks/async_mv/Materialized_view.md
• table_design/table_design.md
• data_source/catalog/iceberg/iceberg_meta_table.md
• sql-reference/sys/object_dependencies.md
• sql-reference/information_schema/materialized_views.mdGregM
05/19/2026, 5:45 PMRocky
05/19/2026, 5:45 PMINSERT to Stream Load (or a connector like Flink/Spark that uses it).
* Why: JDBC inserts are treated as many tiny, individual transactions. This forces the FE to manage thousands of small "versions" of metadata, which is what eventually bloats your FE Heap and Metaspace.
* The Benefit: Stream Load batches data into larger chunks. This reduces the metadata "churn" on your FE and is significantly more efficient for the cluster.
* Recommendation: If you are using an application to send data, switch to the StarRocks Stream Load HTTP API or use a community connector.
2. Migration to Duplicate Key Tables (It Depends)
Moving to Duplicate Key tables is a trade-off. Your current 158MB Metaspace usage is NOT a reason to abandon PK tables.
Feature | Primary Key (PK) Table | Duplicate Key Table
Best For | Real-time updates / UPSERTs | Append-only logs / Raw data
Query Speed | Fastest (3-10x faster for many cases) | Slower (requires more scanning)
Memory Cost | High (Needs BE memory for indexes) | Very Low (No index needed)
Uniqueness | Enforced automatically | Not enforced (allows duplicates)
When to stay with Primary Key:
* If you need to update existing rows (UPSERT).
* If your query performance is critical.
_ _Note:* As long as your BE nodes have enough RAM to hold the PK index, you should keep using PK tables for the performance gains.
When to switch to Duplicate Key:
* If your data is append-only (like log files or telemetry) and you never need to update a row.
* If you are running out of BE memory (not FE memory).
* If you don't care about row-level uniqueness.
Summary Checklist:
1. Switch to Stream Load: YES. This is the "correct" way to scale ingestion and will keep your FE healthy long-term.
2. Switch to Duplicate Key: ONLY IF your data doesn't require updates. If you need updates, stay with PK tables; your FE memory is currently very safe, and the PK index lives on the BEs anyway.
References
• loading/StreamLoad.md
• best_practices/primarykey_table.md
• table_design/table_types/primary_key_table.md
• loading/loading_introduction/loading_considerations.md
• knowledge/trouble_shooting/fe_memory_problems.md
References
• table_design/table_types/primary_key_table.md
• best_practices/primarykey_table.mdGregM
05/19/2026, 6:03 PMRocky
05/19/2026, 6:03 PM-Xmx setting (the ceiling).
* Committed (GB): A step-like line below Max. This is the memory the JVM has actually claimed from the OS.
* Used (GB): The jagged, "all over the place" line. This is the actual memory being used by objects right now.
* Used % (Right Axis): Usually a percentage line that tracks how close Used is to Max.
2. The "Sawtooth" Pattern (A Good State)
In a healthy FE, the Used line should look like a sawtooth (a series of sharp zig-zags):
* The Upward Slope: The FE is doing work (loading metadata, handling queries). It’s normal for this to climb steadily.
* The Sharp Drop: This is a Garbage Collection (GC) event. The JVM has finished a task and "cleaned up" the memory.
* The "Valley" (The most important part): Look at the bottom of the zig-zags. If the "valleys" always return to roughly the same low level (e.g., 2GB or 4GB), your FE is in a perfect state.
3. When to be concerned (The "Bad State")
You are in a bad state if you see these patterns:
* The Rising Floor: If the bottom of the zig-zags keeps getting higher and higher over time, you likely have a memory leak or your metadata volume (table/tablet count) has grown too large for your current heap.
* The "Hairy" Top: If the line stays near the top (80-90%) and the zig-zags become very small and frequent, the JVM is struggling. It is spending all its time doing GC and very little time doing actual work.
* Flatlining at the Top: If the Used line is a flat line near the Max line, you are minutes away from an OOM (Out of Memory) crash or a "Full GC" lockup.
Summary: How to tell if you're "Good"
1. Check the Valleys: Does the memory drop back down to a consistent baseline after a spike? (If yes, you're good).
2. Check the %: Is the baseline (the valleys) below 70-80%? (If yes, you're good).
3. Check the Max: Is your Max line far above your Used peaks? (If yes, you have plenty of "breathing room").
In your specific case:
Since your jstat showed only 158MB of Metaspace and your FE has 16GB of Heap, your "Used" line should be fluctuating way down at the bottom of that chart. If the Used line is consistently below 8-10GB (50-60% of your 16GB), you are in a very safe state.
References
• administration/management/monitoring/Monitor_and_Alert.md
• knowledge/trouble_shooting/fe_memory_problems.md
• faq/fe_mem_faq.md
• administration/Meta_recovery.md
• administration/management/monitoring/metrics.md
References
• administration/management/monitoring/Monitor_and_Alert.md
• administration/Meta_recovery.md
• administration/management/resource_management/Memory_management.md
• faq/fe_mem_faq.md
• knowledge/trouble_shooting/fe_memory_problems.md