Skip to content

Conversation

@abhinavmuk04
Copy link
Contributor

@abhinavmuk04 abhinavmuk04 commented Oct 23, 2025

Summary:
Adds comprehensive monitoring for query state transitions to detect and diagnose
queries spending abnormally long time in DISPATCHING and FINISHING states. This
addresses issues observed on DKL cluster where queries were getting stuck during
dispatch and commit phases (ref: S561047).

Differential Revision: D85353849

== NO RELEASE NOTE ==


… Presto

Summary:
Adds comprehensive monitoring for query state transitions to detect and diagnose
queries spending abnormally long time in DISPATCHING and FINISHING states. This
addresses issues observed on DKL cluster where queries were getting stuck during
dispatch and commit phases (ref: S561047).

Differential Revision: D85353849
@abhinavmuk04 abhinavmuk04 requested a review from a team as a code owner October 23, 2025 19:10
@prestodb-ci prestodb-ci added the from:Meta PR from Meta label Oct 23, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 23, 2025

Reviewer's Guide

This PR introduces a QueryStateTransitionMonitor to record query state transition durations and detect anomalies, integrates monitoring hooks into QueryStateMachine, binds and exports the monitor metrics in the server module, and adds comprehensive unit tests for all monitoring scenarios.

Sequence diagram for query state transition monitoring integration

sequenceDiagram
    participant QSM as QueryStateMachine
    participant Monitor as QueryStateTransitionMonitor
    participant Tracker as QueryTransitionTracker
    QSM->>Monitor: setStateTransitionMonitor(monitor)
    Monitor->>QSM: registerQuery(queryId)
    QSM->>QSM: addStateChangeListener(trackStateTransition)
    QSM->>QSM: trackStateTransition(newState)
    QSM->>Monitor: recordStateTransition(queryId, prevState, newState, durationMillis)
    Monitor->>Tracker: recordTransition(prevState, newState, durationMillis)
    alt Anomaly detected
        Monitor->>Monitor: checkAndLogAnomaly(...)
    end
    alt Query done
        Monitor->>Monitor: activeQueries.remove(queryId)
    end
Loading

Class diagram for QueryStateTransitionMonitor and integration with QueryStateMachine

classDiagram
    class QueryStateTransitionMonitor {
        +registerQuery(queryId)
        +recordStateTransition(queryId, fromState, toState, durationMillis)
        +getTotalQueriesTracked()
        +getActiveQueriesCount()
        +getDispatchingTimeStats()
        +getFinishingTimeStats()
        +getPlanningTimeStats()
        +getRunningTimeStats()
        +getAnomalousDispatchingCount()
        +getAnomalousFinishingCount()
        +getAnomalousPlanningCount()
        +getAnomalousRunningCount()
        +getStatsSummary()
        -activeQueries: Map<QueryId, QueryTransitionTracker>
        -dispatchingTimeStats: TimeStat
        -finishingTimeStats: TimeStat
        -planningTimeStats: TimeStat
        -runningTimeStats: TimeStat
        -anomalousDispatchingCount: CounterStat
        -anomalousFinishingCount: CounterStat
        -anomalousPlanningCount: CounterStat
        -anomalousRunningCount: CounterStat
        -totalQueriesTracked: AtomicLong
    }
    class QueryTransitionTracker {
        -queryId: QueryId
        -lastState: QueryState
        -lastTransitionTimeMillis: long
        +recordTransition(fromState, toState, durationMillis)
        +getLastState()
        +getLastTransitionTimeMillis()
    }
    class QueryStateMachine {
        -stateTransitionMonitor: AtomicReference<QueryStateTransitionMonitor>
        -previousState: AtomicReference<QueryState>
        -previousStateTimestamp: AtomicLong
        +setStateTransitionMonitor(monitor)
        +trackStateTransition(newState)
    }
    QueryStateTransitionMonitor "1" *-- "many" QueryTransitionTracker
    QueryStateMachine "1" o-- "1" QueryStateTransitionMonitor
Loading

File-Level Changes

Change Details Files
Integrate state transition monitoring into QueryStateMachine
  • Added AtomicReferences for monitor, previous state and timestamp
  • Implemented setStateTransitionMonitor to register monitor and attach listener
  • Added trackStateTransition to capture durations and update monitor
presto-main-base/src/main/java/com/facebook/presto/execution/QueryStateMachine.java
Implement QueryStateTransitionMonitor for timing and anomaly detection
  • Created monitor class with TimeStat and CounterStat for each state
  • Implemented registerQuery, recordStateTransition logic, and anomaly logging
  • Exposed metrics and stats summary via JMX getters
presto-main-base/src/main/java/com/facebook/presto/execution/QueryStateTransitionMonitor.java
Bind and export monitor in server DI module
  • Registered QueryStateTransitionMonitor as a singleton
  • Exported monitor via JMX/ODS exporter
presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java
Add comprehensive unit tests for state transition monitoring
  • Tests for query registration, lifecycle transitions, stats collection and anomaly detection
  • Coverage for terminal state cleanup, JMX metrics, concurrent scenarios
presto-main-base/src/test/java/com/facebook/presto/execution/TestQueryStateTransitionMonitor.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
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 there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `presto-main-base/src/main/java/com/facebook/presto/execution/QueryStateMachine.java:1013-1023` </location>
<code_context>
+
+        QueryState prevState = previousState.get();
+        long prevTimestamp = previousStateTimestamp.get();
+        long currentTimestamp = System.currentTimeMillis();
+        long durationMillis = currentTimestamp - prevTimestamp;
+
+        if (prevState != null) {
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Using System.currentTimeMillis() for duration may be affected by system clock changes.

System.nanoTime() is preferred for measuring elapsed time, as it is not affected by system clock changes.

```suggestion
        QueryState prevState = previousState.get();
        long prevTimestampNanos = previousStateTimestamp.get();
        long currentTimestampNanos = System.nanoTime();
        long durationMillis = (currentTimestampNanos - prevTimestampNanos) / 1_000_000;

        if (prevState != null) {
            monitor.recordStateTransition(queryId, prevState, newState, durationMillis);
        }

        previousState.set(newState);
        previousStateTimestamp.set(currentTimestampNanos);
```
</issue_to_address>

### Comment 2
<location> `presto-main-base/src/test/java/com/facebook/presto/execution/TestQueryStateTransitionMonitor.java:50-59` </location>
<code_context>
+        assertNotNull(monitor.getStatsSummary());
+    }
+
+    @Test
+    public void testConcurrentQueryRegistration()
+    {
+        int numQueries = 100;
+        for (int i = 0; i < numQueries; i++) {
+            QueryId queryId = new QueryId("concurrent_query_" + i);
+            monitor.registerQuery(queryId);
+            monitor.recordStateTransition(queryId, DISPATCHING, PLANNING, 1000L + i);
+        }
+
+        assertEquals(monitor.getTotalQueriesTracked(), numQueries);
+    }
+
+    @Test
+    public void testStateTransitionWithZeroDuration()
+    {
+        monitor.registerQuery(testQueryId);
</code_context>

<issue_to_address>
**suggestion (testing):** Edge case for negative duration is not tested.

Please add a test for negative duration values in state transitions to verify the monitor's behavior in these cases.

Suggested implementation:

```java
    @Test
    public void testStateTransitionWithNegativeDuration()
    {
        monitor.registerQuery(testQueryId);
        // Attempt to record a state transition with a negative duration
        monitor.recordStateTransition(testQueryId, DISPATCHING, PLANNING, -500L);
        // Assert that the query is still tracked and no exception is thrown
        assertEquals(monitor.getTotalQueriesTracked(), 1);
        assertEquals(monitor.getActiveQueriesCount(), 1);
        // Optionally, check stats summary if available
        assertNotNull(monitor.getStatsSummary());
    }

    @Test
    public void testStateTransitionWithZeroDuration()
    {
        monitor.registerQuery(testQueryId);

```

If the monitor is expected to handle negative durations in a specific way (e.g., clamp to zero, ignore, or log a warning), you may want to add more specific assertions based on the implementation of `recordStateTransition` and `getStatsSummary()`. Adjust the assertions as needed to match the expected behavior.
</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.

Comment on lines +1013 to +1023
QueryState prevState = previousState.get();
long prevTimestamp = previousStateTimestamp.get();
long currentTimestamp = System.currentTimeMillis();
long durationMillis = currentTimestamp - prevTimestamp;

if (prevState != null) {
monitor.recordStateTransition(queryId, prevState, newState, durationMillis);
}

previousState.set(newState);
previousStateTimestamp.set(currentTimestamp);
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Using System.currentTimeMillis() for duration may be affected by system clock changes.

System.nanoTime() is preferred for measuring elapsed time, as it is not affected by system clock changes.

Suggested change
QueryState prevState = previousState.get();
long prevTimestamp = previousStateTimestamp.get();
long currentTimestamp = System.currentTimeMillis();
long durationMillis = currentTimestamp - prevTimestamp;
if (prevState != null) {
monitor.recordStateTransition(queryId, prevState, newState, durationMillis);
}
previousState.set(newState);
previousStateTimestamp.set(currentTimestamp);
QueryState prevState = previousState.get();
long prevTimestampNanos = previousStateTimestamp.get();
long currentTimestampNanos = System.nanoTime();
long durationMillis = (currentTimestampNanos - prevTimestampNanos) / 1_000_000;
if (prevState != null) {
monitor.recordStateTransition(queryId, prevState, newState, durationMillis);
}
previousState.set(newState);
previousStateTimestamp.set(currentTimestampNanos);

@github-actions
Copy link

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@abhinavmuk04 abhinavmuk04 changed the title [Presto-Reliability] Add observability for query state transitions in Presto feat: Add observability for query state transitions in Presto Oct 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

from:Meta PR from Meta

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants