Class ExternalSourceSettings
MAX_CONCURRENT_REQUESTS) and the throttle retry budget are read at node startup — the concurrency
bound sizes both the per-scheme permit semaphores and the backend SDK HTTP connection pools when they are built,
the retry budget is read when the storage-provider registry initializes — so a change to either takes effect
after a node restart.
Covers three areas: the external-read concurrency bound (the single MAX_CONCURRENT_REQUESTS knob, which
sizes the in-flight-read permit semaphore and the SDK connection pools below it); reactive throttle handling for
object stores (the retry duration budget — throttling is handled by backoff, not a concurrency cap); and
glob/listing safety limits (max discovered files, max brace expansion) to prevent degenerate queries from
overwhelming storage backends.
-
Field Summary
FieldsModifier and TypeFieldDescriptionGates provisioning data sources that use workload-identity federation (e.g.Allowlist of local filesystem root paths from which ES|QLfile://external sources are permitted to read.Enablesauth=managed_identityfor external data source reads, which resolves credentials from the node's ambient cloud identity (IAM instance profile / IMDS on AWS and Azure, GCE metadata server on GCP) rather than requiring explicit credentials in the query or datasource.The single external-read concurrency knob, per scheme, per node.Upper bound on how many stream-only-compressed (gzip/zstd) segmentators may occupy theesql_external_iopool at once, across all queries and operators on the node.Hard cap on the number of files that glob expansion will collect before aborting.Cap on the number of concrete paths generated by brace expansion before falling back to listing.Maximum total time (in seconds) to spend retrying throttled cloud API requests before giving up.Deprecated former name forMANAGED_IDENTITY_ENABLED. -
Method Summary
Modifier and TypeMethodDescriptionstatic intblobStoreConcurrency(Settings settings) The effective per-node blob-store access concurrency that every external access path reads, so one knob governs metadata discovery and data reads alike: the operator'sMAX_CONCURRENT_REQUESTSvalue when set, otherwise the CPU-bounddefaultBlobStoreConcurrency(Settings)default.static intdefaultBlobStoreConcurrency(int allocatedProcessors) The default per-node concurrency for accessing an external blob store, derived from the node's allocated processors using thesnapshot_metathread pool's sizing shape (processors * 3), clamped to[16,100].static intdefaultBlobStoreConcurrency(Settings settings) Convenience overload resolving allocated processors from the given settings.static intexternalIoThreads(Settings settings) Thread count for the dedicatedesql_external_iopool (EsqlPlugin).static intmaxConcurrentSegmentators(Settings settings) Effective cap on concurrent stream-only-compressed segmentators for a node, resolvingMAX_CONCURRENT_SEGMENTATORS(0= derive) and clamping the result to[1, poolSize - 1]so at least oneesql_external_iothread always remains for the one-shot parser tasks that a segmentator depends on.settings()
-
Field Details
-
MAX_CONCURRENT_REQUESTS
The single external-read concurrency knob, per scheme, per node. It sizes both the per-scheme permit semaphore that bounds in-flight data reads (StorageProviderRegistry) and the backend SDK HTTP connection pools (S3 NettymaxConcurrency, Azure reactor-nettyConnectionProvider), and — viablobStoreConcurrency(Settings)— the metadata-discovery fan-out. Set to 0 to disable permit-based concurrency limiting entirely.The default is CPU-bound rather than a fixed literal:
defaultBlobStoreConcurrency(Settings)— thesnapshot_metasizing shape (allocatedProcessors * 3) clamped to[16,100]. That scales in-flight reads with node size so a wide fan-out over many small blobs is not self-throttled by a low fixed cap, while the floor keeps small nodes from collapsing to a handful of permits and the ceiling bounds a single store's load. Operators can raise it (up to 500) for high-throughput clusters or lower it when a store throttles.Static (
Setting.Property.NodeScope): the value sizes the per-scheme semaphores and SDK pools when they are built and there is no settings-update consumer to resize a liveSemaphoreor connection pool, so a change takes effect after a node restart. -
MAX_CONCURRENT_SEGMENTATORS
Upper bound on how many stream-only-compressed (gzip/zstd) segmentators may occupy theesql_external_iopool at once, across all queries and operators on the node. Each open streaming read runs one long-lived segmentator that pins a pool thread while it blocks on its chunk/buffer queues and the upstream decompress stream; its per-chunk parser tasks run on the same pool. Without a cap, enough concurrently-open reads pin every pool thread and their parser tasks — queued behind the segmentators — never get a thread, deadlocking the reads (elastic/esql-planning #1093, item 4). Bounding concurrent segmentators below the pool size guarantees threads remain free for parser tasks.Default
0means "derive": the effective cap tracks theesql_external_iopool size (externalIoThreads(Settings)), clamped topoolSize - 1so at least one pool thread always remains for parser tasks. An explicit positive value overrides the derivation (still clamped below the pool size). Node-scoped: the pool is sized at startup. -
THROTTLE_MAX_RETRY_DURATION
Maximum total time (in seconds) to spend retrying throttled cloud API requests before giving up. Bounds the cumulative retry duration regardless of the retry count, ensuring queries fail cleanly when throttling is persistent rather than blocking until the HTTP request timeout fires. Default: 30 seconds. Set to 0 to disable the duration budget (retry count only). -
MAX_DISCOVERED_FILES
Hard cap on the number of files that glob expansion will collect before aborting. Protects against degenerate globs (e.g.s3://bucket/*) on large buckets. Default: 10,000 — generous for legitimate use, catches truly degenerate cases. -
MAX_GLOB_EXPANSION
Cap on the number of concrete paths generated by brace expansion before falling back to listing. Prevents patterns like{1,2,...,10000}from generating excessive HeadObject calls. Default: 100 — above this, the system uses normal listing instead of per-key checks. -
WORKLOAD_IDENTITY_ENABLED
Deprecated former name forMANAGED_IDENTITY_ENABLED. Still honored for backwards compatibility — it is the fallback source for the new key, so an operator's existingesql.datasource.workload_identity.enabledconfig keeps working — and emits a deprecation warning when set. PreferMANAGED_IDENTITY_ENABLED. -
MANAGED_IDENTITY_ENABLED
Enablesauth=managed_identityfor external data source reads, which resolves credentials from the node's ambient cloud identity (IAM instance profile / IMDS on AWS and Azure, GCE metadata server on GCP) rather than requiring explicit credentials in the query or datasource.Disabled by default. Must be explicitly enabled by an operator on self-hosted, single-cloud, single-tenant deployments where the node's ambient identity is the intended credential. Never enable in serverless or multi-tenant deployments: ambient credentials bypass tenant isolation.
This is an operator-dynamic setting: changes take effect immediately without a node restart. When this key is not set, it falls back to the deprecated
WORKLOAD_IDENTITY_ENABLEDkey's value, so reads through this setting see an operator's pre-rename configuration. -
FEDERATED_IDENTITY_ENABLED
Gates provisioning data sources that use workload-identity federation (e.g. S3role_arn, GCSsts_audience, Azuretenant_id/client_id). A single setting covers every file-based provider, since they all funnel throughFileDataSourceValidatorand share theDataSourceConfiguration#hasFederatedAuth()mechanism.Disabled by default. This is an operator-dynamic setting: changes take effect immediately without a node restart, and it can only be changed by an operator (e.g. via file-based settings), not by an end user through the public
_cluster/settingsAPI — so it can be enabled fleet-wide (or per-deployment) via gitops without exposing a customer-facing toggle. -
LOCAL_ALLOWED_PATHS
Allowlist of local filesystem root paths from which ES|QLfile://external sources are permitted to read. Mirrorspath.repo: the list is the enable — an empty list (the default) disables local-disk reads entirely. When non-empty, afile://path is allowed only if it normalizes to a location under one of the listed roots;..-escapes and anything outside every root are rejected.This is a node-scope setting; a node restart is required for changes to take effect.
-
-
Method Details
-
defaultBlobStoreConcurrency
public static int defaultBlobStoreConcurrency(int allocatedProcessors) The default per-node concurrency for accessing an external blob store, derived from the node's allocated processors using thesnapshot_metathread pool's sizing shape (processors * 3), clamped to[16,100]. This is the single source of truth for blob-store access concurrency so metadata discovery and data retrieval stay consistent: both are latency-bound I/O against object stores and should scale the same way with node size rather than each picking an ad-hoc constant. The floor keeps small nodes from self-throttling (and from sizing theesql_external_iopool too small to run the parse pipeline); the ceiling bounds a single store's load. -
defaultBlobStoreConcurrency
Convenience overload resolving allocated processors from the given settings. -
blobStoreConcurrency
The effective per-node blob-store access concurrency that every external access path reads, so one knob governs metadata discovery and data reads alike: the operator'sMAX_CONCURRENT_REQUESTSvalue when set, otherwise the CPU-bounddefaultBlobStoreConcurrency(Settings)default. The data-read path bounds in-flight reads with a per-scheme permit semaphore sized by this value (StorageProviderRegistry), and the metadata-discovery fan-out (TransportEsqlQueryAction.externalSourceConcurrency()) uses the same value — so an operator override reaches both paths. -
externalIoThreads
Thread count for the dedicatedesql_external_iopool (EsqlPlugin). Sized to exactly the single concurrency knobblobStoreConcurrency(Settings)— no headroom — because every blocking task that lands on this pool is a permit-gated reader (remote schemes) or is bounded by the pool itself (file://, which is not permit-wrapped), so the pool never needs more thanNthreads. The parse pipeline's page consumer runs onesql_worker, not here (seeAsyncExternalSourceOperatorFactory), so a full pool of readers can never starve its own drain; that separation is what makespool == permitssafe rather than deadlock-prone. One exception to tracking the knob:max_concurrent_requests=0disables the permit limiter (unbounded in-flight reads), but the I/O pool still needs threads to run the reads and parse pipeline, so it falls back to the CPU-scaleddefaultBlobStoreConcurrency(Settings)default rather than a zero-thread pool. Always>= 1. -
maxConcurrentSegmentators
Effective cap on concurrent stream-only-compressed segmentators for a node, resolvingMAX_CONCURRENT_SEGMENTATORS(0= derive) and clamping the result to[1, poolSize - 1]so at least oneesql_external_iothread always remains for the one-shot parser tasks that a segmentator depends on. The pool size isexternalIoThreads(Settings), which sizes that pool; the derived default tracks it, so the cap ispoolSize - 1unless an operator sets a lower explicit value. An explicit value abovepoolSize - 1is clamped and logged at WARN so the operator sees it was ineffective.Degenerate case: a
poolSize == 1pool (only reachable by explicitly settingesql.external.max_concurrent_requests=1) floors the cap at 1, which cannot leave a free parser thread — parallel streaming needspoolSize >= 2to be deadlock-free. At that size the deadlock-free path is the single-pass fallback (parallelism<= 1), which runs no segmentator; a parallel streaming read on a one-thread pool is a misconfiguration this cap cannot rescue. -
settings
-