<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Integrate with Graphite using the proxy on Grafana Labs</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/</link><description>Recent content in Integrate with Graphite using the proxy on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/enterprise-metrics/v2.16.x/manage/graphite/index.xml" rel="self" type="application/rss+xml"/><item><title>Architecture overview</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/architecture/</link><pubDate>Tue, 22 Apr 2025 16:43:47 +0000</pubDate><guid>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/architecture/</guid><content><![CDATA[&lt;h1 id=&#34;architecture-overview&#34;&gt;Architecture overview&lt;/h1&gt;
&lt;p&gt;This page provides an overview of the architecture of the Graphite proxy services and how they interact with the other
GEM services.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;../graphite_proxy.png&#34;
  alt=&#34;Graphite proxy&#34;/&gt;&lt;/p&gt;
&lt;p&gt;Note that the &amp;ldquo;Graphite deployment&amp;rdquo; shown in the architecture diagram is optional, but without it the number of functions
which are available in the Graphite query language is limited to a subset of what standard Graphite provides.&lt;/p&gt;
&lt;h2 id=&#34;api&#34;&gt;API&lt;/h2&gt;
&lt;p&gt;The Graphite proxy in GEM exposes the same write and read endpoints as &lt;a href=&#34;/docs/grafana-cloud/data-configuration/metrics/metrics-graphite/http-api/&#34;&gt;Grafana Cloud Graphite&lt;/a&gt;.
All endpoints have a &lt;code&gt;/graphite/&lt;/code&gt; prefix.&lt;/p&gt;
&lt;p&gt;If using 
    &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/api/admin-api/#access-policy-api&#34;&gt;access policies&lt;/a&gt;, write endpoints require the &lt;code&gt;metrics:write&lt;/code&gt; scope and read endpoints require the &lt;code&gt;metrics:read&lt;/code&gt; scope.&lt;/p&gt;
