Skip to main content
To improve disk storage and query performance, we recommend merging segments in real-time Pinot tables using the Minion merge rollup task. To learn how to merge segments in real-time Pinot tables, watch the following video, or complete the tutorial below.
If your use case supports aggregating data, you may also want to rollup segments in real-time tables. For more information, check out the following video.
You can also merge segments in offline tables. For more information see the merge small segments in offline tables guide

Prerequisites

To follow the code examples in this guide, you must install Docker locally and download recipes.
  1. If you haven’t already, download recipes.
  2. In terminal, go to the recipe by running the following command:

Launch Pinot Cluster

You can spin up a Pinot Cluster by running the following command:
This command will run a single instance of the Pinot Controller, Pinot Server, Pinot Broker, and Zookeeper. You can find the docker-compose.yml file on GitHub.

Controller configuration

The Pinot controller is launched with the following custom configuration:
controller-conf.conf This configuration is needed to enabled Pinot’s task scheduler, which we’ll use to automatically merge segments.
We’ve configured the task scheduler to run every 5 minutes, but you’d set that to an hour or more in a production system.

Dataset

We’ve got the following data generator that generates data in bursts depending on the minute of the hour:
datagen.py If the minute is divisible by 2, we generate around 20,000 messages per second and if it isn’t we generate around 100 messages per second. An example of a message produced by this script is shown below:
We can write that data into Kafka by running the following command:

Pinot Schema and Table

Now let’s create a Pinot Schema and Table. First, the schema:
config/schema.json We’ll also have the following table config:
config/table.json There are a few bits of config that we’re interested in. First up is segment threshold, which is defined like so:
This means that a segment will be committed once it contains 1m records or every minute, whichever comes first. For more on configuring the segment threshold, see the segment threshold guide. Next is the batchIngestionConfig:
This shouldn’t be strictly necessary because it’s a config that’s usually used for offline tables, but the merge/roll-up logic in the current version (0.12.1) relies on it being there. The reliance on this config existing has already been fixed in the main branch and therefore this config won’t be required when 0.13.0 is released.
We’re also interested in the MergeRollupTask, which is extracted below:
This configuration will bucket records from the same 5 minute period and will only process records with a timestamp from more than 2 minutes ago.
We are intentionally using very small values for the bucketTimePeriod and bufferTimePeriod for the purposes of this example. You’ll want to use larger values for production systems.
You can create the table and schema by running the following command:
Remove the -arm64 suffix if you’re not using a Mac M1/M2.

Viewing segments

We can navigate to the Pinot UI and run the following query to see the segments that have been created and the number of records that they contain:
We’ll see something like the following output: Query Results

Merge segments

The job to merge segments runs every 5 minutes, so if we wait a little while it will eventually start.
If you want to manually trigger the merge segments job, see the merge segments section of the merge small segments guide.
We can check the Pinot Controller logs to see that it’s been triggered:
Output
And we can check the Pinot Minion logs to see if the job has run:
Output
Let’s now run the segments query again: We can see that the merge job has rolled up segments 0-4 into two larger segments, containing data from 5 minute windows. If we wait a few more minutes, the next time it runs it will roll up the rest of the data for the 11:20 to 11:25 window, as well as some of the next window. We can see the output of the next few runs below: