Set the segment age boundary
Set a segment age boundary to split data across your local and remote tiers. For example, if you have 30 days of data, and you set the segment age boundary at 7 days, segments less than 7 days old will be on the local tier, and segments older than 7 days are moved to the remote tier.Set up a tenant
Do one of the following:- (Recommended) Set up a server as a dedicated tenant
- (Default) Use the same server as your table as your tenant. We do not recommend this option for production environments.
Set up a dedicated tenant server (Recommended)
Create a new tenant to serve as a compute-only node. This tenant will be dedicated to serving the segments which are on the remote tier backend. These do not require as much local storage as your table’s default local tenant since the data is stored remotely. To add one or more dedicated tenants, do the following:- Add servers. You’ll need at least as many servers as in your table replication setting in the Pinot table configuration.
- Tag these as
RemoteTenant_OFFLINE(or whatever you prefer) using the/instances/{instanceName}/updateTagsAPI in the Swagger UI on the host where Pinot is running. This link is only accessible when Pinot is running.
Use same tenant as your table (Default)
We don’t recommend this option for production environments. This option is selected by default. StarTree will use the same tenant that you set intableConfig->tenants->server in your Pinot table configuration. This option is a no-op, requiring you to do nothing.
Processing segments from the cloud object store causes higher than usual memory and CPU utilization on nodes, and can affect local data query performance. If your local data queries are serving latency sensitive, user-facing traffic, we recommend using a separate server as your tenant.
Add cluster configs and restart servers
To enable tiered storage, do one of the following:- Add the configs as part of the server instance configs or
- From the Swagger UI on the host where Pinot is running, use the
POST /cluster/configsAPI to add the following cluster-level tier storage configurations.
The
pinot.server.instance.segment.cache.directory enables caching to restart the server more quickly, but does not affect the query execution speed. The two ondemand configs are shown here for their importance in query performance. More information about these configs others can be found in the sections below. You may want to set some of them before restarting servers.Update the Pinot table configuration
- If not already set, set in the Pinot table configuration.
- Add
tierConfigsto your Pinot table configuration (example below). ThesegmentAgewill be calculated using the primarytimeColumnNameas set in Pinot table configuration.
SegmentRelocator, will check and migrate segments to the proper tier, such as when the segments cross the segmentAge. See the SegmentRelocator configuration documentation for more information.
This
segmentAge setting is independent of the table’s retention. Segments will continue to be forgotten from Pinot once the table’s retention is reached, regardless of what tier the segments are on.Tiered storage with Google Cloud Storage (GCS)
Tiered storage with Google Cloud Storage (GCS) is supported using the Cloud Storage XML API. This API lets you access Pinot segments stored in a GCS bucket using a S3 client. To use a GCS bucket as the tier backend, complete the following steps:- To use GCS’s interoperable APIs with a S3 client, you must generate HMAC keys for the GCP bucket. Each HMAC key consists of an access ID and a secret key and can be associated with any service account.
- For testing purposes, you can store the HMAC key pair in
tierBackendProperties, as shown in the following example:
- Enable the GCP secret manager API, and then add the key pair as secrets (one for the access ID and the other for the secret key).
- Ensure that the service account used to create the cluster has access to read the added secrets.
-
Set the following properties in
tierBackendProperties:Thegcpprojectidandgcpkeypathare required to be able to access the GCP secret manager. Thegcpkeypathshould be set to the absolute path where the gcp key is mounted on the server (typically/home/pinot/gcp/credentials.json).
Upload new segments and migrate existing segments
When uploading new segments to the table, the segments are put on the remote tier if their data is older than the specifiedsegmentAge. For existing segments, the SegmentRelocator task moves them to the remote tier periodically as configured.
Validate and query
For segments that move to the remote tenant, verify the following:- From the Swagger UI on the host where Pinot is running use the
segments/{tableName}/tiersAPI to check segments’ current tier - Segments are not present on the pinot-server’s local disk (in dataDir and indexDir)
- Segments are present in the remote tier backend in uncompressed form
Fine tunings for query speed
You have the option to use the following tuning adjustments to speed up queries.Preloaded index
Pin some index types of some columns on the local disk to avoid frequent remote reads. For example, pin bloom filters on local disk so that segments are pruned first to reduce the number of segments processed, minimizing remote reads. Bloom filters tend to be compact, not affecting local disk usage much. All index types can be pinned, including the tree nodes inside the star-tree index. Decide what to pin based on the need for cost savings and reduced query latencies. Indexes to be preloaded can be specifed at a cluster level globally, and can be overriden at a table level by setting a table level override either via cluster configuration or table configuration.- To enable the buffer preloading, use cluster config apis to add the following option to your cluster configuration:
- Once enabled, set global buffer preloading using the following cluster configuration:
<columnName>.<indexType> convention to specify the index type to preload. Use * as a wildcard for the column name to preload all index buffers of a given index type.
- To override the global cluster level configuration, specify the configuration option on the table level:
- To specify the override in the cluster configuration, set the
pinot.server.instance.buffer.reference.manager.preload.index.keys.overridecluster configuration option as follows:
tableNameWithType like foo_OFFLINE.
- To specify the override in the table configuration, set the
preload.index.keys.overrideproperty insidetierBackendPropertiesas follows:
- Both clusterConfig and tableConfig changes for preloading index as said above can be applied by reloading segments. Btw, restarting servers also works if wanted.
Automatic caching (Experimental)
Caching is different from preloading an index. You can decide what to cache and evict from local disk automatically, based on commonly accessed segment data by ongoing queries. If queries find cached index data, the remote reads can be saved. Automatic caching is specified as part of the cluster configuration like this:
Because these are cluster configs, any changes will require you to restart the servers.
On demand access
If index data isn’t found in local cache, StarTree accesses the data from the remote tier backend on demand during query execution.Using cache alone does not ensure stable and predicable query performance, particularly when the amount of data in the remote tier backend is more than the size of local disk by multiple orders. But the size of each Pinot segment is limited, and at any time only a limited number of segments are processed, which lets you configure the memory space for on demand access accordingly.
ondemand space is an important configuration for query performance. If it’s too small, it may become a bottleneck to prefetch segment data effectively or to fully parallelize the remote reads. The reserved size is to ensure query execution can always proceed, so it should be larger than the max segment size in the table. If query execution tends to wait for ondemand space, you should increase the reserved space. You’ve seen these two configs above in Add cluster configs and restart servers.
Because these are cluster configs, any changes will require you to restart the servers.
Access methods
There are three methods for on demand data access: prefetch, read ahead, and block cache prefetch read. Prefetching happens asynchronously before segment processing but it gets the index data as a whole. Reading ahead happens on demand during segment processing synchronously, which adds I/O waiting time onto query latency directly, but only to get the index data requested by the query plus a small amount of bytes ahead, reading just a few small chunks from the index data. By default, all remote data is obtained via prefetching. But based on the query, such as the indexes accessed, you can enablereadAhead to speed up the query. The access methods can be turned on/off as a query option, making it easy to experiment with both access methods, as in this example:
tierBackendProperties, it will apply to all queries towards this table.
- For columns in non-predicate clauses (such as
projection,groupBy, andorderBy): if they are dictionary encoded, their dictionaries are prefetched; but in any case, their forward index data is accessed via thereadAheadmethod, because only part of the data is needed to evaluate the clauses. - For columns in predicate clauses (such as a where clause): if they have no index to be used to evaluate the predicates, their forward index data is prefetched as a whole, because the whole forward index data is scanned to evaluate the predicates anyway; otherwise, their index data (like an inverted index, range index or json index) is accessed via the readAhead method, because only part of them is needed to evaluate the predicates.
Change of these configs do not require you to restart servers, because they are set as query options.
Forward Block Index
Forward block index is a small utility index that provides lookup capability on forward indexes without scanning the entire column. It helps Pinot identify the exact block or blocks needed for a given row range, improving read efficiency, particularly under tiered storage. It works together with block cache prefetch to enhance data locality and reduce amount of remote data to read. When using block cache prefetch, enable the forward block index on raw forward index columns. The block size should align with the chunk size used by the raw forward index (a block size of 1000 is a good default). Here’s an example configuration:*.forward_block to the pinot.server.instance.buffer.reference.manager.preload.index.keys cluster configuration. Additionally, set pinot.server.instance.index.sparse.enabled to true in the cluster config to enable prefetching support.
Star-tree index
Enable the star-tree index with theenable.startree.index configuration, in tierBackendProperties (shown below). Changes here require you to reload the table or restart the servers. This is disabled by default.
readAhead method during query.
Here is an example to preload the tree nodes of StarTree Index. The count suffix is aligned with the order of star-ree index configurations set in the table configuration.
Tier overwrites
The performance/cost requirements can be very different for data on the local and remote tiers, requiring different index across tiers. For example, if you want to add a bloom filter, inverted index, and star-tree index for segments on the local tier to lower the query latency, and just add bloom filters on the remote tier to save cost. To configure indexes according to tiers, see Overwrite index configs at tier level.S3 client configs
The S3 client can also be tuned, for example, to tolerate longer request latency. However, the default of the S3 configurations should work in most cases. In case there is a need, below is the list of current configs to customize when S3 clients fetch data from remote segments. All those configs are put insidetierBackendProperties. Most of them need to reload the table or restart servers to take effect.
Pinot supports both sync and async s3 client.
By default, sync s3 client is used and a thread pool is created to fetch data via the sync s3 clients in parallel, without blocking the query processing threads. The configs for sync s3 client are prefixed with s3client.http.
The async s3 client can reduce the size of the thread pool considerably. It uses async I/O to fetch data in parallel in a non-blocking manner; and uses the thread pool mentioned above to process the I/O completion callbacks only. The configs for the async s3 client are prefixed with s3client.asynchttp. Configs prefixed with s3client.general apply to both kinds of clients.
Review the related configuration options here:
Configs for ease of operations
You have the option to use the following adjustments to simplify operations.Server restart optimizations
When tiered storage is enabled, server restarts can take a lot longer than otherwise. This is primarily due to servers having to make multiple object store calls when loading a segment. This high restart time can be problematic especially during upgrades and server outages. To alleviate this, enable segment header caching by setting thepinot.server.instance.segment.cache.directory cluster configuration to a path. This path must be different from the dataDir. Setting the property leads to segment metadata files, such as creation.meta, metadata.properties, and index_map, along with certain byte slices (specifically, the header bytes of each of the index buffers) of the columns.psf file being cached onto the disk.
These cached files are used during server startup and eliminate the expensive object store calls, which helps in reducing the overall server startup time.
This feature only helps with server restarts and not when adding a new server since no cached files will be present when a server comes up for the first time.
Minion Tasks
Segment reload or refresh operations can be resource-intensive, especially with tiered storage enabled tables, due to heavy remote fetches and uploads. It’s recommended to offload this overhead from Pinot servers to minions. This can be achieved by enabling"directTierUpload": "true" in the individual table’s minion task config and adding the cluster config "pinot.server.instance.table.data.manager.provider.class" : "ai.startree.pinot.data.tier.table.StarTreeDefaultTableDataManagerProvider" but will be made default in later releases.
A server restart is required for the cluster config to take effect.
Note
As of now, File ingestion or Segment import task is currently not supported.POST /tasks/schedule API call.
Stale segment directory cleanup
With the implementation of segment directory level MVCC, a new segment directory will be created in the object store whenever a segment is reloaded to apply changes in the schema or table configs. By default, these directories are not automatically removed, potentially resulting in a significant utilization of object storage space to store outdated segment directories that correspond to previous schema or table configs. To address this issue, periodic cleanup of these stale segment directories can be enabled by adding the following cluster config:All of the cleanup configs below are dynamic cluster configs — the controller picks up changes to them without a restart. The one exception is
controller.tieredStorage.segment.cleanup.frequencyPeriod, which is read when the periodic task is scheduled at controller startup, so changing the schedule does require a controller restart.
The periodic task is always scheduled at
frequencyPeriod, but it is a no-op until controller.tieredStorage.segment.cleanup.enabled is set to true. When cleanup is disabled, each run logs Tiered storage segment directory cleanup is disabled. Skipping cleanup task. and exits without touching the tier backend.
Increasing controller.tieredStorage.segment.cleanup.tombstoneTtlMillis allows more time for reusing old segment directories if table config reverts. However, it prolongs the persistence of stale directories in the object store.
controller.tieredStorage.segment.cleanup.tombstoneToDeletionTtlMillis config specifies the wait time before deleting a tombstoned directory. This prevents the wrongful deletion of a segment directory if any server starts using the segment directory concurrently during the tombstoning process.
Monitoring metrics
Below are metrics we emit to help understand how tiered storage works. The metrics mainly cover how:- Segments get uploaded to the tier backend, such as data volume and operation duration, rates or failures
- Queries fetch data from the remote segments, such as data volume and query duration breakdown over query operators, query rates or failures
- Segment data is kept on servers temporarily, such as data volume from segments temporarily held in memory or on disk
In addition to the metrics emitted from tiered storage modules, the cpu util and network bandwidth util are important to help you fine tune the system.

