Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 49 additions & 33 deletions data-explorer/kusto/concepts/query-limits.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Query limits
description: This article describes Query limits.
ms.reviewer: zivc
ms.topic: reference
ms.date: 01/12/2026
ms.date: 03/30/2026
monikerRange: "microsoft-fabric || azure-data-explorer || azure-monitor || microsoft-sentinel"
---
# Query limits
Expand Down Expand Up @@ -72,7 +72,7 @@ MyTable | take 1000000
You can also have more refined control over result truncation
by setting the value of `truncationmaxsize` (maximum data size in bytes,
defaults to 64 MB) and `truncationmaxrecords` (maximum number of records,
defaults to 500,000). For example, the following query sets result truncation
defaults to 500,000). For example, the following querysets result truncation
to happen at either 1,105 records or 1 MB, whichever is exceeded.

```kusto
Expand Down Expand Up @@ -109,9 +109,9 @@ The following rules apply when you use `set` statements or specify flags in [cli

## Limit on memory consumed by query operators

**Max memory consumption per iterator** limit can be configured to control the amount of memory that each query operator consumes, per node. Some query operators, such as `join` and `summarize`, hold significant data in memory. By increasing the default of the request option `maxmemoryconsumptionperiterator`, you can run queries that require more memory per operator.
You can configure the **Max memory consumption per iterator** limit to control the amount of memory that each query operator consumes, per node. Some query operators, such as `join` and `summarize`, hold significant data in memory. By increasing the default value of the request option `maxmemoryconsumptionperiterator`, you can run queries that require more memory per operator.

The maximum supported value for this request option is 32212254720 (30 GB). If you set `maxmemoryconsumptionperiterator` multiple times, for example in both client request properties and using a `set` statement, the lower value applies.
The maximum supported value for this request option is 32,212,254,720 (30 GB). If you set `maxmemoryconsumptionperiterator` multiple times, such as in both client request properties and by using a `set` statement, the lower value applies.

When the query reaches the configured memory per operator limit, a partial query failure message displays and includes the text `E_RUNAWAY_QUERY`.

Expand All @@ -123,7 +123,7 @@ For example:

`The Sort operator has exceeded the memory budget during evaluation. Results might be incorrect or incomplete (E_RUNAWAY_QUERY).`

For example, this query sets the max memory consumption per iterator to 15 GB:
For example, this querysets the max memory consumption per iterator to 15 GB:

<!-- csl -->
```kusto
Expand All @@ -132,15 +132,15 @@ MyTable | summarize count() by Use
```

Another limit that might trigger an `E_RUNAWAY_QUERY` partial query failure is the max accumulated size of
strings held by a single operator. The request option above can't override this limit.
strings held by a single operator. The request option described earlier can't override this limit.

`Runaway query (E_RUNAWAY_QUERY). Aggregation over string column exceeded the memory budget of 8GB during evaluation.`

When this limit is exceeded, most likely the relevant query operator is a `join`, `summarize`, or `make-series`.

To work around the limit, modify the query to use the [shuffle query](../query/shuffle-query.md) strategy. This change is also likely to improve the performance of the query.

In all cases of `E_RUNAWAY_QUERY`, an additional option (beyond increasing the limit by setting the request option and changing the
In all cases of `E_RUNAWAY_QUERY`, another option (beyond increasing the limit by setting the request option and changing the
query to use a shuffle strategy) is to switch to sampling. Sampling reduces the amount of data processed by the query, and therefore reduces the memory pressure on query operators.

These two queries show how to do the sampling. The first query is a statistical sampling, using a random number generator. The second query is deterministic sampling, done by hashing some column from the dataset, usually some ID.
Expand All @@ -152,40 +152,40 @@ T | where rand() < 0.1 | ...
T | where hash(UserId, 10) == 1 | ...
```

For more information about using mechanisms such as hint.shufflekey for both `summarize` and `join`, see [Best practices for Kusto Query Language queries](../query/best-practices.md).
For more information about using mechanisms such as `hint.shufflekey` for both `summarize` and `join`, see [Best practices for Kusto Query Language queries](../query/best-practices.md).

## Limit on memory per node

**Max memory per query per node** is another limit used to protect against "runaway" queries. This limit, represented by the request option `max_memory_consumption_per_query_per_node`, sets an upper bound
on the amount of memory that can be used on a single node for a specific query.
**Max memory per query per node** is another limit that protects against runaway queries. The request option `max_memory_consumption_per_query_per_node` sets an upper bound
on the amount of memory that a single node can use for a specific query.

```kusto
set max_memory_consumption_per_query_per_node=68719476736;
MyTable | ...
```

If `max_memory_consumption_per_query_per_node` is set multiple times, for example in both client request properties and using a `set` statement, the lower value applies.
If you set `max_memory_consumption_per_query_per_node` multiple times, such as in both client request properties and a `set` statement, the lower value applies.

If the query uses `summarize`, `join`, or `make-series` operators, you can use the [shuffle query](../query/shuffle-query.md) strategy to reduce memory pressure on a single machine.

## Limit execution timeout

**Server timeout** is a service-side timeout that is applied to all requests.
Timeout on running requests (queries and management commands) is enforced at multiple
points in the Kusto:
**Server timeout** is a service-side timeout that the service applies to all requests.
Kusto enforces timeout on running requests (queries and management commands) at multiple
points:

* client library (if used)
* service endpoint that accepts the request
* service engine that processes the request

By default, timeout is set to four minutes for queries, and 10 minutes for
management commands. This value can be increased if needed (capped at one hour).
By default, the timeout is set to four minutes for queries, and 10 minutes for
management commands. You can increase this value if needed, up to one hour.

* Various client tools support changing the timeout as part of their global
or per-connection settings. For example, in Kusto.Explorer, use **Tools** &gt; **Options*** &gt;
or per-connection settings. For example, in Kusto.Explorer, use **Tools** &gt; **Options** &gt;
**Connections** &gt; **Query Server Timeout**.
* Programmatically, SDKs support setting the timeout through the `servertimeout`
property. For example, in .NET SDK this is done through a [client request property](../api/rest/request-properties.md),
property. For example, in .NET SDK, set this property through a [client request property](../api/rest/request-properties.md),
by setting a value of type `System.TimeSpan`.

### Notes about timeouts
Expand All @@ -206,34 +206,50 @@ management commands. This value can be increased if needed (capped at one hour).

## Limit on query CPU resource usage

Kusto lets you run queries and use all the available CPU resources that the database has.
It attempts to do a fair round-robin between queries if more than one is running. This method yields the best performance for query-defined functions.
Kusto runs queries and uses all the available CPU resources that the database has.
It attempts to do a fair round-robin between queries if more than one query is running. This method yields the best performance for query-defined functions.
At other times, you might want to limit the CPU resources used for a particular
query. If you run a "background job", for example, the system might tolerate higher
query. If you run a background job, for example, the system might tolerate higher
latencies to give concurrent inline queries high priority.

Kusto supports specifying two [request properties](../api/rest/request-properties.md) when running a query.
The properties are *query_fanout_threads_percent* and *query_fanout_nodes_percent*.
Both properties are integers that default to the maximum value (100), but may be reduced for a specific query to some other value.
Both properties are integers that default to the maximum value (100), but you can reduce them for a specific query.

The first, *query_fanout_threads_percent*, controls the fanout factor for thread use.
When this property is set 100%, all CPUs are assigned on each node. For example, 16 CPUs deployed on Azure D14 nodes.
When this property is set to 50%, then half of the CPUs are used, and so on.
The first property, *query_fanout_threads_percent*, controls the fanout factor for thread use.
When you set this property to 100%, the query uses all CPUs on each node. For example, 16 CPUs deployed on Azure D14 nodes.
When you set this property to 50%, the query uses half of the CPUs, and so on.
The numbers are rounded up to a whole CPU, so it's safe to set the property value to 0.

The second, *query_fanout_nodes_percent*, controls how many of the query nodes to use per subquery distribution operation.
The second property, *query_fanout_nodes_percent*, controls how many of the query nodes to use per subquery distribution operation.
It functions in a similar manner.

If `query_fanout_nodes_percent` or `query_fanout_threads_percent` are set multiple times, for example, in both client request properties and using a `set` statement - the lower value for each property applies.
If you set `query_fanout_nodes_percent` or `query_fanout_threads_percent` multiple times, for example, in both client request properties and by using a `set` statement, the lower value for each property applies.

## Limit on query complexity

During query execution, the query text is transformed into a tree of relational operators representing the query.
If the tree depth exceeds an internal threshold, the query is considered too complex for processing, and fails with an error code. The failure indicates that the relational operators tree exceeds its limits.
If the tree depth exceeds an internal threshold, the query is too complex for processing, and fails with an error code. The failure indicates that the relational operators tree exceeds its limits.

The following examples show common query patterns that can cause the query to exceed this limit and fail:
### Query complexity exceeded

* a long list of binary operators that are chained together. For example:
The query is too complex for the engine to compile. This complexity usually occurs when query plan construction consumes too many resources. Common causes include:

* Large unions across many tables, such as using `union *` on a large database.
* Deeply nested subqueries.
* Many levels of `let` statements that reference each other.

### Query plan size or complexity exceeds set limits

The query generates a query plan that's too large to process. Common causes include:

* The left side of a broadcast join produces too much data.
* The results returned by `toscalar()` are too large.
* The results inside an `in()` expression are too large. For example, an `in (subquery)` where the subquery returns too many values.

The following examples show common query patterns that can cause the query to exceed these limits and fail:

* A long list of binary operators that are chained together. For example:

```kusto
T
Expand All @@ -243,16 +259,16 @@ T
Column == "valueN"
```

For this specific case, rewrite the query using the [`in()`](../query/in-operator.md) operator.
For this specific case, rewrite the query by using the [`in()`](../query/in-operator.md) operator.

```kusto
T
| where Column in ("value1", "value2".... "valueN")
```

* a query which has a union operator that's running too wide schema analysis especially that the default flavor of union is to return "outer" union schema (meaning – that output includes all columns of the underlying table).
* A query that uses a union operator and runs too wide schema analysis. This problem happens because the default flavor of union returns an "outer" union schema. This schema means that the output includes all columns of the underlying table.

The suggestion in this case is to review the query and reduce the columns being used by the query.
Review the query and reduce the number of columns the query uses.

## Related content

Expand Down