<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pyroscope components on Grafana Labs</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/</link><description>Recent content in Pyroscope components on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/index.xml" rel="self" type="application/rss+xml"/><item><title>Grafana Pyroscope compactor</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/compactor/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/compactor/</guid><content><![CDATA[&lt;h1 id=&#34;grafana-pyroscope-compactor&#34;&gt;Grafana Pyroscope compactor&lt;/h1&gt;
&lt;p&gt;The compactor increases query performance and reduces long-term storage usage by combining blocks.&lt;/p&gt;
&lt;p&gt;The compactor is the component responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compacting multiple blocks of a given tenant into a single, optimized larger block. This deduplicates chunks and reduces the size of the index, resulting in reduced storage costs. Querying fewer blocks is faster, so it also increases query speed.&lt;/li&gt;
&lt;li&gt;Keeping the per-tenant bucket index updated. The &lt;a href=&#34;../../bucket-index/&#34;&gt;bucket index&lt;/a&gt; is used by &lt;a href=&#34;../querier/&#34;&gt;queriers&lt;/a&gt; and &lt;a href=&#34;../store-gateway/&#34;&gt;store-gateways&lt;/a&gt; to discover both new blocks and deleted blocks in the storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The compactor is stateless.&lt;/p&gt;
&lt;h2 id=&#34;how-compaction-works&#34;&gt;How compaction works&lt;/h2&gt;
&lt;p&gt;Compaction occurs on a per-tenant basis.&lt;/p&gt;
&lt;p&gt;The compactor runs at regular, configurable intervals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vertical compaction&lt;/strong&gt; merges all the blocks of a tenant uploaded by ingesters for the same time range (1 hour range by default) into a single block. It also deduplicates samples that were originally written to N blocks as a result of replication. Vertical compaction reduces the number of blocks for a single time range from the quantity of ingesters down to one block per tenant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Horizontal compaction&lt;/strong&gt; triggers after a vertical compaction. It compacts several blocks with adjacent range periods into a single larger block. The total size of the associated block chunks does not change after horizontal compaction. The horizontal compaction may significantly reduce the size of the index and the index-header kept in memory by store-gateways.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;compactor-horizontal-and-vertical-compaction.png&#34;
  alt=&#34;Compactor - horizontal and vertical compaction&#34;/&gt;&lt;/p&gt;
&lt;!-- Diagram source at https://docs.google.com/presentation/d/1bHp8_zcoWCYoNU2AhO2lSagQyuIrghkCncViSqn14cU/edit --&gt;
&lt;h2 id=&#34;scaling&#34;&gt;Scaling&lt;/h2&gt;
&lt;p&gt;Compaction can be tuned for clusters with large tenants. Configuration specifies both vertical and horizontal scaling of how the compactor runs as it compacts on a per-tenant basis.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vertical scaling&lt;/strong&gt;&lt;br /&gt;
The setting &lt;code&gt;-compactor.compaction-concurrency&lt;/code&gt; configures the max number of concurrent compactions running in a single compactor instance. Each compaction uses one CPU core.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal scaling&lt;/strong&gt;&lt;br /&gt;
By default, tenant blocks can be compacted by any Grafana Pyroscope compactor. When you enable compactor &lt;a href=&#34;../../../configure-server/configure-shuffle-sharding/&#34;&gt;shuffle sharding&lt;/a&gt; by setting &lt;code&gt;-compactor.compactor-tenant-shard-size&lt;/code&gt; (or its respective YAML configuration option) to a value higher than &lt;code&gt;0&lt;/code&gt; and lower than the number of available compactors, only the specified number of compactors are eligible to compact blocks for a given tenant.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;compaction-algorithm&#34;&gt;Compaction algorithm&lt;/h2&gt;
&lt;p&gt;Pyroscope uses a sophisticated compaction algorithm called split-and-merge.&lt;/p&gt;
&lt;p&gt;By design, the split-and-merge algorithm overcomes time series database (TSDB) index limitations, and it avoids situations in which compacted blocks grow indefinitely for a very large tenant at any compaction stage.&lt;/p&gt;
&lt;p&gt;This compaction strategy is a two-stage process: split and merge.
The default configuration disables the split stage.&lt;/p&gt;
&lt;p&gt;To split, the first level of compaction, for example &lt;code&gt;2h&lt;/code&gt;, the compactor divides all source blocks into &lt;em&gt;N&lt;/em&gt; (&lt;code&gt;-compactor.split-groups&lt;/code&gt;) groups. For each group, the compactor compacts the blocks, but instead of producing a single result block, it outputs &lt;em&gt;M&lt;/em&gt; (&lt;code&gt;-compactor.split-and-merge-shards&lt;/code&gt;) blocks, known as &lt;em&gt;split blocks&lt;/em&gt;. Each split block contains only a subset of the series belonging to a given shard out of &lt;em&gt;M&lt;/em&gt; shards. At the end of the split stage, the compactor produces &lt;em&gt;N * M&lt;/em&gt; blocks with a reference to their respective shard in the block’s &lt;code&gt;meta.json&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The compactor merges the split blocks for each shard. This compacts all &lt;em&gt;N&lt;/em&gt; split blocks of a given shard. The merge reduces the number of blocks from &lt;em&gt;N * M&lt;/em&gt; to &lt;em&gt;M&lt;/em&gt;. For a given compaction time range, there will be a compacted block for each of the &lt;em&gt;M&lt;/em&gt; shards.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;compactor-split-and-merge.png&#34;
  alt=&#34;Compactor - split-and-merge compaction strategy&#34;/&gt;&lt;/p&gt;
