<@U0A71G31CDV> I just noticed one of my fe's lates...
# questions-and-troubleshooting
g
@Rocky I just noticed one of my fe's latest heartbeats was a few days ago with error
No route to host
, how do i fix that
r
The error
No 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.
Copy code
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:
Copy code
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. Referencesfaq/operation_maintenance_faq.mdadministration/Meta_recovery.mdadministration/management/BE_configuration.mdadministration/management/FE_configuration.mdfaq/Deploy_faq.md
g
@Rocky I recently stood up the fe-proxy, I wonder if that had anything to do with it (via helm)
r
Enabling the
fe-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. Referencesquick_start/helm.mdadministration/Meta_recovery.mdfaq/operation_maintenance_faq.md Referencesquick_start/helm.mdfaq/operation_maintenance_faq.mdadministration/management/BE_configuration.mdadministration/Meta_recovery.mdfaq/Deploy_faq.md
g
@Rocky yikes both the operator and that node are status TERMINATING
r
This is a "deadlock" situation common in Kubernetes: the StarRocks Operator is responsible for cleaning up the FE pod, but since the Operator itself is also stuck
TERMINATING
, 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:
Copy code
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:
Copy code
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:
Copy code
bash
    kubectl delete pod <fe-pod-name> --grace-period=0 --force
3. Patch the FE pod (if force-delete fails):
Copy code
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.
g
@Rocky weird, I have two operators at the moment, one is up and one is terminating
r
Having two operator pods—one Running and one *Terminating*—is a clear sign that your
helm 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.
Copy code
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:
Copy code
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:
Copy code
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`:
Copy code
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.
g
@Rocky that patch didn't do anything for the operator or the fe, the fe is still stuck in terminating after I force deleted the operator
r
When both your Operator and FE pod are stuck in
TERMINATING
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.
Copy code
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:
Copy code
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.
Copy code
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.
g
@Rocky Ok I got a new fe up but it's failing health check with
Copy code
Startup 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 here
r
The "connection refused" error on port 8030 during the startup probe means the StarRocks FE process is running, but it has not yet opened its HTTP server. This usually happens for one of three reasons: 1. The FE is stuck "Waiting for Leader" If this is a new FE (follower), it won't fully start its HTTP and Query services until it successfully handshakes with the Leader FE. * Check the logs: Run
kubectl 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. Referencesfaq/operation_maintenance_faq.mdadministration/management/FE_configuration.mddeployment/deploy_manually.mdadministration/management/BE_configuration.md
g
@Rocky getting
Copy code
cp: 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'
r
The error you are seeing—`Read-only file system` and
Permission 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`:
Copy code
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.
Copy code
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. Referencesadministration/Meta_recovery.mdfaq/operation_maintenance_faq.md
g
@Rocky i see
readOnlyRootFilesystem
for both the operator and the fe, do i just put it as false for the fe?
r
Yes, you should set it to
false
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.
Copy code
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"). Referencesadministration/management/FE_configuration.mdfaq/operation_maintenance_faq.mdadministration/Meta_recovery.md
g
@Rocky i'm on 4.0.9, i see this option for FE,CN, and operator, does it need to be false for both FE and CN? Why?
r
In StarRocks 4.0.9 (and earlier versions), you should set
readOnlyRootFilesystem: 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. Referencesadministration/management/FE_configuration.mdfaq/operation_maintenance_faq.mdadministration/Meta_recovery.mdadministration/management/BE_configuration.mddeployment/deploy_manually.md Referencesadministration/management/FE_configuration.md
g
@Rocky if i have only 2/3 FEs up, would that cause more heap pressure than normal on the running 2?
r
Having only 2 out of 3 FEs running generally increases transient heap pressure on the remaining two, though your baseline heap usage (metadata) will remain roughly the same. Here is the breakdown of how the pressure changes: 1. Transient Heap Pressure (Increased) FEs are responsible for parsing, analyzing, and planning every SQL query. * Query Planning: If you have a load balancer (like the FE-Proxy you are setting up) or a JDBC load-balancing string, the traffic that was going to the 3rd FE is now redirected to the remaining two. * Object Allocation: Each query plan generates many short-lived Java objects. Handling 50% more queries per node means 50% more frequent allocations, which can lead to more frequent Minor GCs and higher heap "churn." 2. Baseline Heap Usage (Stable) Metadata (tablets, schemas, etc.) is the largest long-term consumer of FE heap. * Replication: Every Follower FE maintains a full copy of the metadata in its memory. * No Change: Because the remaining 2 FEs already had 100% of the metadata in memory while the 3rd was alive, their baseline memory footprint won't increase just because the 3rd node is gone. 3. Leader-Specific Roles If one of your surviving nodes is the Leader, it handles additional tasks: * Tablet Reports: All BEs send heartbeats and tablet status reports to the Leader. This creates constant heap pressure. * Checkpointing: In newer versions (3.4+), StarRocks can offload "image checkpoints" (saving metadata to disk) to Follower nodes to save Leader memory. If you lose a Follower that was handling checkpoints, the Leader may have to perform this memory-intensive task itself, leading to a temporary heap spike. 4. Risk of "The Domino Effect" The primary danger of running 2/3 FEs is not just the memory increase, but the Quorum risk: * Quorum: With a 3-node setup, you need at least 2 nodes to maintain a "Quorum." * Sensitivity: If the increased heap pressure on the remaining 2 FEs causes one to trigger a Long GC pause (Stop-the-World), the other node may think the "paused" node has died. This can break the quorum, causing the whole cluster to stop accepting writes or even reads. Summary * Is there more pressure? Yes, specifically from query planning and client connections. * Is it dangerous? Only if your FEs were already near their JVM limit (
-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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdfaq/Others.mddeployment/plan_cluster.mdfaq/Deploy_faq.mddeployment/deploy_manually.md
g
@Rocky how can i check to see if increasing the heap on my fe pods would be ok, they have 16gig mem but only 8gig allocated to heap
r
With 16GB of total RAM, increasing your FE heap from 8GB to 11GB or 12GB is generally safe and recommended if you are seeing memory pressure. In StarRocks, the rule of thumb for FE memory is: * Total RAM < 32GB: Set
-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:
Copy code
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:
Copy code
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:
Copy code
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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdfaq/Others.mdadministration/Meta_recovery.mdfaq/fe_mem_faq.md Referencesfaq/Others.mdadministration/management/FE_configuration.mdfaq/fe_mem_faq.mdknowledge/trouble_shooting/fe_memory_problems.mdadministration/Meta_recovery.md
g
@Rocky i only have 94k tablets, i should be ok with 8gig?
r
Strictly speaking, 94k tablets should fit comfortably within an 8GB heap from a pure metadata perspective. However, the official StarRocks recommendation sets a "floor" at 16GB of RAM (which typically means an ~11GB heap) for a reason. Here is how to think about your current setup: 1. Metadata vs. Operational Headroom * The Metadata: 94k tablets is relatively small. Each tablet in memory consumes roughly 10-15KB of heap (depending on the version and number of replicas). 94k tablets would only consume about 1GB to 1.5GB of actual heap space for the tablet objects themselves. * The Rest of the Heap: The remaining 6.5GB is used for: * Query Planning: Complex SQL with many joins or long
IN
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. Referencesfaq/Others.mdfaq/Deploy_faq.mdadministration/management/FE_configuration.mdadministration/Meta_recovery.mdfaq/operation_maintenance_faq.md
g
@Rocky i see the problem, the fe was OOM'ing from heap exhaustion trying to come up. is it safe to turn
JAVA_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"
r
Yes, it is safe and highly recommended to increase the heap, but 12GB might be slightly too aggressive for a 16GB pod. In a containerized environment, you need to leave enough "headroom" for the OS, thread stacks, and off-heap memory (Direct Memory). If the JVM claims too much, the Kubernetes OOM-Killer will kill the entire pod from the outside, even if the JVM thinks it has space. Recommendation Instead of 12GB, set it to 11GB. This aligns with the StarRocks "70% rule" for nodes with less than 32GB of RAM ($16GB \times 0.7 = 11.2GB$). Update your
JAVA_OPTS
to:
Copy code
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. Referencesfaq/Others.mdknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdadministration/Meta_recovery.mdadministration/management/FE_configuration.md Referencesfaq/Others.mdknowledge/trouble_shooting/fe_memory_problems.mdfaq/operation_maintenance_faq.mdfaq/fe_mem_faq.mdadministration/Meta_recovery.mdadministration/management/FE_configuration.md
g
@Rocky These are the values in my helm for FE resources: requests: cpu: 8 memory: 16Gi limits: cpu: 16 memory: 32Gi still keep at 11g?
r
With your 32Gi memory limit, you have plenty of room to increase the heap beyond 8GB. However, because your request is only 16Gi, there is a specific strategy you should follow to keep the pods stable. The Recommendation: Set Heap to 11Gi or 12Gi Even though your limit is 32Gi, setting your heap to 11Gi or 12Gi is the "sweet spot" for your current configuration: 1. Guaranteed Stability: By staying at 12Gi, your heap fits entirely within your 16Gi Request. This ensures that even if the Kubernetes node is under heavy pressure, your FE pod is guaranteed to have enough RAM to run its heap without being killed. 2. Solves the Startup OOM: The 94k tablets you have only need ~1.5GB of heap. The OOM you saw during startup was likely a temporary spike during the "Metadata Replay" phase. Increasing to 12Gi provides a ~400% increase in "buffer" space for that process. 3. Safe Off-Heap Room: Kubernetes pods need memory for thread stacks, Direct Memory (used by RPC/networking), and the OS. 12Gi heap + 4Gi off-heap = 16Gi. This fits perfectly in your request. Can you go higher (e.g., 24Gi)? Since your limit is 32Gi, you could technically set
-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:
Copy code
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:
Copy code
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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdfaq/Others.mdadministration/management/FE_configuration.mdadministration/Meta_recovery.mdfaq/operation_maintenance_faq.md Referencesfaq/Others.mdadministration/management/FE_configuration.mdquick_start/helm.mdfaq/fe_mem_faq.mddeployment/plan_cluster.md
g
@Rocky getting
Copy code
S0     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.026
r
This
jstat
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:
Copy code
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:
Copy code
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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdadministration/Meta_recovery.mdfaq/fe_mem_faq.mdfaq/Others.mdfaq/operation_maintenance_faq.md
g
@Rocky what could be the cause of this if I don't have many tablets. I do have a fair amount of concurrent JDBC inserts from nifi into primary key tables, I have migrated the noisiest one of these over to stream load into duplicate key table
r
The high memory and metaspace usage you're seeing—despite a low tablet count—is almost certainly caused by the concurrent JDBC inserts, particularly into Primary Key (PK) tables. Even after migrating your "noisiest" job to Stream Load, the remaining JDBC inserts are likely "death by a thousand cuts" for the FE. Here is why: 1. The "Hidden" Cost of JDBC Inserts (
INSERT 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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdbest_practices/primarykey_table.mdloading/StreamLoad.mdtable_design/table_types/primary_key_table.md Referencesbest_practices/primarykey_table.md
g
@Rocky better numbers? S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT 0.00 95.42 72.53 7.00 99.20 96.60 13 0.248 0 0.000 0 0.000 0.248 0.00 95.42 73.00 7.00 99.20 96.60 13 0.248 0 0.000 0 0.000 0.248 0.00 95.42 73.47 7.00 99.20 96.60 13 0.248 0 0.000 0 0.000 0.248 0.00 95.42 73.63 7.00 99.20 96.60 13 0.248 0 0.000 0 0.000 0.248 0.00 95.42 73.86 7.00 99.20 96.60 13 0.248 0 0.000 0 0.000 0.248
r
These numbers are much better regarding the heap, but you still have one major "red flag" that will cause your FE to freeze again. The Good News: Old Gen (
O
) 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:
Copy code
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%. Referencesfaq/Others.mdknowledge/trouble_shooting/fe_memory_problems.mdadministration/Meta_recovery.mdadministration/management/FE_configuration.mdfaq/fe_mem_faq.mdadministration/management/monitoring/Monitor_and_Alert.md
g
@Rocky i did change it to
Copy code
JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true -Xms16g -Xmx16g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -Xlog:gc*:${LOG_DIR}/fe.gc.log.$DATE:time"
r
If your Old Gen (
O
)
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:
Copy code
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:
Copy code
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. Referencesknowledge/trouble_shooting/fe_memory_problems.mdfaq/Others.mdloading/StreamLoad.mdfaq/fe_mem_faq.mdadministration/management/FE_configuration.md Referencesfaq/Others.md
g
@Rocky how do i get the pid for 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 ""}'
r
To get the PID (Process ID) for your StarRocks Frontend (FE), you can use any of the following methods directly from the terminal where the FE is running: 1. Using
jps
(Recommended)
jps
is a Java-specific tool that lists all running Java processes. This is the cleanest way to find it.
Copy code
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:
Copy code
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:
Copy code
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:
Copy code
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
. Referencesdeployment/deploy_manually.mdsql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROC.mdsql-reference/sql-statements/cluster-management/nodes_processes/SHOW_FRONTENDS.md
g
@Rocky here is the output of that command MU: 158.31MB MC: 159.62MB
r
This output explains exactly why you see 99% in
jstat
, 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:
Copy code
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. Referencesadministration/management/FE_configuration.mdfaq/Others.mdknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdadministration/management/monitoring/Monitor_and_Alert.md
g
@Rocky i do see the MetaspaceSize bits in the ps aux
Copy code
starroc+      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 FQDN
r
The
ps 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. Referencesadministration/management/FE_configuration.mdknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdfaq/Others.mdadministration/Meta_recovery.md Referencesusing_starrocks/async_mv/materialized_view_task_run_details.mdusing_starrocks/Materialized_view-single_table.mdusing_starrocks/async_mv/Materialized_view.mdtable_design/table_design.mddata_source/catalog/iceberg/iceberg_meta_table.mdsql-reference/sys/object_dependencies.mdsql-reference/information_schema/materialized_views.md
g
@Rocky but i should still migrate away from JDBC and pk tables in favor of stream load and dup key?
r
Yes, you should still consider migrating to Stream Load, but the decision to move away from Primary Key (PK) tables depends on your specific business requirements. Here is the breakdown of why you should (or shouldn't) make these changes: 1. Migration to Stream Load (Highly Recommended) Regardless of your table type, you should move from JDBC/SQL
INSERT
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. Referencesloading/StreamLoad.mdbest_practices/primarykey_table.mdtable_design/table_types/primary_key_table.mdloading/loading_introduction/loading_considerations.mdknowledge/trouble_shooting/fe_memory_problems.md Referencestable_design/table_types/primary_key_table.mdbest_practices/primarykey_table.md
g
@Rocky i don't understand the JVM Heap chart in grafana, the lines are all over the place. GB on the left axis, %used on the right, but how can I tell when i'm in a good state?
r
Reading the JVM Heap chart in Grafana can be confusing because it combines several metrics on different scales. Here is how to interpret those "all over the place" lines and tell if your FE is healthy. 1. What the lines actually mean Typically, a StarRocks Grafana JVM chart has 3–4 lines. Look at the legend at the bottom: * Max (GB): A flat line at the top. This is your
-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. Referencesadministration/management/monitoring/Monitor_and_Alert.mdknowledge/trouble_shooting/fe_memory_problems.mdfaq/fe_mem_faq.mdadministration/Meta_recovery.mdadministration/management/monitoring/metrics.md Referencesadministration/management/monitoring/Monitor_and_Alert.mdadministration/Meta_recovery.mdadministration/management/resource_management/Memory_management.mdfaq/fe_mem_faq.mdknowledge/trouble_shooting/fe_memory_problems.md