Skip to content

feat(surrealdb-ractor): implement LiveQueryRouter::run + live_stream (Sprint 1 follow-up to #24)#25

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/surrealdb-ractor-live-query-sprint1
May 19, 2026
Merged

feat(surrealdb-ractor): implement LiveQueryRouter::run + live_stream (Sprint 1 follow-up to #24)#25
AdaWorldAPI merged 1 commit into
mainfrom
claude/surrealdb-ractor-live-query-sprint1

Conversation

@AdaWorldAPI
Copy link
Copy Markdown
Owner

Summary

Follow-up to merged PR #24 (Sprint 0/1 surrealdb-ractor scaffolds). The wave-5 W-SD-5 commit 74d4afd was pushed to the branch after #24 merged, so the real Sprint-1 implementation of LiveQueryRouter got stranded. This PR brings it onto main.

What lands

Replaces the wave-1 unimplemented!() stubs in surrealdb-ractor with real bodies using SurrealDB 3.1.0-alpha's live-query subscription API:

  • surrealdb-ractor/src/stream.rslive_stream(query) -> impl Stream<Item = Result<LiveDelta>> uses db.query("LIVE SELECT * FROM table").stream::<Value>(0) (surrealdb/src/method/query.rs:394). The idiomatic db.select::<Vec<T>>(table).live() shape from the plan sketch hit a trait-bound wall — &str satisfies IntoResource<Vec<R>> but not IntoResource<Value>, and surrealdb_types::Value doesn't implement SurrealValue required for Notification<R>. The query() path accepts any SQL string and returns QueryStream<Value>.
  • surrealdb-ractor/src/router.rsLiveQueryRouter<M>::run loops over the stream, maps NotificationLiveDelta via the Action enum (Create/Update/Delete/Killed at surrealdb/types/src/notification.rs:18), and forwards to the configured actor via M::from(delta).
  • surrealdb-ractor/src/delta.rs — minor revisions to support the Value payload form (Arrow conversion deferred to Sprint 2; current impl carries serde_json::Value behind the variant).
  • surrealdb-ractor/Cargo.toml — added serde (with derive) + tracing.
  • surrealdb-ractor/tests/live_query.rs (new) — e2e test: spin up in-memory surrealdb with Capabilities::all() (live-query notifications are gated on the local engine), DEFINE TABLE events SCHEMALESS, start router watching SELECT * FROM events, INSERT 3 records, assert counter sees 3 Create events. Passes in 1.29s.

Real-world deviations from the plan sketch (documented inline)

  1. db.select::<Vec<T>>(table).live() not usable for Value payloads → query() path used instead.
  2. surrealdb 3.1.0-alpha requires DEFINE TABLE ... SCHEMALESS before LIVE SELECT.
  3. In-memory engine requires Capabilities::all() for notifications to fire.

Test plan

  • cargo check --manifest-path surrealdb-ractor/Cargo.toml — clean
  • cargo test --manifest-path surrealdb-ractor/Cargo.toml --test live_query — 1/1 passes in 1.29s
  • Sprint 2: convert the Value payload to a proper Arrow RecordBatch so consumers downstream of sea-orm-arrow can stay in columnar form (out of scope)
  • Sprint 2: add the EntityActor dispatch glue so delta.primary_key("id") routes directly to Ticket::Entity::actor(pk) (cross-repo: sea-orm-ractor side)

Context

Worker: W-SD-5. Sprint 1 of the four-repo integration.

https://claude.ai/code/session_01LiUiGeUDLje8KMnxB4FfA3


Generated by Claude Code

…(Sprint 1)

Wave-5 worker W-SD-5. Replaces wave-1 `unimplemented!()` stubs with
real bodies using SurrealDB 3.1.0-alpha's live-query subscription API.

* surrealdb-ractor/src/stream.rs — `live_stream(query) -> impl Stream<Item = Result<LiveDelta>>`
  uses `db.query("LIVE SELECT * FROM table").stream::<Value>(0)`
  (surrealdb/src/method/query.rs:394). The idiomatic
  `db.select::<Vec<T>>(table).live()` shape from the plan sketch hit a
  trait-bound wall — `&str` satisfies `IntoResource<Vec<R>>` but not
  `IntoResource<Value>`, and `surrealdb_types::Value` doesn't implement
  `SurrealValue` required for `Notification<R>`. The query() path
  accepts any SQL string and returns `QueryStream<Value>`.
* surrealdb-ractor/src/router.rs — `LiveQueryRouter<M>::run` loops over
  the stream, maps Notification → LiveDelta via Action enum
  (Create/Update/Delete/Killed at surrealdb/types/src/notification.rs:18),
  and forwards to the configured actor via `M::from(delta)`.
* surrealdb-ractor/src/delta.rs — minor revisions to support the Value
  payload form (Arrow conversion deferred to Sprint 2; current impl
  carries serde_json::Value behind the variant).
* surrealdb-ractor/Cargo.toml — added serde (with derive) + tracing.
* surrealdb-ractor/tests/live_query.rs — e2e test: spin up in-memory
  surrealdb with `Capabilities::all()` (live-query notifications are
  gated on the local engine), DEFINE TABLE events SCHEMALESS, start
  router watching SELECT * FROM events, INSERT 3 records, assert
  counter sees 3 Create events. Passes in 1.29s.

Real-world deviations from plan sketch (documented inline):
1. `select().live()` not usable for Value payloads → query() path.
2. 3.1.0-alpha requires DEFINE TABLE before LIVE SELECT.
3. In-memory engine requires Capabilities::all() for notifications.

Verdict: LAND. cargo check clean, 1/1 e2e test passes.

Worker: W-SD-5. Sprint 1 of the four-repo integration.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a485f1a37

ℹ️ 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".

//
// SDK path: surrealdb/src/method/query.rs:394 (IndexedResults::stream)
// surrealdb/src/method/query.rs:164 (live-query UUID extraction)
let sql = format!("LIVE SELECT * FROM {table}");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept query strings instead of treating them as table names

When callers use LiveQueryRouter::query as the SurrealQL query string shown in the integration plan (for example, SELECT * FROM events WHERE category = 'sale' or a full LIVE SELECT ...), this unconditional wrapping produces invalid SQL like LIVE SELECT * FROM SELECT * FROM events ... and the router fails before subscribing. Either pass through full query strings or rename/restrict the API to validated table identifiers so documented filtered subscriptions keep working.

Useful? React with 👍 / 👎.

@AdaWorldAPI AdaWorldAPI merged commit df97e31 into main May 19, 2026
AdaWorldAPI added a commit that referenced this pull request May 19, 2026
Revert #24 + #25 — surrealdb-ractor four-repo integration
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.

2 participants