Skip to content

perf: Add runtime stats for uncaptured metastore and planning operations#27438

Merged
feilong-liu merged 1 commit intoprestodb:masterfrom
feilong-liu:export-D98045070
Mar 26, 2026
Merged

perf: Add runtime stats for uncaptured metastore and planning operations#27438
feilong-liu merged 1 commit intoprestodb:masterfrom
feilong-liu:export-D98045070

Conversation

@feilong-liu
Copy link
Copy Markdown
Contributor

@feilong-liu feilong-liu commented Mar 25, 2026

Summary

Adds runtime metrics to capture previously untracked wall time during query planning, helping decompose the gap between ANALYZE_TIME_NANOS wall time and CPU time.

Verbose Planner Runtime Stats Session Property

  • Added verbose_planner_runtime_stats_enabled session property to enable verbose runtime stats for analyzer, logical planner, and optimizer phases only (without enabling all verbose runtime stats)
  • Wired into Optimizer, IterativeOptimizer, ApplyConnectorOptimization, HistoryBasedPlanStatisticsCalculator, and CachingPlanCanonicalInfoProvider

New Timing Metrics

  • GET_TABLE_STATISTICS_TIME_NANOS — wall time for getTableStatistics() in MetadataManager
  • CHECK_ACCESS_PERMISSIONS_TIME_NANOS — wall and CPU time for checkAccessPermissions() in SqlQueryExecution
  • GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS — wall time for getMaterializedViewStatus() calls in StatementAnalyzer
  • GET_COLUMN_HANDLE_TIME_NANOS — wall time for getColumnHandles() calls during ANALYZE and vector index creation in StatementAnalyzer
  • GET_TABLE_HANDLE_TIME_NANOS — wall time for getTableHandle() during vector index creation in StatementAnalyzer

Differential Revision: D98045070

Release Notes

== NO RELEASE NOTE ==

@feilong-liu feilong-liu requested review from a team, elharo and jaystarshot as code owners March 25, 2026 17:52
@prestodb-ci prestodb-ci added the from:Meta PR from Meta label Mar 25, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Mar 25, 2026

Reviewer's Guide

Adds runtime planning and metastore timing metrics by wrapping specific metadata and access control operations with RuntimeStats recording, and introduces a new CHECK_ACCESS_PERMISSIONS_TIME_NANOS runtime metric.

Sequence diagram for recording access permission check timing

sequenceDiagram
    participant SqlQueryExecution
    participant Session
    participant RuntimeStats
    participant AccessControl

    SqlQueryExecution->>Session: getSession()
    Session-->>SqlQueryExecution: Session

    SqlQueryExecution->>Session: getRuntimeStats()
    Session-->>SqlQueryExecution: RuntimeStats

    SqlQueryExecution->>RuntimeStats: recordWallAndCpuTime(CHECK_ACCESS_PERMISSIONS_TIME_NANOS, callback)
    activate RuntimeStats
    RuntimeStats-->>SqlQueryExecution: invoke callback

    activate SqlQueryExecution
    SqlQueryExecution->>SqlQueryExecution: checkAccessPermissions(accessControlReferences, viewDefinitionReferences, query, preparedStatements, identity, accessControl, accessControlContext)
    SqlQueryExecution->>AccessControl: check permissions for referenced tables and views
    AccessControl-->>SqlQueryExecution: permission check result
    deactivate SqlQueryExecution

    SqlQueryExecution-->>RuntimeStats: callback returns null
    RuntimeStats-->>SqlQueryExecution: recorded wall and CPU time for CHECK_ACCESS_PERMISSIONS_TIME_NANOS
    deactivate RuntimeStats
Loading

Sequence diagram for metadata and metastore timing in StatementAnalyzer

