Skip to content

[FEATURE] Add PPL asynchronous query lifecycle core - #5809

Open
penghuo wants to merge 12 commits into
opensearch-project:mainfrom
penghuo:feat/asyncquery-core
Open

penghuo wants to merge 12 commits into
opensearch-project:mainfrom
penghuo:feat/asyncquery-core

Conversation

@penghuo

@penghuo penghuo commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Description

This is the first implementation slice of #5796, under the PPL async-query meta issue #5765. It adds the execution contract and owner-node lifecycle core without registering REST routes or changing the existing synchronous PPL API.

The lifecycle module depends only on:

public interface AsyncQueryExecution extends AutoCloseable {
  Optional<QueryResponse> currentResult();
  CompletionStage<Void> completion();
  void close();
}

A successful final result must be visible through currentResult() before completion() completes normally.

Core components:

  • PPLAsyncQueryJob: synchronized state machine for one query.
  • PPLAsyncQueryService: job registry, timeout race, leases, capacity, task ownership, cancellation, expiration, and cleanup.
  • PPLAsyncQueryJobId: opaque owner-routable identifier.
  • PPLAsyncQueryUser: retained-job owner identity.
  • DefaultAsyncQueryExecution: final-result adapter for existing PPL execution.

Result before timeout

sequenceDiagram
    participant C as Submit caller
    participant S as PPLAsyncQueryService
    participant J as PPLAsyncQueryJob
    participant E as AsyncQueryExecution
    participant T as Wait timeout
    C->>S: start(wait)
    S->>J: create RUNNING
    S->>T: schedule
    S->>E: start and attach
    E-->>S: completion()
    S-->>T: cancel
    S->>J: complete()
    J-->>S: DirectResponse
    S->>E: currentResult()
    S-->>C: final snapshot without ID
    S->>E: close()
Loading

Timeout before result

sequenceDiagram
    participant C as Submit caller
    participant S as PPLAsyncQueryService
    participant J as PPLAsyncQueryJob
    participant E as AsyncQueryExecution
    participant T as Wait timeout
    C->>S: start(wait)
    S->>J: create RUNNING
    S->>T: schedule
    S->>E: start and attach
    T-->>S: wait expires
    S->>J: retain()
    J-->>S: Retained(RUNNING)
    S->>E: currentResult()
    S-->>C: RUNNING snapshot with ID
    E-->>S: completion()
    S->>J: complete()
    J-->>S: ExecutionFinished
    C->>S: get(ID)
    S->>J: get()
    J-->>S: succeeded snapshot source
    S->>E: currentResult()
    S-->>C: retained final snapshot
Loading

DELETE, expiration, startup abort, and shutdown produce typed removal transitions. The service removes registry state and releases tasks/executions after leaving the job lock.

The follow-up PR adds request parsing, REST/transport actions, owner-node forwarding, response formatting, and integration tests.

Related Issues

Part of #5796.
Parent meta issue: #5765.

Check List

  • New functionality includes testing.
  • New functionality has been documented.
    • New functionality has javadoc added.
    • User manual documentation is not applicable until the public API PR.
  • New PPL command checklist is not applicable.
  • API specification PR is not applicable because this PR exposes no endpoint.
  • Commits are signed per the DCO using --signoff.
  • Public documentation issue/PR will accompany the public API PR.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
Signed-off-by: Peng Huo <penghuo@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Code Analyzer ❗

AI-powered 'Code-Diff-Analyzer' found issues on commit 7abe947.

PathLineSeverityDescription
plugin/build.gradle171highNew dependency added: 'org.opensearch:common-utils:${opensearch_build}'. Per mandatory review policy, all dependency additions must be flagged regardless of whether the artifact name appears legitimate. Maintainers should verify the artifact resolves to the expected OpenSearch common-utils package from a trusted registry and that the version pinned via ${opensearch_build} is appropriate.

The table above displays the top 10 most important findings.

Total: 1 | Critical: 0 | High: 1 | Medium: 0 | Low: 0


Pull Requests Author(s): Please update your Pull Request according to the report above.

Repository Maintainer(s): You can bypass diff analyzer by adding label skip-diff-analyzer after reviewing the changes carefully, then re-run failed actions. To re-enable the analyzer, remove the label, then re-run all actions.


⚠️ Note: The Code-Diff-Analyzer helps protect against potentially harmful code patterns. Please ensure you have thoroughly reviewed the changes beforehand.

Thanks.

@penghuo penghuo self-assigned this Sep 24, 2026
@penghuo penghuo added PPL Piped processing language enhancement New feature or request labels Sep 24, 2026
@codecov

codecov Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.26%. Comparing base (f2f4893) to head (7abe947).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...c/main/java/org/opensearch/sql/ppl/PPLService.java 50.00% 4 Missing ⚠️
...opensearch/sql/ppl/DefaultAsyncQueryExecution.java 90.00% 1 Missing ⚠️

❌ Your project check has failed because the head coverage (63.26%) is below the target coverage (99.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5809      +/-   ##
============================================
+ Coverage     63.23%   63.26%   +0.02%     
- Complexity     8823     8829       +6     
============================================
  Files           938      939       +1     
  Lines         40236    40276      +40     
  Branches       4537     4537              
============================================
+ Hits          25445    25480      +35     
- Misses        13966    13971       +5     
  Partials        825      825              
Flag Coverage Δ
sql-engine 63.26% <87.50%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

* <p>This class is thread-safe. The registry is concurrent, capacity counters are guarded by {@code
* admissionLock}, and each job synchronizes its own lifecycle transitions.
*/
public final class PPLAsyncQueryService extends AbstractLifecycleComponent {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  1. Can we make this new lifecycle layer language-neutral, similar to Livy’s control plane over Spark?
  2. Probably the execution boundary can also be engine-neutral? SQL and PPL can both run on AE too, although AE does not currently support progress listeners.

* lock.
*
* <pre>
* Every job starts in RUNNING. It becomes retained when wait_for_completion_timeout expires.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we separate execution state from retention and treat wait_for_completion_timeout only as submit-response behavior? In a BigQuery-style model, the job remains RUNNING whether or not the request is still waiting.

Comment on lines +189 to +198
private final Supplier<String> ownerNodeIdSupplier;
private final LongSupplier currentTimeMillis;
private final TimeoutScheduler timeoutScheduler;
private final IntSupplier maxRunningQueries;
private final IntSupplier maxRetainedJobs;
private final Supplier<TimeValue> maxWaitForCompletion;
private final Supplier<TimeValue> maxKeepAlive;
private final ThreadPool threadPool;
private final ConcurrentMap<String, PPLAsyncQueryJob> jobs = new ConcurrentHashMap<>();
private final Object admissionLock = new Object();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

wondering shall we extract serveral internal components to offload the responsibilities of this service class, such as async-query-core’s JobStore, LeaseManager etc?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request PPL Piped processing language

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants