FuseQL Limitations

FuseQL queries on Kloudfuse run against Apache Pinot with a set of hard limits designed to protect cluster stability. This page documents every enforced ceiling, the error message you will see when a limit is reached, and practical steps to stay within bounds.

Log query row limit

Log-retrieval queries (those that return individual log lines) are capped at 10,000 rows per query.

Queries that specify a limit exceeding this return an error:

limit 20000 exceeds maximum allowed limit of 10000

Queries without an explicit limit use the server default and will be silently capped. The pagination step size is 200 rows; paging through results always returns at most 200 rows per page regardless of the outer limit.

To work within this constraint:

  • Narrow the stream selector and facet filters to reduce the result set before adding a limit.

  • Use a shorter time range — the limit applies to the entire range, so a narrower window returns the most recent or most relevant rows.

  • For analysis over large result sets, use metric queries (count_over_time, rate, aggregations) instead of fetching raw lines.

Metric series limit

Metric queries (those that produce time series) are capped at 10,000 distinct label combinations per query. When the limit is reached the query fails with:

number of series exceeds threshold

No partial result is returned. The fix is always to reduce the cardinality of the output:

  • Add more label matchers to the stream selector to narrow the input set.

  • Aggregate earlier with by to collapse high-cardinality labels before they reach the metric result:

# May exceed the series limit for a busy cluster
count_over_time({kube_namespace="prod"}[5m])

# Reduces output to one series per namespace×service combination
sum by (kube_namespace, kube_service) (count_over_time({kube_namespace="prod"}[5m]))

Compose operator limit

The compose operator runs a subquery and uses its result as an in-filter for the outer query. It has a hard ceiling of 10,000 results. The default result size is 2,500; you can raise it with maxresults=N, but values above 10,000 are silently clamped.

When the subquery returns more rows than maxresults the excess rows are dropped and the outer query filters against the truncated set. There is no error — rows that would have matched the dropped entries pass through unfiltered.

Use compose only for bounded reference sets (lookup lists, known entity IDs). For large or unbounded sets, express the condition as a direct facet filter instead.

Query timeouts

FuseQL queries are subject to two independent timeout layers:

Layer Default Applies to

Per-Pinot-call timeout

60 seconds

Each individual storage call within the query

Overall query engine timeout

5 minutes

The entire query execution from start to result

A query that touches a very long time range or returns a very large intermediate result set may hit either limit. The per-Pinot-call timeout is the one most commonly encountered in interactive queries; the overall timeout is more relevant for scheduled searches and long-running analytics.

To avoid timeouts:

  • Narrow the time range. The per-call cost scales with the data volume in the selected window.

  • Use the three-layer filtering order (stream selector → facets → text search) so Pinot eliminates rows early.

  • For metric queries over long ranges, widen the step interval to reduce the number of evaluation points.

Pushdown constraints

Certain pipeline stages prevent FuseQL from pushing computation down to the Pinot storage layer. Once pushdown is disabled for a stage, every subsequent stage in the pipeline also runs in the query engine rather than storage.

Stage Effect on pushdown

line_format with template syntax ({{ .field }})

Disables pushdown for itself and all following stages

label_format used to add or modify labels (not rename)

Disables pushdown for itself and all following stages

Parser invoked with expression parameters

Disables pushdown for that parser and all following stages

Basic json, logfmt, pattern, regexp (no parameters)

Pushdown-eligible

label_format new=old (rename only)

Pushdown-eligible

Metric aggregations (count, sum, avg, min, max, and the percentile operators) combined with a timeslice grouping are pushed down to Pinot only when all preceding pipeline stages are also pushdown-eligible.

Practical consequence: keep line_format out of metric queries. If you need reformatted output, run the formatting step in a separate log-retrieval query.

Lookup table limits

The lookup operator loads an external CSV file and enriches query results with additional fields. Two hard limits apply:

  • 100,000 rows maximum per lookup table

  • 50 MB maximum file size

Uploads that exceed either limit are rejected with an error message stating the actual and maximum sizes. If your reference data exceeds these limits, pre-aggregate or subset it before uploading, or split it into multiple lookup tables and join them in sequence.

Column value length

Individual field values stored or extracted at query time are capped at 102,400 bytes (~100 KB) per value. Values longer than this are truncated. This limit applies to:

  • Facet values extracted from the log body at ingest

  • Fields returned by parser stages at query time

  • String columns created by label_format or line_format

If a log line contains a very large embedded payload (a serialised object, a base64-encoded blob), the extracted field will be truncated. Structure your ingest pipeline to avoid storing large values in indexed facet columns.

Regex engine constraints

FuseQL uses the RE2 regex engine. RE2 does not support:

  • Look-ahead ((?=…​), (?!…​))

  • Look-behind ((?⇐…​), (?<!…​))

  • Back-references (\1, \2)

  • Possessive quantifiers

Patterns are compiled at query time; invalid RE2 patterns return a parse error before the query executes. Label matchers (=~, !~) are fully anchored — the pattern must match the entire label value, not a substring:

# Matches nothing — "nginx" does not equal the full value "nginx-ingress"
source=~"nginx"

# Correct — anchor with .* to match a substring
source=~"nginx.*"

For most text searches, the purpose-built FuseQL operators avoid regex entirely: term search ('…'), contains (*), starts-with (~), and ends-with (~*) cover the majority of patterns without RE2 constraints.

Aggregation syntax constraints

timeslice must precede _timeslice grouping.

The _timeslice grouping key is only available after a | timeslice <duration> | stage has been added to the pipeline. Referencing it in a by or aggregation clause without a prior timeslice stage returns:

aggregation groups by "_timeslice" but no timeslice operator was specified;
add "| timeslice <duration> |" before the aggregation

Post-aggregation field references.

Fields that are not part of the aggregation output are not available after the aggregation stage. Referencing a dropped field returns:

field "fieldname" is not available after aggregation; available fields: [...]

Re-structure the query to either include the field in the by clause or filter on it before the aggregation.

Scheduled views limits

Kloudfuse runs scheduled views as background aggregation jobs. The following limits apply:

Limit Value

Maximum scheduled views loaded at startup

100

Concurrent evaluation workers

2

Dispatch timeout per evaluation

120 seconds

If your cluster requires more than 100 scheduled views or higher concurrency, contact Kloudfuse support to adjust these defaults.