&lt;h2 id=&#34;metrics-ingestion&#34;&gt;Metrics ingestion&lt;/h2&gt;
&lt;p&gt;This section explains the components of a typical ingestion pipeline.
The use of Carbon-relay-ng is optional, if the metrics producer has the ability to send its metrics in the right format
via HTTP(S) directly to the Graphite write proxy then Carbon-relay-ng could be omitted, but since Carbon-relay-ng comes
with lots of useful features such as buffering of failed writes it is recommended to use it.&lt;/p&gt;
&lt;h3 id=&#34;carbon-relay-ng&#34;&gt;Carbon-relay-ng&lt;/h3&gt;
&lt;p&gt;Carbon-relay-ng is a relay for carbon streams, to make it forward data to the Graphite proxy a &lt;code&gt;gnet&lt;/code&gt; route needs to be
configured and pointed at the Graphite ingest API (&lt;code&gt;&amp;lt;GEM gateway URL&amp;gt;/graphite/metrics&lt;/code&gt;) on the Graphite write proxy.
It comes with a wide variety of features, for a detailed documentation of them refer to
&lt;a href=&#34;https://github.com/grafana/carbon-relay-ng#documentation&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Carbon-relay-ng&lt;/a&gt;.
The official documentation also comes with an example configuration which demonstrates the usage a &lt;code&gt;gnet&lt;/code&gt; route, refer to
&lt;a href=&#34;https://github.com/grafana/carbon-relay-ng/blob/master/docs/config.md#example&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;grafanaNet route&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;graphite-write-proxy&#34;&gt;Graphite write proxy&lt;/h3&gt;
&lt;p&gt;The Graphite write proxy accepts the incoming requests which Carbon-relay-ng is sending and translates the Graphite
metrics into Prometheus metrics, then it forwards them to the GEM distributors.
For more information about how the process works, refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_write_proxy/&#34;&gt;Graphite write proxy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;metrics-querying&#34;&gt;Metrics querying&lt;/h2&gt;
&lt;p&gt;This section explains the components that are used to query Graphite metrics.&lt;/p&gt;
&lt;h3 id=&#34;graphite-querier&#34;&gt;Graphite Querier&lt;/h3&gt;
&lt;p&gt;The Graphite querier provides the Graphite render API, for more information about the API refer to the
&lt;a href=&#34;https://graphite.readthedocs.io/en/stable/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Graphite documentation&lt;/a&gt;.
It fetches the data required to serve a query from GEM, performs the configured aggregations on the data at query time
and runs the requested Graphite functions on the aggregated data.
For more information about how the process works, refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_querier/&#34;&gt;Graphite querier&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;grafana-datasource&#34;&gt;Grafana datasource&lt;/h4&gt;
&lt;p&gt;If you are using the &lt;a href=&#34;/docs/grafana/latest/datasources/graphite/&#34;&gt;Graphite datasource&lt;/a&gt; to query the Graphite proxy, use the URL &lt;code&gt;&amp;lt;GEM gateway URL&amp;gt;/graphite&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;graphite&#34;&gt;Graphite&lt;/h3&gt;
&lt;p&gt;Open source Graphite can be used by the Graphite querier to off-load the Graphite function processing of query functions
that are not implemented in its own native function library, that way all Graphite functions are available to use.&lt;/p&gt;
&lt;p&gt;You can run the Graphite querier without providing it any open source Graphite deployment.
However, without the Graphite deployment the set of query functions that are available in the Graphite query language
will be limited to a reduced set of very commonly used functions.
In order to gain function completeness a Graphite deployment is required.&lt;/p&gt;
]]></content><description>&lt;h1 id="architecture-overview">Architecture overview&lt;/h1>
&lt;p>This page provides an overview of the architecture of the Graphite proxy services and how they interact with the other
GEM services.&lt;/p>
&lt;p>&lt;img
class="lazyload d-inline-block"
data-src="../graphite_proxy.png"
alt="Graphite proxy"/>&lt;/p></description></item><item><title>Use the Graphite querier</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_querier/</link><pubDate>Tue, 22 Apr 2025 16:43:47 +0000</pubDate><guid>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_querier/</guid><content><![CDATA[&lt;h1 id=&#34;use-the-graphite-querier&#34;&gt;Use the Graphite querier&lt;/h1&gt;
&lt;p&gt;The Graphite querier provides the Graphite querying API, for more information about the API refer to the
&lt;a href=&#34;https://graphite.readthedocs.io/en/stable/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Graphite documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;supported-graphite-functions&#34;&gt;Supported Graphite functions&lt;/h2&gt;
&lt;p&gt;The Graphite querier comes with a set of built-in functions which tend to execute faster than the original Graphite
implementation, we refer to these built-in functions as the &amp;ldquo;native query engine&amp;rdquo;.
The native query engine only implements a set of very commonly used functions, many of the less commonly used functions
are missing from it.
Whenever the Graphite querier receives a query which includes a function that it doesn&amp;rsquo;t have in the native query engine
it offloads the function processing to a stock Graphite deployment (if available).
To learn more about how the Graphite querier offloads the function processing to a separate Graphite process, refer
to &lt;a href=&#34;#offloading-the-function-processing-to-graphite&#34;&gt;Offloading the function processing to Graphite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The native query engine is imported from an open source project called CarbonAPI, for more information about its
function library and which functions are/aren&amp;rsquo;t implemented, refer to
&lt;a href=&#34;https://github.com/go-graphite/carbonapi&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the CarbonAPI repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;query-handling&#34;&gt;Query handling&lt;/h2&gt;
&lt;p&gt;The query handling endpoint accepts Graphite queries, it processes them in the following steps.
This is a simplification which ignores the fact that some steps in the process are cached.&lt;/p&gt;
&lt;h3 id=&#34;parsing-the-query&#34;&gt;Parsing the query&lt;/h3&gt;
&lt;p&gt;The query gets parsed and based on the used functions a decision is made which query engine shall be used.
Whenever all used functions are supported by the native query engine then this one is used, otherwise the Graphite
function processing will be off-loaded to the external Graphite process.&lt;/p&gt;
&lt;p&gt;The metric name patterns get extracted from the query and a Prometheus query gets generated to fetch the required data
to serve the query from Grafana Mimir.
To generate that Prometheus query the name mapping schemes get applied in reverse, for more information about the name
mapping schema refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_write_proxy/&#34;&gt;Graphite write proxy&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;breaking-the-query-up-into-sub-queries&#34;&gt;Breaking the query up into sub-queries&lt;/h3&gt;
&lt;p&gt;If the original query is requesting a long time range, then it gets broken up into sub-queries.
Each sub-query has a maximum time range of &lt;code&gt;1d&lt;/code&gt; by default, configurable via the flag
&lt;code&gt;graphite.querier.split-queries-by-interval&lt;/code&gt;, the time range boundaries are aligned to multiples of the split interval.
The default sub-query length of &lt;code&gt;1d&lt;/code&gt; is derived from the fact that by default the blocks in the store have a length of
&lt;code&gt;1d&lt;/code&gt; after compaction, that way each sub-query processes exactly one block.&lt;/p&gt;
&lt;p&gt;Each sub-query gets assigned two points budgets defining how many data points it may produce, the two budgets are called
&lt;code&gt;soft budget&lt;/code&gt; and &lt;code&gt;hard budget&lt;/code&gt;.
The points budgets per sub-query get calculated based on the total query budgets configured via the flags
&lt;code&gt;graphite.querier.max-points-per-req-{soft,hard}&lt;/code&gt; which by default have the values &lt;code&gt;1000000&lt;/code&gt; and &lt;code&gt;20000000&lt;/code&gt;, each
sub-query gets an equal share which is proportionate to the total queried time range.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;soft budget&lt;/code&gt; defines to what number of points the results should optimally be aggregated, but it may be exceeded if
it is not possible to stay within the &lt;code&gt;soft budget&lt;/code&gt; due to the configured &lt;code&gt;retentions&lt;/code&gt; in the storage-schemas.
If the number of points can&amp;rsquo;t be reduced to fit within the &lt;code&gt;hard budget&lt;/code&gt; then the query directly errors to prevent
overloading of the query engine.&lt;/p&gt;
&lt;p&gt;Note that the sub-queries are only responsible for fetching and processing the raw data in preparation for the query
engine to run on top of the data.
The query engine will later run on the combined sub-query results and not on each sub-query result separately.&lt;/p&gt;
&lt;h3 id=&#34;processing-of-the-sub-queries&#34;&gt;Processing of the sub-queries&lt;/h3&gt;
&lt;p&gt;The sub-queries which resulted from breaking up the original query get processed concurrently by a pool of worker
threads.
The size of the work pool is configurable via the flag &lt;code&gt;graphite.querier.query-handling-concurrency&lt;/code&gt;, with a default
value of &lt;code&gt;8&lt;/code&gt;.
Increasing the worker pool size can speed up the processing of queries with long time ranges because they will be
processed at a higher concurrency as long as enough physical cores are available, but it will also increase the memory
consumption.&lt;/p&gt;
&lt;h3 id=&#34;fetching-the-data&#34;&gt;Fetching the data&lt;/h3&gt;
&lt;p&gt;Each sub-query worker separately fetches the data which it requires to process its sub-query from Grafana Mimir.
This means that if one Graphite query gets broken up into &lt;code&gt;5&lt;/code&gt; sub-queries then Grafana Mimir sees &lt;code&gt;5&lt;/code&gt; separate queries, so it
is expected that the Grafana Mimir query rate might be higher than the Graphite query rate.&lt;/p&gt;
&lt;h3 id=&#34;choosing-the-target-interval-for-each-metric&#34;&gt;Choosing the target interval for each metric&lt;/h3&gt;
&lt;p&gt;Each metric will get aligned to an interval that is determined based on the tenant&amp;rsquo;s storage-schemas configuration.
First the metrics get grouped by the schemas of which they match the pattern, these groups are called &lt;code&gt;rollup groups&lt;/code&gt;
because they always get rolled up (horizontally aggregated) together, then each &lt;code&gt;rollup group&lt;/code&gt;&amp;rsquo;s target interval gets
determined based on the queried time range and the schema configuration.
For this the time range of the original Graphite query is used and not the time range of the sub-query.
To learn more details about the process of determining the target interval, refer to
&lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/#storage-schemas&#34;&gt;Storage schemas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After each &lt;code&gt;rollup group&lt;/code&gt; has an assigned target interval the total number of points which this sub-query is going to
generate gets calculated based on the number of metrics in each &lt;code&gt;rollup group&lt;/code&gt;, their assigned target intervals, and the
time range of the sub-query.&lt;/p&gt;
&lt;p&gt;If the total number of points which will get generated by this sub-query exceeds the sub-query&amp;rsquo;s &lt;code&gt;soft budget&lt;/code&gt; then the
&lt;code&gt;rollup group&lt;/code&gt; which currently has the lowest target interval gets bumped to the next retention of its matching storage
schema.
Since the retentions are always defined in increasing interval order, bumping a rollup group to the next retention
results in less points being generated.
This process gets repeated until either the total number of points generated by the sub-query fits in the &lt;code&gt;soft budget&lt;/code&gt;
or all &lt;code&gt;rollup group&lt;/code&gt;s have already been bumped to their final retention.&lt;/p&gt;
&lt;p&gt;If the total number of points which will get generated by this sub-query still exceeds the sub-query&amp;rsquo;s &lt;code&gt;hard budget&lt;/code&gt;
then an error gets returned and the query processing gets aborted.&lt;/p&gt;
&lt;h3 id=&#34;alignment-and-aggregation-of-the-data&#34;&gt;Alignment and aggregation of the data&lt;/h3&gt;
&lt;p&gt;The data points of each metric now get aligned to the target interval of the &lt;code&gt;rollup group&lt;/code&gt; which the metric is in.
If there are data points with time stamps that are not a multiple of the target interval then they get aligned to
multiples of the interval by shifting them to a lower time stamp.
If there are multiple data points within one interval then they get aggregated together by the aggregation function
chosen based on the storage-aggregation&amp;rsquo;s configuration of the tenant, the resulting value will be assigned with a
timestamp that is a multiple of the target interval.
To learn more about the storage-aggregation refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/#storage-aggregations&#34;&gt;Storage aggregations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This process is cached, but the caching logic is omitted in this description for the sake of simplicity, to learn more
about the caching logic refer to &lt;a href=&#34;#caching&#34;&gt;Caching&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;combining-the-sub-query-results&#34;&gt;Combining the sub-query results&lt;/h3&gt;
&lt;p&gt;Each sub-query returns a set of metrics that has been aligned and aggregated to the correct interval, now the results
of the different sub-queries get combined into one set of metrics.&lt;/p&gt;
&lt;p&gt;If a sub-query result contains a metric which is not present in another sub-query result then the gap is filled with
&lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;../concatenating_subquery_results.png&#34;
  alt=&#34;Concatenating sub-query results&#34;/&gt;&lt;/p&gt;
&lt;p&gt;At this point it is possible that the same metric has been aggregated to different intervals in the different
sub-queries, because if one sub-query resulted in a larger number of metrics than another then its retention might have
been bumped to fit the number of points generated by the sub-query into its &lt;code&gt;soft budget&lt;/code&gt;
In this situation the sub-query results with the lower interval get aggregated to match the interval of the sub-query
result with the higher interval, because the Graphite query engines require each metric to have a constant interval.&lt;/p&gt;
&lt;p&gt;The result after combining the sub-query results is one set of metrics, where each metric is guaranteed to have a
consistent interval and each metric has data points filling the entire queried time range because all gaps have been
filled with &lt;code&gt;NaN&lt;/code&gt; values.&lt;/p&gt;
&lt;h3 id=&#34;function-processing&#34;&gt;Function processing&lt;/h3&gt;
&lt;p&gt;The combined sub-query results now get passed into the query engine that has been chosen in the beginning of the query
handling process.
This can either be the native query engine (preferred) or a an optional Graphite deployment running in a separate
process (for function completeness).
To learn more about how the Graphite querier offloads the function processing to a separate Graphite process, refer
to &lt;a href=&#34;#offloading-the-function-processing-to-graphite&#34;&gt;Offloading the function processing to Graphite&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;returning-to-the-user&#34;&gt;Returning to the user&lt;/h3&gt;
&lt;p&gt;The query engine returns a set of metrics which has been generated by running the Graphite functions specified in the
query on the combined sub-query results, this set of metrics now gets returned to the user.&lt;/p&gt;
&lt;h2 id=&#34;illustration&#34;&gt;Illustration&lt;/h2&gt;
&lt;p&gt;This is an illustration of the above described query handling process:&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;../graphite_query_processing.png&#34;
  alt=&#34;Graphite query processing&#34;/&gt;&lt;/p&gt;
&lt;h2 id=&#34;caching&#34;&gt;Caching&lt;/h2&gt;
&lt;p&gt;The aggregation work which is performed as part of the query handling gets cached in order to minimize the latency of
queries that request the same metrics with overlapping time ranges multiple times.
The caching happens in chunks of data, where each chunk has a size of &lt;code&gt;1d&lt;/code&gt; by default, configurable via
&lt;code&gt;-graphite.querier.split-queries-by-interval&lt;/code&gt;.
The boundaries of the cached chunks are always multiples of the chunk size in UTC, meaning that each chunk contains the
data of one day from midnight to midnight in UTC by default.
Partial chunks don&amp;rsquo;t get cached, they get regenerated at every query.&lt;/p&gt;
&lt;p&gt;Imagine a Grafana dashboard querying a given set of metrics with a constant time range length of &lt;code&gt;3d&lt;/code&gt; applied relative
to the current time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first query requests the time range &lt;code&gt;2021-01-10T13:25:00Z - 2021-01-12T13:25:00Z&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-10T13:25:00Z - 2021-01-11T00:00:00Z&lt;/code&gt; gets generated but it can&amp;rsquo;t be
cached because it is partial&lt;/li&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-11T00:00:00Z - 2021-01-12T00:00:00Z&lt;/code&gt; gets generated and cached&lt;/li&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-12T00:00:00Z - 2021-01-12T13:25:00Z&lt;/code&gt; gets generated but it can&amp;rsquo;t be
cached because it is partial&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Grafana dashboard refreshes again &lt;code&gt;1min&lt;/code&gt; later&lt;/li&gt;
&lt;li&gt;Now the new query is requesting the time range &lt;code&gt;2021-01-10T13:26:00Z - 2021-01-12T13:26:00Z&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-10T13:26:00Z - 2021-01-11T00:00:00Z&lt;/code&gt; gets generated but it can&amp;rsquo;t be
cached because it is partial&lt;/li&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-11T00:00:00Z - 2021-01-12T00:00:00Z&lt;/code&gt; gets retrieved from the cache&lt;/li&gt;
&lt;li&gt;The sub-query result for the time range &lt;code&gt;2021-01-12T00:00:00Z - 2021-01-12T13:26:00Z&lt;/code&gt; gets generated but it can&amp;rsquo;t be
cached because it is partial&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means the data fetching, the alignment and the aggregation of the data of the entire day &lt;code&gt;2021-01-11&lt;/code&gt; is omitted.
The caching is especially effective for queries that query for long time ranges.
There are two caches involved in this, they are named &lt;code&gt;metric name cache&lt;/code&gt; and &lt;code&gt;aggregation cache&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;metric-name-cache&#34;&gt;Metric name cache&lt;/h3&gt;
&lt;p&gt;The metric name cache caches the resolution of metric name patterns that are used in the queries into lists of
concrete metric names.
Each entry in the metric name cache contains a list of metric names corresponding to a combination of the following
attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metric name pattern&lt;/li&gt;
&lt;li&gt;Time range&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;aggregation-cache&#34;&gt;Aggregation cache&lt;/h3&gt;
&lt;p&gt;The aggregation cache caches the aligned and aggregated results of sub-queries on a per-metrics basis.
Each entry in the aggregation cache contains a chunk of data corresponding to a combination of the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Metric name&lt;/li&gt;
&lt;li&gt;Time range with a length of &lt;code&gt;1d&lt;/code&gt; by default&lt;/li&gt;
&lt;li&gt;Target interval&lt;/li&gt;
&lt;li&gt;Aggregation function used&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cache-usage&#34;&gt;Cache usage&lt;/h3&gt;
&lt;p&gt;This illustrates the flow how these two caches are used in the query handling process.
&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;../cache_usage_flow.png&#34;
  alt=&#34;cache usage flow&#34;/&gt;&lt;/p&gt;
&lt;h2 id=&#34;offloading-the-function-processing-to-graphite&#34;&gt;Offloading the function processing to Graphite&lt;/h2&gt;
&lt;p&gt;The Graphite querier&amp;rsquo;s native query engine only implements an incomplete set of Graphite functions, whenever a query
involves a Graphite function that is not implemented in the native query engine then the Graphite querier offloads the
function processing to a Graphite deployment which runs in a separate process.&lt;/p&gt;
&lt;p&gt;It does this by forwarding the original query to the Graphite process, just like a simple reverse HTTP Proxy.
The Graphite process is configured to use the Graphite querier as a cluster server, because it doesn&amp;rsquo;t have the
necessary data to serve the query locally it will then reach out to the Graphite querier to fetch the raw data via a
separate call to the &lt;code&gt;/graphite/render&lt;/code&gt; endpoint which queries only for the data without any Graphite functions in the
query.&lt;/p&gt;
&lt;p&gt;The Graphite querier will then handle this second query which it received just like any other query and it will return
the queried data to the client, which in this case is the Graphite process.
In the process of handling this query the Graphite querier will still perform all the necessary aggregations on the
queried data and aggregate it based on the configured &lt;code&gt;storage-schemas&lt;/code&gt;, &lt;code&gt;storage-aggregations&lt;/code&gt; and the
&lt;code&gt;max-points-per-req-soft&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;The Graphite process then executes the functions specified in the query on the data it received from the Graphite querier
and returns the result back to the Graphite querier by replying to the original request that the Graphite querier has
forwarded to it.&lt;/p&gt;
&lt;p&gt;The Graphite querier then proxies the response from the Graphite process back to the user.&lt;/p&gt;
&lt;p&gt;We recommend using this image to deploy the Graphite process, because it allows the configuration of the
&lt;code&gt;CLUSTER_SERVERS&lt;/code&gt; via the env var &lt;code&gt;GRAPHITE_CLUSTER_SERVERS&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;docker.io/raintank/graphite-mt:8-7ebde4c60&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A valid configuration example via the environment variables in a Kubernetes deployment looks like this, this assumes
that the Graphite querier is reachable on the address &lt;code&gt;graphite-querier&lt;/code&gt; on port &lt;code&gt;80&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;        - name: GRAPHITE_CLUSTER_SERVERS
          value: graphite-querier:80/graphite&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The chain of calls looks like this, where the &lt;code&gt;graphite-querier&lt;/code&gt; might or might not be the same process:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;client -&amp;gt; graphite-querier -&amp;gt; graphite -&amp;gt; graphite-querier -&amp;gt; GEM Querier&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;querying-the-remote-read-api&#34;&gt;Querying the remote read API&lt;/h2&gt;
&lt;p&gt;For enhanced internal query performance, the Graphite querier performs the queries against the Prometheus remote read API. The only configuration required is the query address, as described in this configuration example:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;    -graphite.querier.query-address: &amp;#39;http://&amp;lt;current server-http-listen-address&amp;gt;:&amp;lt;current server-http-listen-port&amp;gt;&amp;#39;,&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Regarding &lt;code&gt;graphite.querier.query-address&lt;/code&gt; we should make some clarifications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The address should point to the Grafana Mimir query frontend to benefit from its capabilities.&lt;/li&gt;
&lt;li&gt;If the Query Frontend or Grafana Mimir is configured with an http prefix, then you need to add it at the end of the query address to make it consistent. Otherwise, graphite won&amp;rsquo;t be able to query the right querier address.&lt;/li&gt;
&lt;li&gt;If you are running the single binary installation then this address will be &lt;code&gt;localhost&lt;/code&gt; and unless you have a specific http prefix then you can skip the &lt;code&gt;-graphite.querier.query-address&lt;/code&gt; flag entirely.&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="use-the-graphite-querier">Use the Graphite querier&lt;/h1>
&lt;p>The Graphite querier provides the Graphite querying API, for more information about the API refer to the
&lt;a href="https://graphite.readthedocs.io/en/stable/" target="_blank" rel="noopener noreferrer">Graphite documentation&lt;/a>.&lt;/p>
&lt;h2 id="supported-graphite-functions">Supported Graphite functions&lt;/h2>
&lt;p>The Graphite querier comes with a set of built-in functions which tend to execute faster than the original Graphite
implementation, we refer to these built-in functions as the &amp;ldquo;native query engine&amp;rdquo;.
The native query engine only implements a set of very commonly used functions, many of the less commonly used functions
are missing from it.
Whenever the Graphite querier receives a query which includes a function that it doesn&amp;rsquo;t have in the native query engine
it offloads the function processing to a stock Graphite deployment (if available).
To learn more about how the Graphite querier offloads the function processing to a separate Graphite process, refer
to &lt;a href="#offloading-the-function-processing-to-graphite">Offloading the function processing to Graphite&lt;/a>.&lt;/p></description></item><item><title>Use the Graphite write proxy</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_write_proxy/</link><pubDate>Tue, 22 Apr 2025 16:43:47 +0000</pubDate><guid>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/graphite_write_proxy/</guid><content><![CDATA[&lt;h1 id=&#34;use-the-graphite-write-proxy&#34;&gt;Use the Graphite write proxy&lt;/h1&gt;
&lt;p&gt;The Graphite write proxy accepts the ingest requests sent by Carbon-relay-ng and then translates the incoming Graphite
metrics into Prometheus metrics.
Once the metrics have been translated into Prometheus they get forwarded to the GEM distributor, which handles them the
same way as it would handle any other Prometheus &lt;code&gt;remote_write&lt;/code&gt; requests.
The Graphite to Prometheus metric translation differentiates between untagged Graphite metrics and tagged Graphite
metrics, the name mapping scheme for the two looks as following:&lt;/p&gt;
&lt;h5 id=&#34;untagged-graphite-metrics&#34;&gt;Untagged Graphite metrics&lt;/h5&gt;
&lt;p&gt;Graphite metric: &lt;code&gt;some.test.metric&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In Prometheus: &lt;code&gt;graphite_untagged{__n000__=&amp;quot;some&amp;quot;, __n001__=&amp;quot;test&amp;quot;, __n002__=&amp;quot;metric&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;h5 id=&#34;tagged-graphite-metrics&#34;&gt;Tagged Graphite metrics&lt;/h5&gt;
&lt;p&gt;Graphite metric: &lt;code&gt;some.test.metric;my_tag=my_value;another_tag=another_value&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In Prometheus: &lt;code&gt;graphite_tagged{name=&amp;quot;some.test.metric&amp;quot;, my_tag=&amp;quot;my_value&amp;quot;, another_tag=&amp;quot;another_value&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;writing-metrics-with-the-remote-write-api&#34;&gt;Writing metrics with the remote write API&lt;/h2&gt;
&lt;p&gt;For an enhanced internal write performance we&amp;rsquo;re introducing a new feature that will become the default write strategy in future releases. This is a recommended internal tweak for the graphite write proxy to use Grafana Mimir remote write API. To enable it, pass the following configuration:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;
-graphite.write-proxy.remote-write-enabled: &amp;#39;true&amp;#39;,
-graphite.write-proxy.write-endpoint: &amp;#39;http://&amp;lt;distributor-address&amp;gt;:&amp;lt;distributor-port&amp;gt;/api/v1/push&amp;#39;,
-graphite.write-proxy.skip-label-validation: &amp;#39;true&amp;#39;,&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Regarding &lt;code&gt;graphite.write-proxy.write-endpoint&lt;/code&gt; we should make some clarifications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The address should point to the Grafana Mimir distributors.&lt;/li&gt;
&lt;li&gt;If the there is a gateway in front of the distributors with a http prefix, then you need to add it at the end of the query address to make it consistent. Otherwise graphite won&amp;rsquo;t be able to write against the right endpoint.&lt;/li&gt;
&lt;li&gt;If you are running the single binary installation then this address will be &lt;code&gt;http://localhost:&amp;lt;local-installation-port&amp;gt;/api/v1/push&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="use-the-graphite-write-proxy">Use the Graphite write proxy&lt;/h1>
&lt;p>The Graphite write proxy accepts the ingest requests sent by Carbon-relay-ng and then translates the incoming Graphite
metrics into Prometheus metrics.
Once the metrics have been translated into Prometheus they get forwarded to the GEM distributor, which handles them the
same way as it would handle any other Prometheus &lt;code>remote_write&lt;/code> requests.
The Graphite to Prometheus metric translation differentiates between untagged Graphite metrics and tagged Graphite
metrics, the name mapping scheme for the two looks as following:&lt;/p></description></item><item><title>Work with schemas</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/</link><pubDate>Tue, 22 Apr 2025 16:43:47 +0000</pubDate><guid>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/</guid><content><![CDATA[&lt;h1 id=&#34;work-with-schemas&#34;&gt;Work with schemas&lt;/h1&gt;
&lt;p&gt;The schemas define how data is aggregated before the Graphite query functions process the aggregated data.
There are two types of schemas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Storage-schemas define to what interval data should be aggregated and also up to which point in the past data should
be available for querying.&lt;/li&gt;
&lt;li&gt;Aggregation-schemas define what aggregation function should be used to aggregated the data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of the two schemas has a global default which can be configured via the following flags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-graphite.querier.schemas.default-storage-schemas-file&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-graphite.querier.schemas.default-storage-aggregations-file&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the config flag &lt;code&gt;-graphite.querier.schemas.enable-user-overrides&lt;/code&gt; is enabled then the global defaults can be
overridden on a per-tenant basis.&lt;/p&gt;
&lt;h2 id=&#34;querying-and-updating-a-tenants-schemas&#34;&gt;Querying and updating a tenant&amp;rsquo;s schemas&lt;/h2&gt;
&lt;p&gt;Each of the two schema types has an API endpoint that can be used to query it and to update it, the endpoints are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/graphite/config/storageSchemas&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/graphite/config/storageAggregations&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is an example request to the Graphite querier which gets the current storage-schemas of the tenant &lt;code&gt;12345&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;~$ curl -H &amp;#39;X-Scope-OrgId: 12345&amp;#39; http://&amp;lt;graphite-querier&amp;gt;/graphite/config/storageSchemas
[default]
pattern    = .*
intervals  = 0:1s
retentions = 1m:5w

~$&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is an example request to the Graphite querier which updates the storage-schemas of the tenant &lt;code&gt;12345&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;~$ cat storage-schemas.conf
[default]
pattern    = .*
intervals  = 0:1s
retentions = 1m:5w,2h:1y

~$ curl -H &amp;#39;X-Scope-OrgId: 12345&amp;#39; --data-binary &amp;#39;@storage-schemas.conf&amp;#39; http://&amp;lt;graphite-querier&amp;gt;/graphite/config/storageSchemas
~$&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The tenant schemas get stored in the object store bucket configured with the configuration flags prefixed by
&lt;code&gt;-graphite.querier.schemas&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;schema-caching&#34;&gt;Schema caching&lt;/h2&gt;
&lt;p&gt;When a query is received the Graphite querier needs to obtain the tenant&amp;rsquo;s schemas, in order to avoid getting the schema
from the object store for every single query the schemas get cached in the process memory by default for &lt;code&gt;1h&lt;/code&gt;, this is
configurable via &lt;code&gt;-graphite.querier.schemas.schema-ttl&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;proactive-cache-refreshing&#34;&gt;Proactive cache refreshing&lt;/h3&gt;
&lt;p&gt;When the Graphite querier gets a schema from its local cache in order to handle a query it checks whether this cache
entry is already expired and whether it is in the first or in the second half of its cache life time.
If the entry is expired then it doesn&amp;rsquo;t get used and the latest schema gets fetched from the object store, the obtained
schema is then used to handle the query and to populate the cache.
If the entry is not expired but it is already in the second half of its cache life time then the cached value is used,
but at the same time an asynchronous job is kicked off which fetches the latest schema from the object store and then
populates the cache with it.&lt;/p&gt;
&lt;p&gt;This pro-active cache refreshing mechanism prevents that queries ever get blocked on the fetching of the latest schema
from the object store, assuming that the tenant submits queries relatively frequently.
In the case where a tenant only submits queries very infrequently it is possible that their query handling gets blocked
on the fetching of the latest schemas.&lt;/p&gt;
&lt;h2 id=&#34;schema-structure-and-syntax&#34;&gt;Schema structure and syntax&lt;/h2&gt;
&lt;p&gt;Each of the two schema types (&lt;code&gt;storage-schemas&lt;/code&gt;/&lt;code&gt;storage-aggregations&lt;/code&gt;) consists of a list of schemas where each schema
has a &lt;code&gt;pattern&lt;/code&gt; property containing a regular expression.
When the Graphite querier looks up the schema for a given metric it loops over the list of schemas and matches their
patterns against the given metric name, the first schema where the pattern matches is going to be used, this means that
the order of the schemas within the configuration files matters.&lt;/p&gt;
&lt;p&gt;It is common practice that the last schema in each file is named &lt;code&gt;default&lt;/code&gt; and has the pattern &lt;code&gt;.*&lt;/code&gt;, this is a catch-all
which matches all metrics that haven&amp;rsquo;t matched any of the previous schemas.
If there is no &lt;code&gt;default&lt;/code&gt; schema and none of the defined schemas matches, then a hard-coded default is used.&lt;/p&gt;
&lt;h3 id=&#34;storage-schemas&#34;&gt;Storage schemas&lt;/h3&gt;
&lt;p&gt;The format of the storage schemas is very similar to standard Graphite, with two additional parameters.
For the documentation of the format refer to
&lt;a href=&#34;https://graphite.readthedocs.io/en/latest/config-carbon.html#storage-schemas-conf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;storage-schemas.conf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;[apache_busyWorkers]
pattern = ^servers\.www.*\.workers\.busyWorkers$
retentions = 15s:7d,1m:21d,15m:5y&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This example defines that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All metrics matching this regular expression will be available for querying for up to &lt;code&gt;5y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If a query only queries the most recent &lt;code&gt;7d&lt;/code&gt; of data then the data is available at an interval of &lt;code&gt;15s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If a query queries the most recent &lt;code&gt;21d&lt;/code&gt; of data then the data is available at an interval of &lt;code&gt;1m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If a query queries the most recent &lt;code&gt;5y&lt;/code&gt; of data then the data is available at an interval of &lt;code&gt;15m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If a query queries for more than &lt;code&gt;5y&lt;/code&gt;, it will only get data returned for the most recent &lt;code&gt;5y&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;additions-to-the-standard-storage-schemas&#34;&gt;Additions to the standard storage schemas&lt;/h4&gt;
&lt;p&gt;The Graphite querier supports the standard format of Graphite&amp;rsquo;s storage-schemas and storage-aggregation config files.
In addition to the standard properties, it supports two more parameters in the storage-schemas config file which are
specific to the Graphite querier.&lt;/p&gt;
&lt;h5 id=&#34;intervals&#34;&gt;Intervals&lt;/h5&gt;
&lt;p&gt;The &lt;code&gt;intervals&lt;/code&gt; parameter can be used to define a minimum interval with which the data of a certain static time range
must be returned.
This is useful in cases where the &lt;code&gt;retentions&lt;/code&gt; would potentially assign an interval to data that is lower than
the real interval of the stored data, for example because the data in the store has been generated according to an older
version of the storage-schema which has been updated in the meantime.
In such a scenario the gaps between the stored points would get filled with &lt;code&gt;math.NaN&lt;/code&gt; values in order to enforce the
interval defined by the &lt;code&gt;retentions&lt;/code&gt;, which can lead to unexpected query results.&lt;/p&gt;
&lt;p&gt;The intervals parameter is a list of absolute time ranges, each range has an interval associated with it which is the
minimum interval at which this data shall be returned.
Each time range is defined by its beginning and it ends at the beginning of the following time range, the last time
range is open ended into the future.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;100:15s,200:30s,300:15s&lt;/code&gt;
This string defines that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data in the time range &lt;code&gt;100-200&lt;/code&gt; must be returned with an interval of at least &lt;code&gt;15s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The data in the time range &lt;code&gt;200-300&lt;/code&gt; must be returned with an interval of at least &lt;code&gt;30s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The data in the time range &lt;code&gt;300-&amp;lt;unlimited&amp;gt;&lt;/code&gt; must be returned with an interval of at least &lt;code&gt;15s&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep in mind that in Graphite it is a requirement that each metric which gets passed into the Graphite query engine has
a constant interval, this means if a user would query the time range &lt;code&gt;100-500&lt;/code&gt; then the highest minimum interval of that
time range (&lt;code&gt;30s&lt;/code&gt;) would effectively become the minimum interval for the entire queried range.&lt;/p&gt;
&lt;p&gt;The minimum interval that has been determined according to the &lt;code&gt;intervals&lt;/code&gt; parameter is then used in the selection of
the retention interval at which the data shall be returned.&lt;/p&gt;
&lt;p&gt;For example if the &lt;code&gt;intervals&lt;/code&gt; parameter defines a minimum interval of &lt;code&gt;30s&lt;/code&gt; like in the above example and the defined
retentions which could be used for a query are &lt;code&gt;15s:7d,15m:5y&lt;/code&gt; based on the time range then the Graphite querier will
pick the second retention and return the data at an interval of &lt;code&gt;15m&lt;/code&gt;, because even though the first retention would be
valid according to the queried time range its interval is lower than the minimum interval of &lt;code&gt;30s&lt;/code&gt; defined by
&lt;code&gt;intervals&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An example schema with the intervals parameter might look like this:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;[default]
pattern = .*
intervals = 1594166400:30min,1625702400:15s
retentions = 15s:7d,15m:5y&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 id=&#34;applying-retentions-relative-to-end-of-queried-time-range&#34;&gt;Applying retentions relative to end of queried time range&lt;/h4&gt;
&lt;p&gt;The parameter &lt;code&gt;relativeToQuery&lt;/code&gt; is an optional flag which can be added to a storage schema, when it is not defined then
its default value is &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If set to &lt;code&gt;true&lt;/code&gt; then this flag causes that the defined &lt;code&gt;retentions&lt;/code&gt; do not get applied relative to the current wall
clock time, but instead they get applied relative to the end of the queried time range.
This means that for example with a &lt;code&gt;retentions&lt;/code&gt; settings of &lt;code&gt;15s:7d&lt;/code&gt; a time range of &lt;code&gt;7d&lt;/code&gt; can be queried and gets
returned with an interval of &lt;code&gt;15s&lt;/code&gt; even if the query requests the time range from &lt;code&gt;now()-1y-7d&lt;/code&gt; to &lt;code&gt;now()-1y&lt;/code&gt; because
in that situation the &lt;code&gt;7d&lt;/code&gt; retention would get applied relative to &lt;code&gt;now()-1y&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An example schema with this parameter might look like this:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;[default]
pattern = .*
relativeToQuery = true
retentions = 15s:7d,15m:5y&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;storage-aggregations&#34;&gt;Storage aggregations&lt;/h3&gt;
&lt;p&gt;The format of the storage aggregations is exactly the same as in standard Graphite.&lt;/p&gt;
&lt;p&gt;For the documentation of the format refer to
&lt;a href=&#34;https://graphite.readthedocs.io/en/latest/config-carbon.html#storage-aggregation-conf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;storage-aggregation.conf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A valid storage aggregations entry might look like this example:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;[all_min]
pattern = \.min$
xFilesFactor = 0.1
aggregationMethod = min&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="work-with-schemas">Work with schemas&lt;/h1>
&lt;p>The schemas define how data is aggregated before the Graphite query functions process the aggregated data.
There are two types of schemas:&lt;/p></description></item><item><title>Troubleshoot the Graphite proxy</title><link>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/troubleshooting/</link><pubDate>Tue, 22 Apr 2025 16:43:47 +0000</pubDate><guid>https://grafana.com/docs/enterprise-metrics/v2.16.x/manage/graphite/troubleshooting/</guid><content><![CDATA[&lt;h1 id=&#34;troubleshoot-the-graphite-proxy&#34;&gt;Troubleshoot the Graphite proxy&lt;/h1&gt;
&lt;p&gt;You might run into some common issues when running the Graphite Proxy in a production
environment. To learn how to react to each issue, keep reading.&lt;/p&gt;
&lt;h2 id=&#34;metrics-ingestion&#34;&gt;Metrics ingestion&lt;/h2&gt;
&lt;p&gt;Common metrics ingestion issues such as high error rates and high latency.&lt;/p&gt;
&lt;h3 id=&#34;high-error-rate-on-ingest-api-endpoint&#34;&gt;High error rate on ingest API endpoint&lt;/h3&gt;
&lt;p&gt;The query that follows shows the error rate of the ingest requests.
To run the query replace &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; with the string that corresponds to the Grafana Mimir gateway job or the Graphite write proxy job.&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;sum by (status_code) (
  rate(
    cortex_request_duration_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;, route=&amp;#34;graphite_metrics&amp;#34;}[5m]
  )
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Check if the GEM distributors are returning errors to ingest requests submitted by the Graphite write proxy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If so, these errors will be forwarded by the Graphite write proxy.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if Graphite write proxy instances are running out of memory.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If so, either increase their memory limits or increase the number of instances.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look at the logs to find more specific errors.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;high-latency-on-ingest-requests&#34;&gt;High latency on ingest requests&lt;/h3&gt;
&lt;p&gt;The query that follows displays the latency of the ingest requests in percentiles.
In the query, replace &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; with the string that corresponds to the Grafana Mimir gateway job or the Graphite write proxy job.
Also, replace the &lt;code&gt;&amp;lt;percentile&amp;gt;&lt;/code&gt; with the percentile that you want to see.&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;histogram_quantile(
  &amp;lt;percentile&amp;gt;,
  sum by (le) (
    rate(
      cortex_request_duration_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;, route=&amp;#34;graphite_metrics&amp;#34;}[5m]
    )
  )
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Check if the increased latency comes from the GEM distributors to which the Graphite write proxy is sending its ingest requests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the distributor’s ingest latency is high, then this will also raise the ingest latency of the Graphite write proxy. Therefore, you should try to reduce the distributor latency.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scale the Graphite write proxy to a higher number of replicas.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Doing so might reduce the latency in some situations, but it will not help if the distributors are slow at handling the ingest requests coming from the Graphite write proxy.&lt;/p&gt;
&lt;h2 id=&#34;metrics-querying&#34;&gt;Metrics querying&lt;/h2&gt;
&lt;p&gt;This section covers issues related to metrics querying.&lt;/p&gt;
&lt;h3 id=&#34;high-error-rate-on-queries&#34;&gt;High error rate on queries&lt;/h3&gt;
&lt;p&gt;The error rate of queries can be seen with the following query.
The &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; will need to be replaced either by the cortex-gw job or by the Graphite querier job.&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;sum by (status_code) (
  rate(
    cortex_request_duration_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;, route=&amp;#34;graphite_render&amp;#34;}[5m]
  )
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Check if the GEM queriers are returning errors to queries submitted by the Graphite querier.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If so these errors will result in errors being returned to the user.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if Graphite querier instances are running out of memory. If so you have the following options:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Give the Graphite queriers more memory, either by increasing the number of instances or by raising the memory limit of each instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is important to keep in mind that each query gets executed on a single Graphite querier instance. This means that if the rate of queries is low, but each of them consumes really a lot of memory, then increasing the number of Graphite querier instances might not help because each of them might already process only a single query at a time.&lt;/p&gt;
&lt;p&gt;In this situation it would be better to give each instance of the Graphite querier more memory instead of increasing their number.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Reduce the concurrency at which sub-queries are processed in the Graphite querier. To adjust the concurrency you can update the value of the flag &lt;code&gt;-graphite.querier.query-handling-concurrency&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reduce the number of points to which each result gets aggregated. This might reduce the peak memory usage of the query handling process, but its effectiveness depends on the specific queries. To reduce the number of points which the Graphite querier generates you can use the limit defined via the flag &lt;code&gt;-graphite.querier.max-points-per-req-soft&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reject heavy queries. This will result in an error returned to the user if the submitted query was too heavy, but at least it can prevent out of memory conditions, which means that other queries can still be processed. To reject heavy queries you have the following options:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reduce the number of points each query may produce at max, to do so you can use the flag &lt;code&gt;-graphite.querier.max-points-per-req-hard&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leverage the limits which GEM provides, such as &lt;code&gt;max_fetched_series_per_query&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;If a stock Graphite deployment is used for queries that can&amp;rsquo;t be handled by the Graphite querier, check if Graphite runs out of memory.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If so you can either increase its memory limit or the number of Graphite instances. Just like with the Graphite querier each query gets executed on a single Graphite instance, which means that if you already scaled their number to a level where no two queries get executed concurrently in the same instance then further increasing their number won&amp;rsquo;t help because the additional instances will be idle.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look at the logs to find more specific errors.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;high-latency-on-queries&#34;&gt;High latency on queries&lt;/h3&gt;
&lt;p&gt;The latency on queries in percentiles can be seen with the following query.&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;histogram_quantile(
  &amp;lt;percentile&amp;gt;,
  sum by (le) (
    rate(
      cortex_request_duration_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;, route=&amp;#34;graphite_render&amp;#34;}[5m]
    )
  )
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Check if the increased latency comes from the GEM querier which is used by the Graphite querier.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the querier is slow then this will also raise the latency of the query handling in the Graphite querier. In this case, try to improve the querier latency.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if there is a change in the properties of the queries sent to the Graphite querier, which could lead to the queries being slower to process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The following query can be used to see the lengths of the time ranges which are being queried, the string &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; must be replaced with the job of the Graphite querier and &lt;code&gt;&amp;lt;percentile&amp;gt;&lt;/code&gt; must be replaced with the percentile that you want to see:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;histogram_quantile(&amp;lt;percentile&amp;gt;, avg by (le) (rate(graphite_time_range_length_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;}[5m])))&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If there is an increase in this metric then users are querying for longer time ranges, then:&lt;/p&gt;
&lt;p&gt;a. Check the Memcached eviction rate in the metric name cache and the aggregation cache, to ensure that the cache efficiency is not reduced due to evictions.&lt;/p&gt;
&lt;p&gt;If there are lots of evictions then increasing the available memory in Memcached might help.&lt;/p&gt;
&lt;p&gt;It might help to increase the Graphite querier concurrency because this will allow it to process the data required to serve queries in more threads concurrently, but keep in mind that this might increase the memory usage as well so watch out for restarts due to out of memory conditions. To do so use the flag &lt;code&gt;-graphite.querier.query-handling-concurrency&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;b. The following query can be used to see how many series each Graphite query involves before the function processing, the string &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; must be replaced with the job of the Graphite querier and &lt;code&gt;&amp;lt;percentile&amp;gt;&lt;/code&gt; must be replaced with the percentile that you want to see:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;histogram_quantile(&amp;lt;percentile&amp;gt;, avg by (le) (rate(graphite_series_per_query_by_phase_bucket{phase=&amp;#34;passed_into_graphite_functions&amp;#34;, job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;}[5m])))&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If there is an increase in this metric then the queries are requiring more series to be processed per query, it may help to adjust the GEM limit &lt;code&gt;max_series_per_query&lt;/code&gt; to reject the heavy queries.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if more queries are being processed by Graphite instead of the native query engine.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Generally Graphite tends to be much slower than the native query engine, so if queries get processed by Graphite then this can lead to a significant latency increase. To see how many queries get processed by Graphite you can use the following query, the string &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; must be replaced with the Graphite querier&amp;rsquo;s job:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;rate(graphite_proxied_to_graphite{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;}[1m])&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The latency of the Graphite query processing can be seen with the following query, the string &lt;code&gt;&amp;lt;job&amp;gt;&lt;/code&gt; must be replaced with the job of the Graphite querier and &lt;code&gt;&amp;lt;percentile&amp;gt;&lt;/code&gt; must be replaced with the percentile that you want to see:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;histogram_quantile(&amp;lt;percentile&amp;gt;, rate(graphite_proxied_to_graphite_duration_seconds_bucket{job=&amp;#34;&amp;lt;job&amp;gt;&amp;#34;}[1m]))&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this situation it might help to scale Graphite to a higher number of processes, there are two things to keep in mind though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each query is handled by only one instance of the Graphite process, so once you scaled it to a level where no Graphite instance handles more than one query concurrently then further increasing the number of instances will not help because any additional Graphite instances would just be idle.&lt;/li&gt;
&lt;li&gt;Increasing the number of available cores per Graphite process tends to not benefit the latency much because of how Graphite schedules its worker threads. Instead of increasing the cores it is better to increase the number of Graphite instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;query-results-look-different-from-what-you-would-expect&#34;&gt;Query results look different from what you would expect&lt;/h3&gt;
&lt;p&gt;A user reports that they submit a query and they get a result back, but the result looks different from what they&amp;rsquo;d
expect. This could happen due to a wide variety of issues, but one common issue is that the storage-schema or
storage-aggregation configurations are incorrect.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A wrong storage-aggregation configuration can lead to results which look incorrect, a very common mistake is that the used aggregation method is not appropriate for the type of data which gets horizontally aggregated in the query handling.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example a metric of type &lt;code&gt;gauge&lt;/code&gt; should never be summed when horizontally aggregated because the resulting values will not be useful.&lt;/p&gt;
&lt;p&gt;To read more about the storage-aggregation configuration refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/#storage-aggregations&#34;&gt;storage aggregations&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the target interval which gets determined based on the storage-schema configuration is lower than the interval of the raw data stored in Grafana Mimir, then this can also lead to results which may be wrong in various different ways depending on the Graphite functions used in the query.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more information about the storage-schema configuration refer to &lt;a href=&#34;/docs/enterprise-metrics/v2.16.x/manage/graphite/schemas/#storage-schemas&#34;&gt;storage schemas&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="troubleshoot-the-graphite-proxy">Troubleshoot the Graphite proxy&lt;/h1>
&lt;p>You might run into some common issues when running the Graphite Proxy in a production
environment. To learn how to react to each issue, keep reading.&lt;/p></description></item></channel></rss>