TL;DR: A standard Prometheus process was consuming over 160% CPU. Guided analysis with OpenResty XRay on the unmodified Go process revealed that Go garbage collection (GC) accounted for more than 99% of the CPU time. The main driver of the GC pressure is the loadWAL function, which rapidly allocates a large number of GC objects while loading data from the Write-Ahead Log (WAL) — this top allocating code path alone accounts for over 19% of all new object allocations. The full walkthrough follows below.

In this tutorial, you will get a step-by-step tour of how to use OpenResty XRay to identify the most CPU-intensive Go (golang) code paths of an online Prometheus server. These code paths are the ones that consume the most CPU time and severely affect Prometheus’ performance.

Problem: high CPU usage

Let’s start by running the top command to check the CPU usage. As shown, the Prometheus process consumes more than 160% of CPU cores.

top output: the prometheus process consuming 160% CPU

Running the ps command to see the full command line confirms this is the standard Prometheus binary executable (/usr/bin/prometheus) shipped with the Linux distribution, without any modifications.

ps output: the standard Prometheus binary /usr/bin/prometheus

Use the guided analysis feature of OpenResty XRay to spot the CPU-hottest Go code paths

Let’s use OpenResty XRay to analyze this unmodified process in real time. After confirming the target machine in the Web console, go to the “Guided Analysis” page and select “High CPU usage” from the list of problem types the system can diagnose.

OpenResty XRay guided analysis problem types, selecting High CPU usage

Then select the Go process we saw earlier in top — PID 50785, consuming 125% CPU, with the executable /usr/bin/prometheus. Choose “Go” as the language level, leave the maximum running time at the default 300 seconds, and start analyzing.

Selecting the target Go process in OpenResty XRay: PID 50785 at 125% CPU

OpenResty XRay keeps performing multiple rounds of sampling on the target process. For this case, two or three rounds are enough; then it automatically generates an analysis report.

This is the type of problem we diagnose. It’s “CPU”.

Screenshot

As you can see, the Go garbage collection takes up more than 99% of the CPU time.

Screenshot

For example, this Go code path where garbage collection is performed takes up to 21% of the CPU time.

Screenshot

The Go runtime function scanobject belongs to the garbage collector. It scans GC objects in the heap and marks reachable ones.

Screenshot

The function gcDrain is used to drain the work queue of GC objects that need to be marked.

Screenshot

Fast allocation of many GC objects incurs high GC overhead. So the report shows the hottest Go code paths allocating the most objects with the highest speed.

Screenshot

Now let’s check the #1 Go code path that allocates the most GC objects.

Screenshot

The function loadWAL is loading data from the Write-Ahead Log in Prometheus.

Screenshot

The function Series decodes time series data from the buffer and appends to the given slice.

Screenshot

The function slicebytetostring converts a slice of bytes to a string.

Screenshot

Click “More” to see more details.

Screenshot

This code path was automatically inferred from this Go GC object allocation flame graph.

Screenshot

Below are more explanations and suggestions about the issue.

Screenshot

It mentions the function loadWAL.

Screenshot

and this function is loading data from the Write-Ahead Log.

Screenshot

It also mentions the function Series,

Screenshot

and the function slicebytetostring.

Screenshot

Let’s go back to the code path. Hover the mouse over the green box for the function loadWAL.

Screenshot

We can see its source file and the full path for this file in the tooltip.

Screenshot

The source line number is 141.

Screenshot

Click the icon to copy the full Go source file path for this function.

Screenshot

Use the vim editor to open the source file. And look at the golang code in this file. You can use any editors you like.

Screenshot

Go to line 141, as OpenResty XRay suggested.

Screenshot

The function dec.Series is to decode a set of time series from a record.

Screenshot

On the status bar, you can see that this source line is inside the loadWAL function as shown in the report.

Screenshot

The TSDB of Prometheus creates a memory series to manage the most recent data. The number of GC objects newly allocated by this code path exceeds 19% of the total new allocations.

Screenshot

And we can see here that dynamically allocating new GC objects takes up almost 11% of the CPU time. So such allocations not only incur high garbage collection overhead but are also expensive themselves.

Screenshot

Automatic analysis and reports

Besides guided analysis, OpenResty XRay can also monitor online processes automatically and generate reports periodically. On the “Insights” page, you can find automatic analysis reports for daily and weekly periods — they also pinpoint the Go garbage collection and the hottest code paths without any manual work.

OpenResty XRay Insights page with automatic daily/weekly analysis reports

So you don’t necessarily have to use the “Guided Analysis” feature manually; that said, it remains very useful for application development and demonstration.

FAQ

Why is my Prometheus server using high CPU?

In this case, a standard, unmodified Prometheus binary (/usr/bin/prometheus) was consuming over 160% of CPU cores. Guided analysis with OpenResty XRay showed that Go garbage collection accounted for more than 99% of the CPU time. The GC pressure came from the loadWAL function rapidly allocating a large number of GC objects while loading data from the Write-Ahead Log (WAL) — that single code path accounted for over 19% of all newly allocated objects.

Why does Go garbage collection consume so much CPU?

Fast allocation of many GC objects incurs high GC overhead. The garbage collector then spends CPU time scanning and marking those objects — runtime functions like scanobject, which scans GC objects in the heap and marks reachable ones, and gcDrain, which drains the work queue of objects to be marked, showed up prominently in the analysis report. The allocations are also expensive by themselves: in this case, dynamically allocating new GC objects took up almost 11% of the CPU time on top of the collection work.

How do I find the Go code path causing Prometheus’s high CPU?

Use OpenResty XRay’s guided analysis on the running process — no modifications or instrumentation required. The report lists the hottest Go code paths allocating the most objects, inferred automatically from a Go GC object allocation flame graph. Hovering over a function box, such as loadWAL, reveals its source file path and line number (line 141 in this case), so you can open the file in any editor and inspect the exact code. The Insights page can also produce the same findings automatically in daily and weekly reports.

What is OpenResty XRay

OpenResty XRay is a dynamic-tracing product that automatically analyzes your running applications to troubleshoot performance problems, behavioral issues, and security vulnerabilities with actionable suggestions. Under the hood, OpenResty XRay is powered by our Y language targeting various runtimes like Stap+, eBPF+, GDB, and ODB, depending on the contexts.

If you like this tutorial, please subscribe to this blog site and/or our YouTube channel. Thank you!

About The Author

Yichun Zhang (Github handle: agentzh), is the original creator of the OpenResty® open-source project and the CEO of OpenResty Inc..

Yichun is one of the earliest advocates and leaders of “open-source technology”. He worked at many internationally renowned tech companies, such as Cloudflare, Yahoo!. He is a pioneer of “edge computing”, “dynamic tracing” and “machine coding”, with over 22 years of programming and 16 years of open source experience. Yichun is well-known in the open-source space as the project leader of OpenResty®, adopted by more than 40 million global website domains.

OpenResty Inc., the enterprise software start-up founded by Yichun in 2017, has customers from some of the biggest companies in the world. Its flagship product, OpenResty XRay, is a non-invasive profiling and troubleshooting tool that significantly enhances and utilizes dynamic tracing technology. And its OpenResty Edge product is a powerful distributed traffic management and private CDN software product.

As an avid open-source contributor, Yichun has contributed more than a million lines of code to numerous open-source projects, including Linux kernel, Nginx, LuaJIT, GDB, SystemTap, LLVM, Perl, etc. He has also authored more than 60 open-source software libraries.