sequenceDiagram
    participant StatementAnalyzer
    participant Session
    participant RuntimeStats
    participant MetadataResolver

    rect rgb(230,230,250)
        note over StatementAnalyzer,MetadataResolver: Timing getTableHandle in visitCreateVectorIndex
        StatementAnalyzer->>Session: getRuntimeStats()
        Session-->>StatementAnalyzer: RuntimeStats
        StatementAnalyzer->>RuntimeStats: recordWallTime(GET_TABLE_HANDLE_TIME_NANOS, callback)
        activate RuntimeStats
        RuntimeStats-->>StatementAnalyzer: invoke callback
        activate StatementAnalyzer
        StatementAnalyzer->>MetadataResolver: getTableHandle(sourceTableName)
        MetadataResolver-->>StatementAnalyzer: Optional<TableHandle>
        StatementAnalyzer-->>RuntimeStats: Optional<TableHandle>
        deactivate StatementAnalyzer
        RuntimeStats-->>StatementAnalyzer: TableHandle (via Optional.get())
        deactivate RuntimeStats
    end

    rect rgb(230,250,230)
        note over StatementAnalyzer,MetadataResolver: Timing getColumnHandles in analyze and create vector index
        StatementAnalyzer->>Session: getRuntimeStats()
        Session-->>StatementAnalyzer: RuntimeStats
        StatementAnalyzer->>RuntimeStats: recordWallTime(GET_COLUMN_HANDLE_TIME_NANOS, callback)
        activate RuntimeStats
        RuntimeStats-->>StatementAnalyzer: invoke callback
        activate StatementAnalyzer
        StatementAnalyzer->>MetadataResolver: getColumnHandles(tableHandle)
        MetadataResolver-->>StatementAnalyzer: Map<ColumnName, ColumnHandle>
        StatementAnalyzer-->>RuntimeStats: Map<ColumnName, ColumnHandle>
        deactivate StatementAnalyzer
        RuntimeStats-->>StatementAnalyzer: Map<ColumnName, ColumnHandle> for further processing
        deactivate RuntimeStats
    end

    rect rgb(250,230,230)
        note over StatementAnalyzer,MetadataResolver: Timing getMaterializedViewStatus in analyzeAutoRefreshMaterializedView
        StatementAnalyzer->>Session: getRuntimeStats()
        Session-->>StatementAnalyzer: RuntimeStats
        StatementAnalyzer->>RuntimeStats: recordWallTime(GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS, callback)
        activate RuntimeStats
        RuntimeStats-->>StatementAnalyzer: invoke callback
        activate StatementAnalyzer
        StatementAnalyzer->>MetadataResolver: getMaterializedViewStatus(materializedViewName, baseQueryDomain)
        MetadataResolver-->>StatementAnalyzer: MaterializedViewStatus
        StatementAnalyzer-->>RuntimeStats: MaterializedViewStatus
        deactivate StatementAnalyzer
        RuntimeStats-->>StatementAnalyzer: MaterializedViewStatus for further analysis
        deactivate RuntimeStats
    end
Loading

Updated class diagram for runtime stats integration

