When analyzing time-series data in Prometheus, raw counter values are often less useful than their rate of change. This is where rate() and irate() come in. rate(my_counter[5m]) calculates the average per-second increase of my_counter over the last 5 minutes, ideal for stable, long-term trends like requests per second (RPS) or bandwidth usage. irate(my_counter[5m]), on the other hand, specifically looks at the instantaneous rate over the last two data points within that 5-minute window. This makes irate() more sensitive to sudden spikes or drops, making it particularly useful for rapid-fire alerts where quick detection of change is critical. Understanding which one to use depends on whether you need a smoothed average or immediate responsiveness to recent changes.
Prometheus metrics often have many labels (e.g., instance, job, status_code, method), making raw query results very granular. Aggregation operators like sum(), avg(), max(), min(), and count() allow you to summarize this data across different dimensions. For instance, sum(rate(http_requests_total[5m])) by (job) will give you the total RPS for each job, effectively rolling up data from multiple instances within that job. Conversely, sum(rate(http_requests_total[5m])) without (instance) would sum RPS for each job and status_code, dropping the instance label. These aggregations are fundamental for creating meaningful dashboards and extracting higher-level insights, transforming raw data into actionable information by reducing noise and focusing on key dimensions.
As your monitoring setup grows, complex PromQL queries can become slow and resource-intensive, especially when used repeatedly across multiple dashboards or alerts. Recording rules are Prometheus's solution for this. They allow you to define a PromQL expression and have Prometheus pre-compute its result at regular intervals, saving it as a new time series. For example, you can define a rule to calculate sum(rate(http_requests_total[5m])) by (job) every minute. This pre-computed metric (e.g., job:http_requests_total:rate5m) can then be queried much faster by Grafana dashboards or alerting rules, significantly reducing the query load on your Prometheus server and improving the responsiveness of your observability stack. They are crucial for building high-performance, resilient monitoring systems.
Key Takeaways
rate()andirate()transform raw counters into meaningful rates of change, withrate()for averages andirate()for responsiveness.- Aggregation operators (
sum,avg,max, etc.) withby()orwithout()reduce data granularity for actionable insights. - Recording rules pre-calculate expensive or frequently used PromQL expressions, creating new, faster-to-query metrics.
- Combine these techniques to build efficient, performant dashboards and alerts.
- Leverage recording rules to reduce Prometheus query load and improve UI responsiveness.
Code Example
groups:
- name: application-overview
rules:
- record: job:http_requests_total:rate5m
expr: sum by (job) (rate(http_requests_total[5m]))
- record: instance:node_cpu_usage:avg1m
expr: 100 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100How this code works
This code defines Prometheus "recording rules," which serve to pre-calculate the results of frequently used or complex PromQL queries and store them as new, simpler metrics. This approach significantly speeds up dashboard loading times and simplifies alert rule definitions, as dashboards and alerts can query these pre-computed metrics directly instead of re-evaluating complex expressions repeatedly. Specifically, these rules create a new metric named job:http_requests_total:rate5m to track the 5-minute rate of HTTP requests, aggregated by job, and another named instance:node_cpu_usage:avg1m for the 1-minute average CPU usage percentage, broken down by instance.
The groups block organizes these rules. For the job:http_requests_total:rate5m metric, its expr uses rate(http_requests_total[5m]) to calculate the per-second rate of increase of the http_requests_total counter over a five-minute window, then sum by (job) aggregates these rates across all instances belonging to the same job. For instance:node_cpu_usage:avg1m, the expr calculates the rate of node_cpu_seconds_total specifically in "idle" mode over one minute, averages it by instance, and then subtracts this idle percentage from 100 to yield the actual CPU usage percentage. A subtle point for beginners is that rate() always returns a per-second rate, regardless of the [duration] (e.g., [5m]) specified; the duration merely defines the time window over which to observe the change.