&lt;!-- Diagram source at https://docs.google.com/presentation/d/1bHp8_zcoWCYoNU2AhO2lSagQyuIrghkCncViSqn14cU/edit --&gt;
&lt;p&gt;The merge then runs on other configured compaction time ranges, for example 1h and 4h. It compacts blocks belonging to the same shard.&lt;/p&gt;
&lt;p&gt;This strategy is suitable for clusters with large tenants. The number of shards &lt;em&gt;M&lt;/em&gt; is configurable on a per-tenant basis using &lt;code&gt;-compactor.split-and-merge-shards&lt;/code&gt;, and it can be adjusted based on the number of series of each tenant. The more a tenant grows in terms of series, the more you can grow the configured number of shards. Doing so improves compaction parallelization and keeps each per-shard compacted block size under control.&lt;/p&gt;
&lt;p&gt;The number of split groups, &lt;em&gt;N&lt;/em&gt;, can also be adjusted per tenant using the &lt;code&gt;-compactor.split-groups&lt;/code&gt; option. Increasing this value produces more compaction jobs with fewer blocks during the split stage. This allows multiple compactors to work on these jobs, and finish the splitting stage faster. However, increasing this value also generates more intermediate blocks during the split stage, which will only be reduced later in the merge stage.&lt;/p&gt;
&lt;p&gt;If the configuration of &lt;code&gt;-compactor.split-and-merge-shards&lt;/code&gt; changes during compaction, the change will affect only the compaction of blocks which have not yet been split. Already split blocks will use the original configuration when merged. The original configuration is stored in the &lt;code&gt;meta.json&lt;/code&gt; of each split block.&lt;/p&gt;
&lt;p&gt;Splitting and merging can be horizontally scaled. Non-conflicting and non-overlapping jobs will be executed in parallel.&lt;/p&gt;
&lt;h2 id=&#34;compactor-sharding&#34;&gt;Compactor sharding&lt;/h2&gt;
&lt;p&gt;The compactor shards compaction jobs, either from a single tenant or multiple tenants. The compaction of a single tenant can be split and processed by multiple compactor instances.&lt;/p&gt;
&lt;p&gt;Whenever the pool of compactors grows or shrinks, tenants and jobs are resharded across the available compactor instances without any manual intervention.&lt;/p&gt;
&lt;p&gt;Compactor sharding uses a &lt;a href=&#34;../../hash-ring/&#34;&gt;hash ring&lt;/a&gt;. At startup, a compactor generates random tokens and registers itself to the compactor hash ring. While running, it periodically scans the storage bucket at every interval defined by &lt;code&gt;-compactor.compaction-interval&lt;/code&gt;, to discover the list of tenants in storage and to compact blocks for each tenant whose hash matches the token ranges assigned to the instance itself within the hash ring.&lt;/p&gt;
&lt;p&gt;To configure the compactors&amp;rsquo; hash ring, refer to &lt;a href=&#34;../../../configure-server/configuring-memberlist/&#34;&gt;configuring memberlist&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;waiting-for-a-stable-hash-ring-at-startup&#34;&gt;Waiting for a stable hash ring at startup&lt;/h3&gt;
&lt;p&gt;A cluster cold start or an increase of two or more compactor instances at the same time may result in each new compactor instance starting at a slightly different time. Then, each compactor runs its first compaction based on a different state of the hash ring. This is not an error condition, but it may be inefficient, because multiple compactor instances may start compacting the same tenant at nearly the same time.&lt;/p&gt;
&lt;p&gt;To mitigate the issue, compactors can be configured to wait for a stable hash ring at startup. A ring is considered stable if no instance is added to or removed from the hash ring for at least &lt;code&gt;-compactor.ring.wait-stability-min-duration&lt;/code&gt;. The maximum time the compactor will wait is controlled by the flag &lt;code&gt;-compactor.ring.wait-stability-max-duration&lt;/code&gt; (or the respective YAML configuration option). Once the compactor has finished waiting, either because the ring stabilized or because the maximum wait time was reached, it will start up normally.&lt;/p&gt;
&lt;p&gt;The default value of zero for &lt;code&gt;-compactor.ring.wait-stability-min-duration&lt;/code&gt; disables waiting for ring stability.&lt;/p&gt;
&lt;h2 id=&#34;compaction-jobs-order&#34;&gt;Compaction jobs order&lt;/h2&gt;
&lt;p&gt;The compactor allows configuring of the compaction jobs order via the &lt;code&gt;-compactor.compaction-jobs-order&lt;/code&gt; flag (or its respective YAML config option). The configured ordering defines which compaction jobs should be executed first. The following values of &lt;code&gt;-compactor.compaction-jobs-order&lt;/code&gt; are supported:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;smallest-range-oldest-blocks-first&lt;/code&gt; (default)&lt;/p&gt;
&lt;p&gt;This ordering gives priority to smallest range, oldest blocks first.&lt;/p&gt;
&lt;p&gt;For example, with compaction ranges &lt;code&gt;1h, 4h, 8h&lt;/code&gt;, the compactor will compact the 1h ranges first, and among them give priority to the oldest blocks. Once all blocks in the 1h range have been compacted, it moves to the 2h range, and finally to 8h one.&lt;/p&gt;
&lt;p&gt;All split jobs are moved to the front of the work queue, because finishing all split jobs in a given time range unblocks the merge jobs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;newest-blocks-first&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This ordering gives priority to the most recent time ranges first, regardless of their compaction level.&lt;/p&gt;
&lt;p&gt;For example, with compaction ranges &lt;code&gt;1h, 4h, 8h&lt;/code&gt;, the compactor compacts the most recent blocks first (up to the 8h range), and then moves to older blocks. This policy favours the most recent blocks, assuming they are queried the most frequently.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;blocks-deletion&#34;&gt;Blocks deletion&lt;/h2&gt;
&lt;p&gt;Following a successful compaction, the original blocks are deleted from the storage. Block deletion is not immediate; it follows a two-step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An original block is marked for deletion; this is a soft delete&lt;/li&gt;
&lt;li&gt;Once a block has been marked for deletion for longer than the configurable &lt;code&gt;-compactor.deletion-delay&lt;/code&gt;, the block is deleted from storage; this is a hard delete&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The compactor is responsible for both marking blocks and for hard deletion.
Soft deletion is based on a small &lt;code&gt;deletion-mark.json&lt;/code&gt; file stored within the block location in the bucket.&lt;/p&gt;
&lt;p&gt;The soft delete mechanism gives queriers and store-gateways time to discover the new compacted blocks before the original blocks are deleted. If those original blocks were immediately hard deleted, some queries involving the compacted blocks could temporarily fail or return partial results.&lt;/p&gt;
&lt;h2 id=&#34;compactor-disk-utilization&#34;&gt;Compactor disk utilization&lt;/h2&gt;
&lt;p&gt;The compactor needs to download blocks from the bucket to the local disk, and the compactor needs to store compacted blocks to the local disk before uploading them to the bucket. The largest tenants may need a lot of disk space.&lt;/p&gt;
&lt;p&gt;Assuming &lt;code&gt;max_compaction_range_blocks_size&lt;/code&gt; is the total block size for the largest tenant during the longest &lt;code&gt;-compactor.block-ranges&lt;/code&gt; period, the expression that estimates the minimum disk space required is:&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;compactor.compaction-concurrency * max_compaction_range_blocks_size * 2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;compactor-configuration&#34;&gt;Compactor configuration&lt;/h2&gt;
&lt;p&gt;Refer to the &lt;a href=&#34;../../../configure-server/reference-configuration-parameters/#compactor&#34;&gt;compactor&lt;/a&gt;
block section and the &lt;a href=&#34;../../../configure-server/reference-configuration-parameters/#limits&#34;&gt;limits&lt;/a&gt; block section for details of compaction-related configuration.&lt;/p&gt;
]]></content><description>&lt;h1 id="grafana-pyroscope-compactor">Grafana Pyroscope compactor&lt;/h1>
&lt;p>The compactor increases query performance and reduces long-term storage usage by combining blocks.&lt;/p>
&lt;p>The compactor is the component responsible for:&lt;/p>
&lt;ul>
&lt;li>Compacting multiple blocks of a given tenant into a single, optimized larger block. This deduplicates chunks and reduces the size of the index, resulting in reduced storage costs. Querying fewer blocks is faster, so it also increases query speed.&lt;/li>
&lt;li>Keeping the per-tenant bucket index updated. The &lt;a href="../../bucket-index/">bucket index&lt;/a> is used by &lt;a href="../querier/">queriers&lt;/a> and &lt;a href="../store-gateway/">store-gateways&lt;/a> to discover both new blocks and deleted blocks in the storage.&lt;/li>
&lt;/ul>
&lt;p>The compactor is stateless.&lt;/p></description></item><item><title>Pyroscope distributor</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/distributor/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/distributor/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-distributor&#34;&gt;Pyroscope distributor&lt;/h1&gt;
&lt;p&gt;The distributor is a stateless component that receives profiling data from the agent.
The distributor then divides the data into batches and sends it to multiple &lt;a href=&#34;../ingester/&#34;&gt;ingesters&lt;/a&gt; in parallel, shards the series among ingesters, and replicates each series by the configured replication factor. By default, the configured replication factor is three.&lt;/p&gt;
&lt;h2 id=&#34;validation&#34;&gt;Validation&lt;/h2&gt;
&lt;p&gt;The distributor cleans and validates data that it receives before writing the data to the ingesters.
Because a single request can contain valid and invalid profiles, samples, metadata, and exemplars, the distributor only passes valid data to the ingesters. The distributor does not include invalid data in its requests to the ingesters.
If the request contains invalid data, the distributor returns a 400 HTTP status code and the details appear in the response body.
The details about the first invalid data are typically logged by the agent.&lt;/p&gt;
&lt;p&gt;The distributor data cleanup includes the following transformation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the profile has a timestamp set, if not it will default to the time the distributor received the profile.&lt;/li&gt;
&lt;li&gt;The distributor will remove samples that are having values of &lt;code&gt;0&lt;/code&gt; and will sum samples that share the same stacktrace.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;replication&#34;&gt;Replication&lt;/h2&gt;
&lt;p&gt;The distributor shards and replicates incoming series across ingesters.
You can configure the number of ingester replicas that each series is written to via the &lt;code&gt;-distributor.replication-factor&lt;/code&gt; flag, which is &lt;code&gt;1&lt;/code&gt; by default.
Distributors use consistent hashing, in conjunction with a configurable replication factor, to determine which ingesters receive a given series.&lt;/p&gt;
&lt;p&gt;Sharding and replication uses the ingesters&amp;rsquo; hash ring.
For each incoming series, the distributor computes a hash using the profile name, labels, and tenant ID.
The computed hash is called a &lt;em&gt;token&lt;/em&gt;.
The distributor looks up the token in the hash ring to determine which ingesters to write a series to.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&#34;../../hash-ring/&#34;&gt;hash ring&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;quorum-consistency&#34;&gt;Quorum consistency&lt;/h4&gt;
&lt;p&gt;Because distributors share access to the same hash ring, write requests can be sent to any distributor. You can also set up a stateless load balancer in front of it.&lt;/p&gt;
&lt;p&gt;To ensure consistent query results, Pyroscope uses &lt;a href=&#34;https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Dynamo-style&lt;/a&gt; quorum consistency on reads and writes.
The distributor waits for a successful response from &lt;code&gt;n&lt;/code&gt;/2 &#43; 1 ingesters, where &lt;code&gt;n&lt;/code&gt; is the configured replication factor, before sending a successful response to the Agent push request.&lt;/p&gt;
&lt;h2 id=&#34;load-balancing-across-distributors&#34;&gt;Load balancing across distributors&lt;/h2&gt;
&lt;p&gt;We recommend randomly load balancing write requests across distributor instances.
If you&amp;rsquo;re running Pyroscope in a Kubernetes cluster, you can define a Kubernetes &lt;a href=&#34;https://kubernetes.io/docs/concepts/services-networking/service/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Service&lt;/a&gt; as ingress for the distributors.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A Kubernetes Service balances TCP connections across Kubernetes endpoints and does not balance HTTP requests within a single TCP connection.
If you enable HTTP persistent connections (HTTP keep-alive), because the Agent uses HTTP keep-alive, it re-uses the same TCP connection for each push HTTP request.
This can cause distributors to receive an uneven distribution of push HTTP requests.&lt;/p&gt;&lt;/blockquote&gt;
]]></content><description>&lt;h1 id="pyroscope-distributor">Pyroscope distributor&lt;/h1>
&lt;p>The distributor is a stateless component that receives profiling data from the agent.
The distributor then divides the data into batches and sends it to multiple &lt;a href="../ingester/">ingesters&lt;/a> in parallel, shards the series among ingesters, and replicates each series by the configured replication factor. By default, the configured replication factor is three.&lt;/p></description></item><item><title>Pyroscope ingester</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/ingester/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/ingester/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-ingester&#34;&gt;Pyroscope ingester&lt;/h1&gt;
&lt;p&gt;The ingester is a stateful component that writes incoming profiles first to &lt;a href=&#34;../../about-grafana-pyroscope-architecture/#long-term-storage&#34;&gt;on disk storage&lt;/a&gt; on the write path and returns series samples for queries on the read path.&lt;/p&gt;
&lt;p&gt;Incoming profiles from &lt;a href=&#34;../distributor/&#34;&gt;distributors&lt;/a&gt; are not immediately written to the long-term storage but are either kept in the ingester&amp;rsquo;s memory or offloaded to the ingester&amp;rsquo;s disk.
Eventually, all profiles are written to disk and periodically uploaded to the long-term storage.
For this reason, the &lt;a href=&#34;../querier/&#34;&gt;queriers&lt;/a&gt; might need to fetch samples from both ingesters and long-term storage while executing a query on the read path.&lt;/p&gt;
&lt;p&gt;Any Pyroscope component that calls the ingesters starts by first looking up ingesters registered in the &lt;a href=&#34;../../hash-ring/&#34;&gt;hash ring&lt;/a&gt; to determine which ingesters are available.
Each ingester could be in one of the following states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PENDING&lt;/code&gt;&lt;br /&gt;
The ingester has just started. While in this state, the ingester does not receive write or read requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JOINING&lt;/code&gt;&lt;br /&gt;
The ingester starts up and joins the ring. While in this state, the ingester does not receive write or read requests.
The ingester loads tokens from disk (if &lt;code&gt;-ingester.ring.tokens-file-path&lt;/code&gt; is configured) or generates a set of new random tokens.
Finally, the ingester optionally observes the ring for token conflicts, and once resolved, moves to the &lt;code&gt;ACTIVE&lt;/code&gt; state.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ACTIVE&lt;/code&gt;&lt;br /&gt;
The ingester is up and running. While in this state, the ingester can receive both write and read requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LEAVING&lt;/code&gt;&lt;br /&gt;
The ingester is shutting down and leaving the ring. While in this state, the ingester doesn&amp;rsquo;t receive write requests, but can still receive read requests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNHEALTHY&lt;/code&gt;&lt;br /&gt;
The ingester has failed to heartbeat to the hash ring. While in this state, distributors bypass the ingester, which means that the ingester does not receive write or read requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To configure the ingesters&amp;rsquo; hash ring, refer to &lt;a href=&#34;../../../configure-server/configuring-memberlist/&#34;&gt;configuring memberlist&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;ingesters-write-de-amplification&#34;&gt;Ingesters write de-amplification&lt;/h2&gt;
&lt;p&gt;Ingesters store recently received samples in-memory in order to perform write de-amplification.
If the ingesters immediately write received samples to the long-term storage, the system would have difficulty scaling due to the high pressure on the long-term storage.
For this reason, the ingesters batch and compress samples in-memory and periodically upload them to the long-term storage.&lt;/p&gt;
&lt;p&gt;Write de-amplification is the main source of Pyroscope&amp;rsquo;s low total cost of ownership (TCO).&lt;/p&gt;
&lt;h2 id=&#34;ingesters-failure-and-data-loss&#34;&gt;Ingesters failure and data loss&lt;/h2&gt;
&lt;p&gt;If an ingester process crashes or exits abruptly, all the in-memory profiles
that have not yet been uploaded to the long-term storage could be lost. There
are the following ways to mitigate this failure mode:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replication&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;replication&#34;&gt;Replication&lt;/h3&gt;
&lt;p&gt;By default, each profile series is replicated to three ingesters. Writes to the
Pyroscope cluster are successful if a quorum of ingesters received the data, which
is a minimum of 2 with a replication factor of 3. If the Pyroscope cluster loses an
ingester, the in-memory profiles held by the head block of the lost ingester
are available at least in one other ingester. In the event of a single ingester
failure, no profiles are lost. If multiple ingesters fail, profiles might be
lost if the failure affects all the ingesters holding the replicas of a
specific profile series.&lt;/p&gt;
]]></content><description>&lt;h1 id="pyroscope-ingester">Pyroscope ingester&lt;/h1>
&lt;p>The ingester is a stateful component that writes incoming profiles first to &lt;a href="../../about-grafana-pyroscope-architecture/#long-term-storage">on disk storage&lt;/a> on the write path and returns series samples for queries on the read path.&lt;/p></description></item><item><title>Pyroscope querier</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/querier/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/querier/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-querier&#34;&gt;Pyroscope querier&lt;/h1&gt;
&lt;p&gt;The querier is a stateless component that evaluates query expressions by fetching profiles series and labels on the read path.&lt;/p&gt;
&lt;p&gt;The querier uses the &lt;a href=&#34;../ingester/&#34;&gt;ingesters&lt;/a&gt; for gathering recently written data and the [store-gateways] for the &lt;a href=&#34;../../about-grafana-pyroscope-architecture/#long-term-storage&#34;&gt;long-term storage&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;connecting-to-ingesters&#34;&gt;Connecting to ingesters&lt;/h3&gt;
&lt;p&gt;You must configure the querier with the same &lt;code&gt;-ingester.ring.*&lt;/code&gt; flags (or their respective YAML configuration parameters) that you use to configure the ingesters so that the querier can access the ingester hash ring and discover the addresses of the ingesters.&lt;/p&gt;
&lt;h2 id=&#34;querier-configuration&#34;&gt;Querier configuration&lt;/h2&gt;
&lt;p&gt;For details about querier configuration, refer to &lt;a href=&#34;../../../configure-server/reference-configuration-parameters/#querier&#34;&gt;querier&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="pyroscope-querier">Pyroscope querier&lt;/h1>
&lt;p>The querier is a stateless component that evaluates query expressions by fetching profiles series and labels on the read path.&lt;/p>
&lt;p>The querier uses the &lt;a href="../ingester/">ingesters&lt;/a> for gathering recently written data and the [store-gateways] for the &lt;a href="../../about-grafana-pyroscope-architecture/#long-term-storage">long-term storage&lt;/a>.&lt;/p></description></item><item><title>Pyroscope Store-gateway</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/store-gateway/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/store-gateway/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-store-gateway&#34;&gt;Pyroscope Store-gateway&lt;/h1&gt;
&lt;p&gt;The store-gateways in Pyroscope are responsible for looking up profiling data in the &lt;a href=&#34;../../about-grafana-pyroscope-architecture/#long-term-storage&#34;&gt;long-term storage&lt;/a&gt; bucket. A single store-gateway is responsible for a subset of the blocks in the long-term storage and will be involved by a [querier].&lt;/p&gt;
&lt;h2 id=&#34;store-gateway-configuration&#34;&gt;Store-gateway configuration&lt;/h2&gt;
&lt;p&gt;For details about store-gateway configuration, refer to &lt;a href=&#34;../../../configure-server/reference-configuration-parameters/#store_gateway&#34;&gt;store-gateway&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="pyroscope-store-gateway">Pyroscope Store-gateway&lt;/h1>
&lt;p>The store-gateways in Pyroscope are responsible for looking up profiling data in the &lt;a href="../../about-grafana-pyroscope-architecture/#long-term-storage">long-term storage&lt;/a> bucket. A single store-gateway is responsible for a subset of the blocks in the long-term storage and will be involved by a [querier].&lt;/p></description></item><item><title>Pyroscope query-frontend</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/query-frontend/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/query-frontend/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-query-frontend&#34;&gt;Pyroscope query-frontend&lt;/h1&gt;
&lt;p&gt;The query-frontend is a stateless component that provides the same API as the &lt;a href=&#34;../querier/&#34;&gt;querier&lt;/a&gt; and can be used to accelerate the read path and ensure fair scheduling between tenants using the &lt;a href=&#34;../query-scheduler/&#34;&gt;query-scheduler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this situation, queriers act as workers that pull jobs from the queue, execute them, and return the results to the query-frontend for aggregation.&lt;/p&gt;
&lt;p&gt;We recommend that you run at least two query-frontend replicas for high-availability reasons.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because the &lt;a href=&#34;../query-scheduler/&#34;&gt;query-scheduler&lt;/a&gt; is a mandatory component when using the query-frontend, you must run at least one query-scheduler replica.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The following steps describe how a query moves through the query-frontend.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A query-frontend receives a query.&lt;/li&gt;
&lt;li&gt;The query-frontend places the query in a queue by communicating with the query-scheduler, where it waits to be picked up by a querier.&lt;/li&gt;
&lt;li&gt;A querier picks up the query from the queue and executes it.&lt;/li&gt;
&lt;li&gt;A querier or queriers return the result to query-frontend, which then aggregates and forwards the results to the client.&lt;/li&gt;
&lt;/ol&gt;
]]></content><description>&lt;h1 id="pyroscope-query-frontend">Pyroscope query-frontend&lt;/h1>
&lt;p>The query-frontend is a stateless component that provides the same API as the &lt;a href="../querier/">querier&lt;/a> and can be used to accelerate the read path and ensure fair scheduling between tenants using the &lt;a href="../query-scheduler/">query-scheduler&lt;/a>.&lt;/p></description></item><item><title>Pyroscope query-scheduler</title><link>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/query-scheduler/</link><pubDate>Wed, 08 Apr 2026 14:38:28 +0000</pubDate><guid>https://grafana.com/docs/pyroscope/v1.18.x/reference-pyroscope-architecture/components/query-scheduler/</guid><content><![CDATA[&lt;h1 id=&#34;pyroscope-query-scheduler&#34;&gt;Pyroscope query-scheduler&lt;/h1&gt;
&lt;p&gt;The query-scheduler is a stateless component that retains a queue of queries to execute, and distributes the workload to available &lt;a href=&#34;../querier/&#34;&gt;queriers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The query-scheduler is a required component when using the &lt;a href=&#34;../query-frontend/&#34;&gt;query-frontend&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;query-scheduler-architecture.png&#34;
  alt=&#34;Query-scheduler architecture&#34;/&gt;&lt;/p&gt;