classDiagram
    class SqlQueryExecution {
        - QueryStateMachine stateMachine
        - QuerySessionSupplier sessionSupplier
        - AccessControl accessControl
        - QueryAnalysis queryAnalysis
        + SqlQueryExecution(...)
        - checkAccessPermissions(accessControlReferences, viewDefinitionReferences, query, preparedStatements, identity, accessControl, accessControlContext)
    }

    class StatementAnalyzer {
        - Session session
        - MetadataResolver metadataResolver
        + visitAnalyze(node, scope) Scope
        + analyzeAutoRefreshMaterializedView(node, viewName) Map
        + visitCreateVectorIndex(node, scope) Scope
        - getMaterializedViewStatus(materializedViewName, baseQueryDomain) MaterializedViewStatus
    }

    class Session {
        + getRuntimeStats() RuntimeStats
        + getPreparedStatements() Map
        + getIdentity() Identity
        + getAccessControlContext() AccessControlContext
    }

    class RuntimeStats {
        + recordWallTime(metricName, supplier) Object
        + recordWallAndCpuTime(metricName, supplier) Object
    }

    class RuntimeMetricName {
        <<final>>
        + DIRECTORY_LISTING_CACHE_MISS String
        + DIRECTORY_LISTING_TIME_NANOS String
        + FILES_READ_COUNT String
        + CHECK_ACCESS_PERMISSIONS_TIME_NANOS String
    }

    class MetadataResolver {
        + getColumnHandles(tableHandle) Map
        + getTableHandle(tableName) Optional
        + getMaterializedViewStatus(materializedViewName, baseQueryDomain) MaterializedViewStatus
    }

    class AccessControl {
        + checkCanSelectFromColumns(...)
        + checkCanInsertIntoTable(...)
        + checkCanReferenceView(...)
    }

    class MaterializedViewStatus {
        + getPartitionsFromBaseTables() Map
    }

    class TupleDomain {
        + all() TupleDomain
    }

    SqlQueryExecution --> Session : uses
    SqlQueryExecution --> AccessControl : uses
    SqlQueryExecution --> RuntimeStats : uses via Session
    SqlQueryExecution --> RuntimeMetricName : uses CHECK_ACCESS_PERMISSIONS_TIME_NANOS

    StatementAnalyzer --> Session : uses
    StatementAnalyzer --> MetadataResolver : uses
    StatementAnalyzer --> RuntimeStats : uses via Session
    StatementAnalyzer --> RuntimeMetricName : uses GET_TABLE_HANDLE_TIME_NANOS
    StatementAnalyzer --> RuntimeMetricName : uses GET_COLUMN_HANDLE_TIME_NANOS
    StatementAnalyzer --> RuntimeMetricName : uses GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS

    Session --> RuntimeStats : provides

    MetadataResolver --> MaterializedViewStatus : returns
    StatementAnalyzer --> MaterializedViewStatus : uses

    StatementAnalyzer --> TupleDomain : uses
Loading

File-Level Changes

Change Details Files
Record wall time for metadata lookups during analysis and materialized view handling via RuntimeStats wrappers.
  • Wrap getColumnHandles() in Analyze analysis with session.getRuntimeStats().recordWallTime using GET_COLUMN_HANDLE_TIME_NANOS
  • Wrap getMaterializedViewStatus() calls in analysis and helper methods with session.getRuntimeStats().recordWallTime using GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS
  • Wrap getTableHandle() and getColumnHandles() in CreateVectorIndex analysis with session.getRuntimeStats().recordWallTime using GET_TABLE_HANDLE_TIME_NANOS and GET_COLUMN_HANDLE_TIME_NANOS
presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java
Capture wall and CPU time spent performing access permission checks during query execution.
  • Wrap checkAccessPermissions(...) invocation in SqlQueryExecution constructor with session.getRuntimeStats().recordWallAndCpuTime using CHECK_ACCESS_PERMISSIONS_TIME_NANOS
presto-main-base/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java
Define a new runtime metric name for access permissions timing.
  • Add CHECK_ACCESS_PERMISSIONS_TIME_NANOS constant to RuntimeMetricName
presto-common/src/main/java/com/facebook/presto/common/RuntimeMetricName.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In getMaterializedViewStatus, the warning branch previously used baseQueryDomain but now uses TupleDomain.all() inside the recordWallTime wrapper; if this wasn’t intentional, it changes behavior and may widen the domain unexpectedly when the view definition is missing.
  • The new recordWallTime calls for table/column handle and materialized view status lookups are repeated in several places in StatementAnalyzer; consider introducing small helper methods (e.g., getTableHandleWithTiming, getMaterializedViewStatusWithTiming) to centralize the metric wrapping and avoid divergence between call sites.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `getMaterializedViewStatus`, the warning branch previously used `baseQueryDomain` but now uses `TupleDomain.all()` inside the `recordWallTime` wrapper; if this wasn’t intentional, it changes behavior and may widen the domain unexpectedly when the view definition is missing.
- The new `recordWallTime` calls for table/column handle and materialized view status lookups are repeated in several places in `StatementAnalyzer`; consider introducing small helper methods (e.g., `getTableHandleWithTiming`, `getMaterializedViewStatusWithTiming`) to centralize the metric wrapping and avoid divergence between call sites.

## Individual Comments

### Comment 1
<location path="presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java" line_range="2722-2724" />
<code_context>
                 if (!materializedViewDefinition.isPresent()) {
                     log.warn("Materialized view definition not present as expected when fetching materialized view status");
-                    return metadataResolver.getMaterializedViewStatus(materializedViewName, baseQueryDomain);
+                    return session.getRuntimeStats().recordWallTime(
+                            RuntimeMetricName.GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS,
+                            () -> metadataResolver.getMaterializedViewStatus(materializedViewName, TupleDomain.all()));
                 }

</code_context>
<issue_to_address>
**issue (bug_risk):** Using TupleDomain.all() here drops the baseQueryDomain filtering that was present before.

This branch used to call `getMaterializedViewStatus(materializedViewName, baseQueryDomain)`, preserving the predicate. Passing `TupleDomain.all()` now ignores that filter and can change behavior (e.g., requesting status for all partitions). I think this should still use `baseQueryDomain`, consistent with the return at the end of the method.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@feilong-liu feilong-liu changed the title [Presto] Add runtime stats for uncaptured metastore and planning operations perf: Add runtime stats for uncaptured metastore and planning operations Mar 25, 2026
@feilong-liu feilong-liu force-pushed the export-D98045070 branch 2 times, most recently from a02cd9f to 4c1fdf2 Compare March 25, 2026 23:19
…ations

Adds runtime metrics to capture previously untracked wall time during
query planning, helping decompose the gap between ANALYZE_TIME_NANOS
wall time and CPU time.

- Add verbose_planner_runtime_stats_enabled session property
- Add GET_TABLE_STATISTICS_TIME_NANOS timing in MetadataManager
- Add CHECK_ACCESS_PERMISSIONS_TIME_NANOS timing in SqlQueryExecution
- Add GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS timing in StatementAnalyzer
- Add GET_COLUMN_HANDLE_TIME_NANOS and GET_TABLE_HANDLE_TIME_NANOS timing
  in StatementAnalyzer
- Wire isVerbosePlannerRuntimeStatsEnabled into Optimizer,
  IterativeOptimizer, ApplyConnectorOptimization,
  HistoryBasedPlanStatisticsCalculator, and CachingPlanCanonicalInfoProvider

Differential Revision: D98045070
@feilong-liu feilong-liu merged commit 5e415bf into prestodb:master Mar 26, 2026
114 of 116 checks passed
@feilong-liu feilong-liu deleted the export-D98045070 branch March 26, 2026 22:16
feilong-liu added a commit that referenced this pull request Mar 30, 2026
…ons (#27438)

Adds runtime metrics to capture previously untracked wall time during
query planning, helping decompose the gap between ANALYZE_TIME_NANOS
wall time and CPU time.

**Verbose Planner Runtime Stats Session Property**
- Added `verbose_planner_runtime_stats_enabled` session property to
enable verbose runtime stats for analyzer, logical planner, and
optimizer phases only (without enabling all verbose runtime stats)
- Wired into `Optimizer`, `IterativeOptimizer`,
`ApplyConnectorOptimization`, `HistoryBasedPlanStatisticsCalculator`,
and `CachingPlanCanonicalInfoProvider`

**New Timing Metrics**
- `GET_TABLE_STATISTICS_TIME_NANOS` — wall time for
`getTableStatistics()` in `MetadataManager`
- `CHECK_ACCESS_PERMISSIONS_TIME_NANOS` — wall and CPU time for
`checkAccessPermissions()` in `SqlQueryExecution`
- `GET_MATERIALIZED_VIEW_STATUS_TIME_NANOS` — wall time for
`getMaterializedViewStatus()` calls in `StatementAnalyzer`
- `GET_COLUMN_HANDLE_TIME_NANOS` — wall time for `getColumnHandles()`
calls during ANALYZE and vector index creation in `StatementAnalyzer`
- `GET_TABLE_HANDLE_TIME_NANOS` — wall time for `getTableHandle()`
during vector index creation in `StatementAnalyzer`

Differential Revision: D98045070

== NO RELEASE NOTE ==
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