Skip to content

Add live query kill() and expose the live-query UUID up front#184

Open
emmanuel-keller wants to merge 6 commits into
mainfrom
emmanuel/live-query-kill
Open

Add live query kill() and expose the live-query UUID up front#184
emmanuel-keller wants to merge 6 commits into
mainfrom
emmanuel/live-query-kill

Conversation

@emmanuel-keller

Copy link
Copy Markdown
Collaborator

What

Completes the live-query feature surface:

  • Surreal.kill(String queryId) / Surreal.kill(java.util.UUID) — terminate a live query by id via a validated KILL statement.
  • LiveStream.getQueryId() — the live-query UUID, available immediately (before the first notification).

selectLive(table) is reworked internally to start the live query through the public LIVE SELECT query path (query("LIVE SELECT * FROM type::table($tb)")) instead of the .select(table).live() builder, whose query id is pub(crate) and therefore unavailable to the driver. The statement result yields the UUID, captured up front and carried on the returned LiveStream; the notification stream (Response.stream(0)) is driven on the same background-thread machinery as before, so notification delivery, thread-safety, and shutdown are unchanged. Subscription errors (e.g. unknown table) are still surfaced eagerly via take(0).

This also un-disables LiveQueryTests.killLiveQuery_byQueryId, which was waiting on Surreal.kill(...).

Why

Live-query subscription (selectLiveLiveStream → notifications) already existed, but there was no way to terminate a live query by id, and the UUID was only reachable after the first notification (via LiveNotification.getQueryId()).

Reviewer notes — kill semantics

kill() terminates the query (notifications stop) but does not close a client LiveStream on either engine: the KILL-generated Killed notification carries session: None, which both the embedded router and the WebSocket routing drop. A thread blocked in LiveStream.next() keeps waiting after a kill — use LiveStream.close() to release a local stream. kill(id) is for terminating a query you only hold the id of (e.g. read from a notification, or started on another connection). This is documented in the kill javadoc and asserted by the tests (which check "no notifications after kill", not "stream ends").

Tests

  • LiveQueryTests: enabled killLiveQuery_byQueryId; added selectLiveExposesQueryIdUpFront.
  • LiveQueryWebSocketTests: added selectLive_overWebSocket_kill_stopsNotifications (verified against a local SurrealDB server).
  • Full embedded suite green; WebSocket suite green against a local server. cargo clippy, cargo fmt --check, and spotlessCheck all clean.

Built against surrealdb 3.1.4.

`selectLive` now starts the live query via the public `LIVE SELECT`
query path instead of the `.select(table).live()` builder (whose query
id is `pub(crate)`). This lets the driver capture the live-query UUID
eagerly from the statement result and expose it via the new
`LiveStream.getQueryId()` — available before any notification arrives.
Subscription errors (e.g. unknown table) are still surfaced eagerly.

Add `Surreal.kill(String)` / `Surreal.kill(UUID)` to terminate a live
query by id via a validated `KILL` statement, and enable the previously
`@Disabled` `killLiveQuery_byQueryId` test plus new embedded and
WebSocket coverage.

Note: `kill()` terminates the query (notifications stop) but does not
close a client `LiveStream` on either engine — the KILL `Killed`
notification carries `session: None` and is dropped by the routers — so
`LiveStream.close()` is what releases a local stream. Documented in the
`kill` javadoc and asserted by the tests.
chatgpt-codex-connector[bot]

This comment was marked as outdated.

kill() previously only checked the outer query result and discarded the
response, so a server-rejected KILL (e.g. an unknown or not-owned live
query, which the KILL statement raises as a per-statement error stored at
index 0) was swallowed and kill() returned normally. Take/check the
index-0 result via take_one_result! so the failure surfaces as a
SurrealException.
@emmanuel-keller

Copy link
Copy Markdown
Collaborator Author

Re-requesting review after addressing the two findings in 54c29e0:

  • Surface per-statement KILL failureskill() now takes/checks the index-0 statement result via take_one_result!, so a server-rejected KILL (unknown/not-owned live query) surfaces as a SurrealException instead of being swallowed.
  • Handle the KILL notification before forwarding — no change needed: the SDK's Stream<Value> maps Action::Killed → end-of-stream (Poll::Ready(None)), so a kill is never forwarded as a LiveNotification; on both engines the Killed event also carries session: None and is dropped before reaching the stream. The embedded killLiveQuery_byQueryId and selectLive_overWebSocket_kill_stopsNotifications tests assert the "no notifications after kill" contract and pass.

Per-comment detail is on the two inline threads. cargo clippy/cargo fmt --check plus the full embedded suite and the WebSocket suite (against a local server) are green.

🤖 Reply by Claude Code

chatgpt-codex-connector[bot]

This comment was marked as outdated.

The surrealdb client SDK builds IndexedResults with `QueryType::Kill => {}`,
discarding the KILL statement result (including any per-statement error), and
its typed kill is `pub(crate)`. There is therefore no public path to observe a
server-side KILL rejection (e.g. a live query owned by another session):
take(0) on the KILL response only ever yields Value::None.

Revert the ineffective take_one_result check added in 54c29e0 (keep
check_query_result! for transport/connection errors) and document kill() as
best-effort in both the javadoc and the code.
chatgpt-codex-connector[bot]

This comment was marked as outdated.

The `.select(table).live()` builder unwraps a one-element
`Value::Array([Value::Uuid])` result shape (method/live.rs). Mirror that in the
raw-query path's UUID extraction so selectLive() preserves the builder's
result-shape handling across server/protocol versions.

With surrealdb 3.1.4 the `QueryType::Live` path normalizes the id to a bare
`Value::Uuid` via `value.into_uuid()` before storing it in IndexedResults, so
`take(0)` yields a scalar there (covered by the existing tests); the array arm
is defensive parity for any path/version that stores the raw shape.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 2d8be84a05

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Add a Features bullet documenting live queries (selectLive + kill). Remove the
two Planned Features entries: "Killing live queries by ID" (implemented in this
PR) and "Futures" (no longer a SurrealDB feature).
Both items were removed (one shipped, one no longer a SurrealDB feature),
leaving the section empty; drop the heading and the lone feature-request link.
@emmanuel-keller emmanuel-keller marked this pull request as ready for review June 19, 2026 17:53
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.

1 participant