Skip to content

feat(msq): execute system tables with Dart - #213

Open
FrankChen021 wants to merge 30 commits into
masterfrom
codex/dart-system-tables-standalone
Open

FrankChen021 wants to merge 30 commits into
masterfrom
codex/dart-system-tables-standalone

Conversation

@FrankChen021

@FrankChen021 FrankChen021 commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Description

System tables currently use Calcite's local Bindable path, which prevents Dart from applying its distributed query stages and native operators. This change introduces the system-table datasource and MSQ input needed to execute sys.server_properties through Dart as an initial implementation.

Execution path and query planning

Show execution details
SQL request (`engine = msq-dart`)
  -> Calcite plans `sys.server_properties` as `SystemTableDataSource`
  -> Dart controller discovers source nodes and creates input slices
  -> a worker reads its node-local provider when possible
     or issues an authenticated native Scan query to the source node's `/druid/v2` endpoint
  -> rows are exposed as a lazy `RowBasedSegment`
  -> ordinary MSQ worker stages apply filters, aggregation, windows, frames,
     shuffle, and final result processing
  • Adds a generic SystemTableDataSource and a planner bridge for native-capable system-table definitions.
  • Adds system-table MSQ input specs, slices, source discovery, serde, and Dart worker readers.
  • Registers sys.server_properties as the only Dart-capable table in this change.
  • Uses mix shuffle for unordered scans to avoid an unnecessary global sort. Queries with ORDER BY retain the standard global-sort path.
  • Preserves authorization and query-timeout semantics, and cancels outstanding component requests when an input slice closes.
  • Keeps unsupported engines and unsupported system tables on their existing fallback paths.
  • Installs an embedded Dart worker on the Broker. It participates only when needed by a system-table query, uses the same worker-stage processing model as other Dart workers, and keeps sys.server_properties queryable when no Historical is available.

Benchmark

ServerPropertiesEngineBenchmark provides a reproducible standalone benchmark of this Dart path. For the cross-engine comparison below, the same harness was run from a combined checkout containing both this change and the native system-table implementation from apache/druid#20183. The native leg used engine=native with useNativeQueryForSystemTables=true; the Dart leg used engine=msq-dart. Both ran in the same embedded cluster over the same 100,001 generated properties, with 3 warmup iterations followed by 10 measured iterations and alternating engine order.

Workload PR 20183 native median Dart median Result
COUNT(*) and COUNT(DISTINCT property) over 100,001 matching rows 193.62 ms 139.79 ms Dart 27.8% faster
SELECT * FROM sys.server_properties returning 100,047 rows 235.98 ms 222.38 ms Dart 5.8% faster

The master Bindable system-table path is not the native baseline: it cannot plan the COUNT(DISTINCT property) workload. The full scan includes 46 ordinary embedded-server properties in addition to the generated benchmark properties. These figures are local embedded-cluster latency measurements rather than production throughput results.

Release note

Dart can now execute SQL queries over sys.server_properties, including distributed filtering, aggregation, ordering, and window processing. Other system tables continue to use the existing Bindable execution path.


Key changed/added classes in this PR
  • SystemTableDataSource
  • SystemTableDataSourcePlanner
  • DartSystemTableInputSpecSlicer
  • DartSystemTableInputSliceReader
  • DartEmbeddedWorkerModule

This PR has:

  • been self-reviewed.
    • using the concurrency checklist.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs and intent comments for non-trivial code.
  • added unit tests covering the new code paths.

Copilot AI lite review requested due to automatic review settings September 26, 2026 11:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…tables-standalone

# Conflicts:
#	docs/querying/dart.md
#	services/src/main/java/org/apache/druid/cli/CliPeon.java
A limited sequence nested inside a concatenated sequence leaves the row cursor on a null row once the limit is reached, so an unfiltered LIMIT query over a co-located system table source failed with a NullPointerException. Apply the limit once to the concatenated rows instead.
Drop tests that pass without the feature or only assert constants, merge redundant cases, assert returned rows in the slice reader tests, tag the benchmark as perf, and add coverage for the system table datasource, node-local query routing, limit handling and row authorization.
The handler runs the Scan engine directly, bypassing the Scan tool chest that normally widens the limit and skips the offset rows, so a node-local Scan with an offset returned the leading rows. Apply the offset the same way the tool chest does.
The hints are derived from the root query, whose filter, virtual columns and required columns describe the output of the datasource tree. Over an UNNEST or other wrapped datasource they pruned columns the unnest expression reads and pushed filters on the unnest output to the nodes, silently returning wrong results.
…uery-parse handler

A failure while the Router's local container handled a node-local request, such as a client disconnect, was caught by the query-parse handlers, logged as an unparseable query and answered as one. Dispatch after parsing completes, and remove the node-local cancellation dispatch, which is no longer used. Also remove QueryablePeonModule, which Peon and Indexer no longer install.
…ata server queries

Node-local system table scans are not registered with the QueryScheduler, so the explicit cancel request was a no-op that only logged a warning on the node; aborting the in-flight request is what stops it. Also skip the cancel when the response already failed or the query has no id, which previously sent a spurious request or threw from the closer.
…rror rows

Only socket-level failures became per-node error rows, so a node that answered with an error, notably an older node during a rolling upgrade that does not know the system table datasource, failed the whole query. Treat service-unavailable errors and non-authorization HTTP errors as node failures too.
A server with no segments to query, such as one that only serves node-local system table queries, failed with a NullPointerException when sent another query.
…lice input handling

A Broker relayed controller messages from every Broker although only its own embedded worker takes part in its queries. Also close all remote requests of a slice even if one close fails, request one column from remote nodes for row-count-only stages instead of all columns, and treat a missing limit in the serialized input as unlimited.
…les-standalone

# Conflicts:
#	multi-stage-query/src/main/java/org/apache/druid/msq/dart/controller/DartControllerContext.java
#	multi-stage-query/src/main/java/org/apache/druid/msq/dart/guice/DartWorkerModule.java
#	multi-stage-query/src/test/java/org/apache/druid/msq/dart/controller/DartControllerContextTest.java
…le descriptor

The engine hard-coded STATE READ for every system table while the descriptor's row authorizer was the real policy, and DartQueryMaker re-checked it at run time by passing an empty row list to the authorizer, which only worked because the authorizer checks eagerly. Add getResourceActions() to the descriptor and have the engine return it, so the caller is authorized once, when the SQL statement is authorized and before any work is dispatched.
…atic analysis

Checkstyle rejected an unused java.util.Collections import left behind in ServerPropertiesTableDescriptor, and CodeQL flagged two parameterized-test descriptions as unused; use them as assertion messages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants