Skip to content

feat: add listActiveQueries and cancelActiveQuery for running SQL queries - #53

Merged
krinart merged 5 commits into
trunkfrom
feat/active-queries
Aug 21, 2026
Merged

krinart merged 5 commits into
trunkfrom
feat/active-queries

Conversation

@krinart

@krinart krinart commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds SpiceClient.listActiveQueries() (GET /v1/sql/active) and SpiceClient.cancelActiveQuery(queryId) (POST /v1/sql/{id}/cancel) to inspect and cancel synchronous queries running on the runtime.

Test plan

  • New unit tests (ActiveQueriesTest) against a local mock HTTP server, covering success, error-status handling, and client-side validation of the query ID.
  • mvn compile and the new test suite pass locally.

…ries

The runtime exposes GET /v1/sql/active and POST /v1/sql/{id}/cancel for
listing and cancelling currently running synchronous queries, but neither
was reachable from this SDK (gospice already has this pair; dotnet does not).
Copilot AI balanced review requested due to automatic review settings August 19, 2026 18:24

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.

Pull request overview

Adds APIs for listing and cancelling active synchronous SQL queries.

Changes:

  • Adds ActiveQuery and client operations.
  • Adds HTTP mock-based unit tests.
  • Documents active-query management.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
SpiceClient.java Implements list and cancel operations.
ActiveQuery.java Defines active-query metadata.
ActiveQueriesTest.java Tests requests, responses, and validation.
README.md Documents API usage and scope.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/test/java/ai/spice/ActiveQueriesTest.java
Comment thread src/main/java/ai/spice/SpiceClient.java Outdated
Comment thread README.md Outdated
Comment thread src/main/java/ai/spice/SpiceClient.java
Comment thread src/main/java/ai/spice/SpiceClient.java
Comment thread README.md
@krinart krinart self-assigned this Aug 19, 2026
withApiKey() makes SpiceClient's constructor perform a real Flight
handshake, but the two tests that configure an API key only stood up
the HTTP mock server, not a Flight server. The handshake against a
dead default Flight address failed unpredictably by platform
(reliable on Windows CI, intermittent on Linux/macOS). Start a
TestFlightSqlServer with matching credentials for those cases.
Copilot AI review requested due to automatic review settings August 19, 2026 18:33

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/main/java/ai/spice/SpiceClient.java:1712

  • This concatenates the base URI, so a supported address ending in / produces //v1/sql/.../cancel and misses the runtime route. buildProbeRequest explicitly uses URI.resolve to avoid this case; resolve the cancellation path as well.
                    .uri(new URI(String.format("%s/v1/sql/%s/cancel", this.httpAddress, queryId)))

src/main/java/ai/spice/SpiceClient.java:1670

  • A successful response with a missing or non-array queries member is reported as “no active queries.” That hides an incompatible or malformed runtime response even though this public method promises to throw for unexpected responses; reject the invalid shape instead of silently returning an empty result.
        if (queries == null || !queries.isJsonArray()) {
            return Collections.emptyList();

src/main/java/ai/spice/SpiceClient.java:1619

  • buildProbeRequest does not set a per-request timeout, so after a TCP connection succeeds this synchronous call can wait indefinitely if the runtime never sends a response. Cancellation and refresh both apply HTTP_REQUEST_TIMEOUT; the listing request needs the same bound.
            HttpRequest request = buildProbeRequest(this.httpAddress, "/v1/sql/active", this.apiKey);

- Resolve the cancel/list URIs against the base address instead of
  concatenating, so a trailing-slash httpAddress doesn't miss the route.
- Bound listActiveQueries with the same request timeout cancel/refresh use.
- Reject a present-but-malformed "queries" field instead of silently
  reporting zero active queries.
- Document that runtime releases through v2.1.5 don't scope either
  endpoint by credential.
- Fix an undeclared variable in the README example.
Copilot AI review requested due to automatic review settings August 19, 2026 18:46

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/main/java/ai/spice/SpiceClient.java:1707

  • Non-object entries are silently dropped, turning a malformed response into an incomplete active-query list. Reject these entries instead; otherwise callers may be told that a running query does not exist and lose the ability to cancel it.
        for (JsonElement element : queries.getAsJsonArray()) {
            if (element.isJsonObject()) {
                result.add(GSON.fromJson(element, ActiveQuery.class));
            }
        }

src/main/java/ai/spice/SpiceClient.java:1700

  • The endpoint contract always returns a queries array, so treating a missing member as an empty result silently accepts a malformed or incompatible 200 response. That can report “no active queries” when the response was not actually valid; reject an absent member just as this method already rejects a non-array member.

This issue also appears on line 1703 of the same file.

        if (queries == null) {
            return Collections.emptyList();

README.md:432

  • This example throws IndexOutOfBoundsException whenever there are no active queries, which is a normal result of listActiveQueries(). Guard the cancellation so the documented usage is safe to copy.
client.cancelActiveQuery(queries.get(0).getQueryId());

@krinart krinart mentioned this pull request Aug 19, 2026
krinart added a commit that referenced this pull request Aug 19, 2026
#53 (active queries) and #56 (async queries) will not merge for this
release; remove them from the release notes' What's New and reflect
that in Release status instead of listing them as still-pending
dependencies.
krinart added a commit that referenced this pull request Aug 20, 2026
* release: prepare v0.8.0

Bumps pom.xml/Version.java to 0.8.0 and adds release notes covering the
four additive feature PRs (#53-#56): search, Nsql/NsqlGenerateSql,
active-query management, and async queries.

japicmp.oldVersion stays at 0.6.0 rather than bumping to 0.7.0: v0.7.0
was tagged and GitHub-released but its Maven Central publish never
completed, so it isn't a resolvable dependency. See the pom.xml comment
for detail.

* docs: reflect that #54 (search) and #55 (nsql) have merged

* docs: scope v0.8.0 to search + nsql only

#53 (active queries) and #56 (async queries) will not merge for this
release; remove them from the release notes' What's New and reflect
that in Release status instead of listing them as still-pending
dependencies.

* Update release notes for v0.8.0

Removed release status section and notes on features not included in v0.8.0.

* Update release notes for v0.8.0

Removed testing section from release notes for v0.8.0.

* Update release notes for v0.8.0

Removed highlights section from release notes for v0.8.0.

* Change japicmp.oldVersion from 0.6.0 to 0.7.0

Updated the old version for japicmp to 0.7.0.
# Conflicts:
#	README.md
#	src/main/java/ai/spice/SpiceClient.java
Copilot AI review requested due to automatic review settings August 21, 2026 16:13

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comment thread src/main/java/ai/spice/SpiceClient.java Outdated
Comment thread src/main/java/ai/spice/SpiceClient.java
Comment thread README.md Outdated
Comment thread src/main/java/ai/spice/SpiceClient.java
- Reject non-object entries in the /v1/sql/active queries array instead of
  silently dropping them, matching the method's malformed-response contract.
- Make 403 messages credential-neutral so mTLS callers aren't pointed at
  API keys.
- Guard the README's cancellation example against an empty query list.
Copilot AI review requested due to automatic review settings August 21, 2026 17:09

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

src/main/java/ai/spice/SpiceClient.java:1685

  • This omits the unauthenticated case: when the runtime establishes no principal, requests share the public scope, so separate clients without credentials can list the same queries. Documenting that boundary avoids implying isolation for no-credential clients.

This issue also appears on line 1795 of the same file.

     * Results are scoped to the authenticated principal — an API key or client
     * certificate — not to this {@code SpiceClient}: every client presenting the
     * same credential lists the same queries. Results also cover only the one
     * runtime instance this client's HTTP endpoint reaches, which behind a load
     * balancer may not be the instance that received a particular query.

README.md:480

  • This security-boundary summary is incomplete for clients configured without credentials. Such requests share the runtime's public scope, allowing unauthenticated clients to list and cancel one another's public queries; please state that explicitly.
Both calls are scoped to the authenticated API key or client certificate — not to this
`SpiceClient` instance — and reach only the one runtime process behind this client's HTTP
endpoint, which matters if that endpoint is a load balancer in front of several runtimes.
Runtime releases up to and including v2.1.5 do not scope either endpoint at all — every
caller sees and can cancel every query regardless of credential.

src/main/java/ai/spice/SpiceClient.java:1799

  • The cancellation scope also needs to describe unauthenticated requests. They share the runtime's public scope, so one no-credential client can cancel a public query submitted by another; the current wording only explains credentialed callers.
     * {@code queryId} comes from {@link #listActiveQueries()}. Cancellation is
     * scoped to the authenticated principal, not to this {@code SpiceClient}: any
     * client presenting the same credential can cancel the query, while an ID
     * outside that scope is reported as not found. Like
     * {@link #listActiveQueries()}, this reaches only the one runtime instance

@krinart
krinart enabled auto-merge (squash) August 21, 2026 20:52
@krinart
krinart merged commit b37a057 into trunk Aug 21, 2026
21 checks passed
@krinart
krinart deleted the feat/active-queries branch August 21, 2026 21:04
krinart added a commit that referenced this pull request Aug 22, 2026
…es async (#58)

* docs: add active query management and async queries to v0.8.0 notes

#53 (listActiveQueries/cancelActiveQuery) has merged to trunk since
v0.8.0's release notes were first written, and #56 (queryAsync/
queryAsyncWithParams) is expected to merge before release. Both are
purely additive, so the compatibility statement is unchanged.

* Update release notes for v0.8.0

Added documentation for asynchronous query execution and active query management features.

* docs: address review comments on v0.8.0 release notes

- Move the AsyncQuery-handle paragraph (status/waitForCompletion/cancel)
  back under Async Queries; it was left stranded under Active Query
  Management by a later reordering of the two sections.
- Reword "Public API unchanged" to "Additive, backward-compatible
  change" -- new public methods were added, so the API did change,
  just without breaking anything existing.

* feat!: rename query/queryWithParams to sql/sqlWithParams; queryAsync becomes query

Matches the breaking-rename pattern already shipped in the dotnet, js,
and python SDKs: query()/queryWithParams() now submit SQL for
asynchronous execution and return an AsyncQuery handle, and the
previous synchronous, streaming behavior moves to new sql()/
sqlWithParams() methods.

- SpiceClient.query(String) -> SpiceClient.sql(String)
- SpiceClient.queryWithParams(String, Object...) -> SpiceClient.sqlWithParams(String, Object...)
- SpiceClient.queryAsync(String) -> SpiceClient.query(String)
- SpiceClient.queryAsyncWithParams(String, Object...) -> SpiceClient.queryWithParams(String, Object...)

Updates every call site and cross-reference in src/main, src/test,
README.md, and docs/parameterized_queries.md. Historical release notes
(v0.5.0.md, v0.6.0.md) are left untouched since they accurately
document what those versions actually shipped at the time.

Full test suite passes unchanged in behavior -- this is a pure rename,
no logic changes.

* docs: document query/queryWithParams -> sql/sqlWithParams as breaking

Replaces the "Additive, backward-compatible change" compatibility
statement, which the rename in the previous commit made false, with an
actual Breaking Changes section describing it: query()/queryWithParams()
now submit for asynchronous execution and return an AsyncQuery handle;
the previous synchronous, streaming behavior moved to sql()/
sqlWithParams(). Also fixes two now-stale sync-path cross-references
(Nsql and Async Queries sections) that still said query()/
queryWithParams() where they meant the new sql()/sqlWithParams().

* build: excuse query/queryWithParams from the japicmp compatibility gate

The gate correctly caught the intentional breaking change: query()'s
return type changed from FlightStream to AsyncQuery, and
queryWithParams()'s from ArrowReader to AsyncQuery, since both now
submit for asynchronous execution instead of streaming results
directly. Documented, scoped exclusions for exactly these two methods
keep the gate meaningful for catching any other, unintended breaking
change in this or a future release.

Verified locally with the exact CI command (mvn checkstyle:check
japicmp:cmp) -- BUILD SUCCESS, and the generated report confirms
SpiceClient is otherwise fully binary- and source-compatible with the
published 0.7.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants