Controller
Most of the properties in Pinot are set in Zookeeper via Helix. However, you can also set properties in controller.conf file. You can specify the path to configuration file at the startup time as follows:
bin/pinot-admin.sh StartController -configFileName /path/to/controller.confController.conf can have the following properties.
Duplicate Keys in Configuration File
Starting from Apache Pinot 1.3.0, duplicate keys in the configuration file will cause a ConfigurationException to be thrown during startup. Previously, duplicate keys would be silently merged into a list. If you encounter this error, ensure that each configuration property appears only once in your configuration file. The exception will include the exact file path, duplicate key name, and the line numbers where the duplicates were found.
Example error:
ConfigurationException: Duplicate key found in /path/to/controller.conf at line 10 and line 15: controller.portPrimary configuration
controller.vip.host
same as controller.host
The VIP hostname used to set the download URL for segments
controller.vip.port
same as controller.port
controller.vip.protocol
controller.host
localhost
The ip of the host on which controller is running
controller.port
9000
The port on which controller should run
controller.access.protocol
controller.data.dir
${java.io.tmpdir}/PinotController
Directory to host segment data
controller.local.temp.dir
controller.zk.str
localhost:2181
zookeeper host:port string to connect
controller.update_segment_state_model
false
controller.helix.cluster.name
Pinot Cluster Name, required.
cluster.tenant.isolation.enable
true
Enable Tenant Isolation, default is single tenant cluster. If enabled, each server or broker added into the cluster will be tagged with DefaultTenant.
enforce.pool.based.assignment
false
When true, the controller validates table configs to require both pool-based instance assignment and replica-group-based assignment in instanceAssignmentConfigMap. Dimension tables and tables that reuse pre-existing instancePartitionsMap assignments are excluded from this check.
controller.enable.split.commit
false
(Deprecated) Enable split commit protocol for real-time segment commit.
controller.query.console.useHttps
false
use https instead of http for cluster
controller.upload.onlineToOfflineTimeout
2 minutes
controller.mode
dual
Should be one of helix_only, pinot_only or dual
controller.resource.rebalance.strategy
org.apache.helix.controller. rebalancer.strategy.AutoRebalanceStrategy
controller.resource.rebalance.delay_ms
5 minutes
How long the helix waits to rebalance partitions after a controller is lost. Longer time values allow for more time for a controller to recover. Shorter time values prevent cluster operations like segment sealing from being blocked.
controller.realtime.segment.commit.timeoutSeconds
120 seconds
request timeout for segment commit
controller.executor.numThreads
not set (-1)
Size of the controller async executor used for background controller work such as multiget helpers. If unset or set to 0/negative, Pinot uses a cached thread pool. If set to a positive value, Pinot uses a fixed-size thread pool.
controller.executor.rebalance.numThreads
not set (-1)
Size of the dedicated controller executor used for table and tenant rebalance work. If unset or set to 0/negative, Pinot uses a cached thread pool. If set to a positive value, Pinot uses a fixed-size thread pool.
controller.deleted.segments.retentionInDays
7 days
duration for which to retain deleted segments
controller.ingestFromURI.allowLocalFileSystem
false
When true, the /ingestFromURI controller API may read controller-local files through file:///... URIs or LocalPinotFS. Leave this false unless you explicitly trust controller-local ingestion inputs.
controller.lineage.exclusive.delete.enabled
true
When true, Pinot keeps segments that are still locked by a live lineage entry on the lineage-managed cleanup path instead of deleting them through the generic segment delete flow. Set to false to restore the legacy delete behavior.
controller.admin.access.control.factory.class
org.apache.pinot.controller. api.access.AllowAllAccessFactory
controller.segment.upload.timeoutInMillis
10 minutes
timeout for upload of segments.
controller.startup.continueWithoutDeepStore
false
When true, the controller continues startup even if Pinot fails to validate or create controller.data.dir through PinotFS during boot. When false, the controller fails fast on that startup check.
controller.startup.exitOnTableConfigCheckFailure
true
When true, the controller exits during startup if any table is missing its TableConfig. When false, the controller stays up and reports the missing-table-config count via tableWithoutTableConfigCount.
controller.startup.exitOnSchemaCheckFailure
true
When true, the controller exits during startup if any table is missing its Schema. When false, the controller stays up and reports the missing-schema count via tableWithoutSchemaCount.
controller.realtime.segment.metadata.commit.numLocks
64
controller.enable.storage.quota.check
true
controller.storage.factory.class.file
org.apache.pinot.spi. filesystem.LocalPinotFS
table.minReplicas
1
controller.access.protocols
http
Ingress protocols to access controller (http or https or http,https)
controller.access.protocols.http.port
Port to access controller via http
controller.broker.protocols.https.port
Port to access controller via https
controller.broker.protocol
http
protocol for forwarding query requests (http or https)
controller.broker.port.override
override for broker port when forwarding query requests (use in multi-ingress scenarios)
controller.tls.keystore.path
Path to controller TLS keystore
controller.tls.keystore.password
keystore password
controller.tls.truststore.path
Path to controller TLS truststore
controller.tls.truststore.password
truststore password
controller.tls.client.auth
false
toggle for requiring TLS client auth
pinot.controller.http.server.thread.pool.corePoolSize
2 * cores
Config for the thread-pool used by pinot-controller's http-server.
pinot.controller.http.server.thread.pool.maxPoolSize
2 * cores
Config for the thread-pool used by pinot-controller's http-server.
pinot.controller.http.server.max.http.header.size
unset (Grizzly default)
Maximum total HTTP header size in bytes accepted by the controller's Grizzly HTTP/HTTPS listeners. Set this when upstream proxies, auth gateways, or clients send larger headers than the Grizzly default allows.
pinot.controller.http.server.max.request.headers
unset (Grizzly default)
Maximum number of HTTP headers accepted by the controller's Grizzly HTTP/HTTPS listeners.
pinot.controller.segment.fetcher.http.client.maxConnTotal
Config for the http-client used by HttpSegmentFetcher for downloading segments
pinot.controller.segment.fetcher.http.client.maxConnPerRoute
Config for the http-client used by HttpSegmentFetcher for downloading segments
If you temporarily disable either startup check to keep the controller running while you repair legacy tables, watch the controller gauges tableWithoutTableConfigCount and tableWithoutSchemaCount to confirm the invalid-table count returns to zero.
Pinot does not support enabling Helix batch message mode through controller.conf. Controllers now disable batch message mode on controller-managed IdealStates when those resources are created or updated, so any legacy controller.enable.batch.message.mode setting has no effect.
Periodic task configuration
The following controller periodic tasks can be tuned independently.
When a task documents both frequencyPeriod and cronExpression, Pinot uses the Quartz cronExpression if it is set and falls back to the fixed-delay frequencyPeriod otherwise. Invalid cron expressions fail controller startup during periodic-task scheduler initialization. Tasks without a cronExpression entry remain fixed-delay only.
Cron expression examples
Controller periodic-task cron schedules use Quartz syntax, with seconds as the first field: second minute hour day-of-month month day-of-week [year]. Use ? in either the day-of-month or day-of-week field when that field does not constrain the schedule. Pinot evaluates the schedule in the controller JVM's time zone, so keep controller time zones consistent when using wall-clock schedules.
Every 5 minutes
0 0/5 * * * ?
Every hour
0 0 * * * ?
Every day at 02:30
0 30 2 * * ?
Every Sunday at 03:00
0 0 3 ? * SUN
Weekdays at 01:15
0 15 1 ? * MON-FRI
Add the cron expression to controller.conf under the task-specific key:
Do not include quote characters in a Java properties controller.conf value. If the same config is rendered through YAML or another format where spaces are significant, quote the whole cron string in that outer format only. To return a task to fixed-delay scheduling, remove or clear its *.cronExpression value and set the corresponding *.frequencyPeriod.
BrokerResourceValidationManager
This task rebuilds the BrokerResource if the instance set has changed.
controller.broker.resource.validation.frequencyPeriod
1h
controller.broker.resource.validation.cronExpression
unset
controller.broker.resource.validation.initialDelayInSeconds
between 2m-5m
ResponseStoreCleaner
This task removes expired cursor response-store entries from brokers. Cursor expiration itself is controlled by the broker setting pinot.broker.cursor.response.store.expiration.
controller.cluster.response.store.cleaner.frequencyPeriod
1h
controller.cluster.response.store.cleaner.initialDelay
between 2m-5m
StaleInstancesCleanupTask
This task periodically cleans up stale Pinot broker/server/minion instances. It is disabled by default.
controller.stale.instances.cleanup.task.frequencyPeriod
-1s
controller.stale.instances.cleanup.task.cronExpression
unset
controller.stale.instances.cleanup.task.initialDelaySeconds
between 2m-5m
controller.stale.instances.cleanup.task.minOfflineTimeBeforeDeletionPeriod
1h
OfflineSegmentIntervalChecker
This legacy interval controls how often OfflineSegmentValidationManager runs expensive segment-level validation, including missing-segment and offline-push-delay metrics. It does not have its own cron expression; use controller.offline.segment.validation.cronExpression to cron-schedule the offline validation task.
controller.offline.segment.interval.checker.frequencyPeriod
24h
controller.statuschecker.waitForPushTimePeriod
10m
controller.offlineSegmentIntervalChecker.initialDelayInSeconds
between 2m-5m
OfflineSegmentValidationManager
This task validates offline tables and updates offline segment validation metrics. Segment-level validation is throttled by controller.segment.level.validation.intervalPeriod; if controller.offline.segment.interval.checker.frequencyPeriod is also configured, Pinot uses the lower of the two intervals for segment-level validation.
controller.offline.segment.validation.frequencyPeriod
1h
controller.offline.segment.validation.cronExpression
unset
controller.segment.level.validation.intervalPeriod
24h
PinotTaskManager
This task schedules and manages Pinot minion tasks. It periodically generates tasks for each table based on the task configurations defined in the table config. Common minion tasks include MergeRollupTask, RealtimeToOfflineSegmentsTask, SegmentGenerationAndPushTask, and ConvertToRawIndexTask.
controller.task.frequencyPeriod
1h
controller.task.manager.initialDelayInSeconds
between 2m-5m
controller.task.scheduler.enabled
false
controller.task.enableDistributedLocking
false
controller.task.concurrentSchedulingEnabled
false
controller.task.scheduler.enabled enables cron-based task schedules from table configs.
controller.task.enableDistributedLocking enables the ZooKeeper-backed lock that PinotTaskManager uses to coordinate task generation for the same table across controllers.
controller.task.concurrentSchedulingEnabled changes the cluster default for task scheduling from the legacy controller-wide serialized path to a concurrent path. When enabled, task generation for different tables can run in parallel, while same-table coordination relies on distributed locking. Enable controller.task.enableDistributedLocking before enabling concurrent scheduling.
RealtimeSegmentValidationManager
This task validates the ideal state and segment zk metadata of real-time tables by doing the following:
fixing any partitions which have stopped consuming
starting consumption from new partitions
uploading segments to deep store if segment download url is missing
This task ensures that the consumption of the real-time tables gets fixed and keeps going when met with erroneous conditions.
This task does not fix consumption stalled due to
CONSUMING segment being deleted
Kafka OOR exceptions
controller.realtime.segment.validation.frequencyPeriod
1h
controller.realtime.segment.validation.cronExpression
unset
controller.realtime.segment.validation.initialDelayInSeconds
between 2m-5m
controller.realtime.segment.deepStoreUploadRetryEnabled
false
controller.realtime.segment.deepStoreUploadRetry.timeoutMs
-1
controller.realtime.segment.deepStoreUploadRetry.parallelism
1
controller.segment.error.autoReset
false
controller.realtime.segment.partialOfflineReplicaRepairEnabled
false
controller.segment.disaster.recovery.mode
DEFAULT
controller.segment.error.autoReset controls whether the controller's periodic offline and realtime segment validation tasks automatically reset segments in ERROR state so they can retry normal recovery. Pinot leaves this disabled by default.
When controller.realtime.segment.partialOfflineReplicaRepairEnabled is enabled, the controller's periodic validation task automatically resets OFFLINE replicas back to CONSUMING for IN_PROGRESS segments that have a mix of CONSUMING and OFFLINE replicas. This handles cases where a replica's startup fails (for example, due to Kafka consumer initialization errors) while other replicas continue normally. Enable only after verifying that OFFLINE replicas are caused by transient failures rather than persistent errors.
controller.segment.disaster.recovery.mode sets the cluster-wide default disaster-recovery policy used by the controller's realtime segment validation task for pauseless ingestion. Supported values are DEFAULT and ALWAYS, matching the table-level disasterRecoveryMode setting in streamIngestionConfig. You can set this in controller.conf, or update the same key through cluster configs so the controller picks up the new mode without restart.
RealtimeOffsetAutoResetManager
This task runs offset-auto-reset backfill work for real-time tables when realtime segment offset auto reset is enabled in the table ingestion config.
controller.realtime.offsetAutoReset.backfill.enabled
false
controller.realtime.offsetAutoReset.backfill.frequencyPeriod
1h
controller.realtime.offsetAutoReset.backfill.cronExpression
unset
controller.realtime.offsetAutoReset.backfill.initialDelayInSeconds
between 2m-5m
RealtimeConsumerMonitor
This task fetches consuming-segment information for real-time tables and emits per-partition records-lag and availability-lag controller gauges. It is disabled by default.
controller.realtimeConsumerMonitor.frequencyPeriod
-1s
controller.realtimeConsumerMonitor.cronExpression
unset
controller.realtimeConsumerMonitor.initialDelayInSeconds
between 2m-5m
RetentionManager
This task manages retention of segments for all tables. During the run, it looks at the retentionTimeUnit and retentionTimeValue inside the segmentsConfig of every table, and deletes segments which are older than the retention. The deleted segments are moved to a DeletedSegments folder colocated with the dataDir on segment store, and permanently deleted from that folder in a configurable number of days.
controller.retention.frequencyPeriod
6h
Frequency at which the retention manager runs
controller.retention.cronExpression
unset
Quartz cron schedule for the retention manager. When set, Pinot ignores controller.retention.frequencyPeriod.
controller.retentionManager.initialDelayInSeconds
between 2m-5m
Randomized initial delay before first retention manager run
controller.retentionManager.enableCreationTimeFallback
false
If true, use segment creation time as fallback when end time is invalid/missing, enabling cleanup of such segments; dynamic config, no restart needed
controller.deleted.segments.retentionInDays
7d
Duration for which to retain deleted segments
controller.enable.hybrid.table.retention.strategy
false
If true, use hybrid retention strategy for hybrid tables
If controller.enable.hybrid.table.retention.strategy is set to true, the retention manager will use the hybrid retention strategy. In this strategy, the retention manager calculates a time boundary by looking at the maximum end time of offline segments and the segment ingestion frequency specified for the OFFLINE table. Segments older than the time boundary are deleted from the REALTIME table. The time boundary calculation is as mentioned in the Time Boundary section. This type of strategy is useful to prevent data loss when there are failures in moving the data from REALTIME table to OFFLINE table.
When the offline part of the hybrid table is unused or not-required, it's recommended to delete the OFFLINE table. Otherwise, the REALTIME table will keep growing and the retention manager will not be able to delete the segments.
SegmentRelocator
This task is applicable only if you have tierConfig or tagOverrideConfig. It runs rebalance in the background to
relocate COMPLETED segments to tag overrides
relocate ONLINE segments to tiers if tier configs are set
At most one replica is allowed to be unavailable during rebalance unless you override the periodic rebalance defaults below.
controller.segment.relocator.frequencyPeriod
1h
How often the segment relocator scans tables and triggers relocation work.
controller.segment.relocator.cronExpression
unset
Quartz cron schedule for the segment relocator. When set, Pinot ignores controller.segment.relocator.frequencyPeriod.
controller.segmentRelocator.initialDelayInSeconds
between 2m-5m
Randomized startup delay before the first segment relocator run.
controller.segment.relocator.bestEfforts
true
Whether the periodic rebalance should continue in best-effort mode if the no-downtime contract cannot be fully achieved.
controller.segment.relocator.minAvailableReplicas
-1
Minimum replicas to keep available during no-downtime rebalance. Negative values mean the maximum number of replicas that may be unavailable.
controller.segment.relocator.reassignInstances
false
Whether the periodic rebalance should recompute instance assignment before rebalancing segments.
controller.segment.relocator.bootstrap
false
Whether the periodic rebalance should bootstrap newly added servers during relocation.
controller.segment.relocator.downtime
false
Whether the periodic rebalance is allowed to use downtime mode instead of enforcing no-downtime movement.
SegmentStatusChecker
This task manages segment status metrics such as realtimeTableCount, offlineTableCount, disableTableCount, numberOfReplicas, percentOfReplicas, percentOfSegments, idealStateZnodeSize, idealStateZnodeByteSize, segmentCount, segmentsInErrorState, tableCompressedSize.
It also emits the per-table TABLE_TENANT_INFO gauge for tenant attribution. Pinot registers one gauge per (tenantType, tenant) pair and sets each value to 1: server tenant, broker tenant, and each tier server tenant derived from the table's tier configs. With the bundled controller JMX exporter config, Prometheus exposes these gauges with table, tableType, tenantType, and tenant labels so you can join tenant ownership onto other table-scoped metrics.
controller.statuschecker.frequencyPeriod
5m
controller.statuschecker.cronExpression
unset
controller.statusChecker.initialDelayInSeconds
between 2m-5m
RebalanceChecker
Currently, table rebalance triggered by user runs at best effort. It could fail if the controller running it got restarted; or some servers were not stable, making the rebalance timed out while waiting for external view to converge with ideal state, etc. This task checks for failed rebalance and retry them automatically, up to certain times as configured.
controller.rebalance.checker.frequencyPeriod
5m
controller.rebalance.checker.cronExpression
unset
controller.rebalanceChecker.initialDelayInSeconds
between 2m-5m
TenantRebalanceChecker
This task checks tenant rebalance jobs that may be stuck after a controller crash or restart and resumes unfinished table-rebalance work.
controller.tenant.rebalance.checker.frequencyPeriod
5m
controller.tenant.rebalance.checker.cronExpression
unset
controller.tenant.rebalance.checker.initialDelayInSeconds
between 2m-5m
TaskMetricsEmitter
This task periodically emits controller metrics about minion tasks, including task counts by state. It also publishes the per-table, per-task-type gauges MAX_SUBTASK_WAIT_TIME_MS and MAX_SUBTASK_RUNNING_TIME_MS. Pinot rewrites both gauges every emit cycle and sets them to 0 when no matching subtasks are in WAITING or RUNNING, so alerts based on these gauges self-resolve after the queue drains.
controller.minion.task.metrics.emitter.frequencyPeriod
5m
controller.minion.task.metrics.emitter.cronExpression
unset
ResourceUtilizationChecker
The periodic task ResourceUtilizationChecker runs periodically and fetches disk-usage information from Pinot server instances via GET /instance/diskUtilization.
controller.enable.resource.utilization.check
false
Master switch for enforcing resource-utilization checks during real-time ingestion validation and minion task generation.
controller.enable.all.resource.utilization.checkers
true
Registers all resource-utilization checkers for backward compatibility.
controller.enable.disk.utilization.checker
false
Registers the disk-utilization checker when controller.enable.all.resource.utilization.checkers is false.
controller.resource.utilization.checker.frequency
300
Value is in seconds. Setting the value to -1 disables the periodic checker task.
controller.resource.utilization.checker.initial.delay
300 (or 0 when collect.usage.at.startup is true)
Initial delay for the resource-utilization checker in seconds. Automatically set to 0 when controller.resource.utilization.checker.collect.usage.at.startup is enabled.
controller.resource.utilization.checker.collect.usage.at.startup
false
When set to true, the controller waits for the resource utilization checker (disk usage cache) to be populated before marking itself as healthy. This prevents segment push requests from being accepted when disk usage is already high (e.g., 90%) immediately after a restart. When enabled, controller.resource.utilization.checker.initial.delay is automatically set to 0 so the checker runs immediately. If the checker times out, the controller still becomes healthy (fail-open).
controller.disk.utilization.threshold
0.95
Disk-usage threshold expressed as a fraction between 0 and 1.
controller.disk.utilization.check.timeoutMs
30000
Timeout in milliseconds for collecting disk-usage responses from servers.
controller.disk.utilization.path
/home/pinot/data
Compatibility config sent by the controller when requesting disk usage. The current server endpoint ignores this value and always measures the server instanceDataDir.
Last updated
Was this helpful?

