Fix Cpu Throttling in Quarkus/graalvm Operators By Tuning Thread Pools and Cpu Limits
Adjust worker pool sizes and CPU limits to align thread concurrency with available resources in Quarkus/GraalVM-based Kubernetes.
Adjust worker pool sizes and CPU limits to align thread concurrency with available resources in Quarkus/GraalVM-based Kubernetes operators.
Problem Context
Running a Java operator (Operator SDK + Quarkus/GraalVM native) with limits.cpu: 300m causes throttling despite low average CPU usage. Linux CFS enforces strict time-slicing: 300m = 30ms CPU time per 100ms cycle. High thread counts (e.g., Vert.x default 200-worker pool) exhaust this quota quickly, leading to throttling even if total CPU usage appears low.
Diagnosis Workflow
-
Check thread counts:
kubectl exec <pod> -- jstack | grep "Thread" | wc -lLook for mismatches between thread count and CPU limits (e.g., 200 threads on 0.3 CPU).
-
Verify throttling:
kubectl top pod <pod> --containersIf CPU requests/limits are low but throttling is observed in metrics, thread-to-CPU alignment is likely the issue.
-
Inspect runtime behavior:
kubectl exec <pod> -- /proc/1/stat | grep -o '(\w*)'Check for frequent context switches or blocked threads.
Repair Steps
-
Reduce worker pool size:
Quarkus/GraalVM native images often bake thread pool sizes at build time. Override Vert.x defaults explicitly:// In operator code or config vertx.createWorkerPool("operator-worker", 8, 8); // Size and max queueOr via system properties at runtime:
-Dvertx.workerPoolSize=8 -
Set event loop count:
-Dvertx.eventLoopCount=4Align with available CPU cores (e.g., 4 threads for 1 CPU).
-
Adjust CPU limits (if needed):
Remove or increase limits to allow bursting, but only after fixing thread counts:# Example: Set limits to 1 CPU (1000m) or omit entirely limits: cpu: 1000m
Policy Example
Configure operator thread pools via ConfigMap to enforce safe defaults:
apiVersion: v1
kind: ConfigMap
metadata:
name: operator-config
data:
vertx.properties: |
vertx.workerPoolSize=8
vertx.eventLoopCount=4
Reference in deployment:
env:
- name: VERTX_CONFIG
value: "/etc/config/vertx.properties"
Tooling
- Throttling detection:
kubectl describe pod(check Events for OOM kills or throttling hints). - Thread analysis:
jstack,kill -3, or/proc/<pid>/stack. - CPU profiling:
perf,bpftrace, orkubectl execwithtop.
Tradeoffs
- Removing CPU limits: Hides throttling but risks noisy neighbor issues.
- Aggressive pool reduction: May underutilize CPU under load; test with realistic workloads.
- GraalVM limitations: Native images may not dynamically adjust pools based on runtime CPU counts.
Troubleshooting
- No improvement after changes:
- Verify config is applied (check container logs for Vert.x pool initialization).
- Ensure no other libraries (e.g., Spring, Akka) define their own thread pools.
- Throttling persists:
- Check for hidden CPU limits in Helm charts, OperatorHub manifests, or cluster autoscaler policies.
- Use
dmesgto rule out kernel-level throttling (e.g., CPU shares misconfiguration).
Final Note
Thread-to-CPU alignment is critical in constrained environments. Start with conservative pool sizes and scale up based on observed behavior, not theoretical throughput. Always validate changes under load before deploying to production.
Source thread: CPU throttling on Quarkus/GraalVM native K8s operator despite low avg CPU usage — thread count culprit?

Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Pinterest
Email