&lt;p&gt;The following flow describes how a query moves through a Pyroscope cluster:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;a href=&#34;../query-frontend/&#34;&gt;query-frontend&lt;/a&gt; receives queries, and then either splits and shards them, or serves them from the cache.&lt;/li&gt;
&lt;li&gt;The query-frontend enqueues the queries into a query-scheduler.&lt;/li&gt;
&lt;li&gt;The query-scheduler stores the queries in an in-memory queue where they wait for a querier to pick them up.&lt;/li&gt;
&lt;li&gt;Queriers pick up the queries, and executes them.&lt;/li&gt;
&lt;li&gt;The querier sends results back to query-frontend, which then forwards the results to the client.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;benefits-of-using-the-query-scheduler&#34;&gt;Benefits of using the query-scheduler&lt;/h2&gt;
&lt;p&gt;Query-scheduler enables the scaling of query-frontends. To learn more, see Mimir &lt;a href=&#34;/docs/mimir/latest/operators-guide/architecture/components/query-frontend/#why-query-frontend-scalability-is-limited&#34;&gt;Query Frontend&lt;/a&gt; documentation.&lt;/p&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;To use the query-scheduler, query-frontends and queriers need to discover the addresses of query-scheduler instances.
To advertise itself, the query-scheduler uses Ring-based service discovery which is configured via the &lt;a href=&#34;../../../configure-server/configuring-memberlist/&#34;&gt;memberlist configuration&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;operational-considerations&#34;&gt;Operational considerations&lt;/h2&gt;
&lt;p&gt;For high-availability, run two query-scheduler replicas.&lt;/p&gt;
]]></content><description>&lt;h1 id="pyroscope-query-scheduler">Pyroscope query-scheduler&lt;/h1>
&lt;p>The query-scheduler is a stateless component that retains a queue of queries to execute, and distributes the workload to available &lt;a href="../querier/">queriers&lt;/a>.&lt;/p>
&lt;p>The query-scheduler is a required component when using the &lt;a href="../query-frontend/">query-frontend&lt;/a>.&lt;/p></description></item></channel></rss>