mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-03-20 09:01:05 +08:00
# Description of Changes
Update the Rust client SDK to use the new V2 WebSocket format, and
present the V2 user-facing API.
## Reducer events
### Remove on-reducer callbacks
It's no longer possible to observe reducers called by other clients by
registering callbacks with `ctx.reducers.on_{my_reducer}`. We no longer
code-generate those methods, or the associated
`ctx.reducers.remove_on_{my_reducer}`. Internal plumbing for storing and
invoking those callbacks is also removed.
### Add specific reducer invocation callbacks
In addition to the previous way to invoke reducers,
`ctx.reducers.{my_reducer}(args...)`, we add a method that registers a
callback to run after the reducer is finished. This method has the
suffix `_then`, as in `ctx.reducers.{my_reducer}_then(args...,
callback)`.
The callback will accept two arguments:
- `ctx: &ReducerEventContext`, the same context as was previously passed
to on-reducer callbacks.
- `status: Result<Result<(), String>, InternalError>`, denoting the
outcome of the reducer.
- `Ok(Ok(())` means the reducer committed. This corresponds to
`ReducerOutcome::Ok` or `ReducerOutcome::Okmpty` in the new WS format.
- `Ok(Err(message))` means the reducer returned an "expected" or "user"
error. This corresponds to `ReducerOutcome::Err` in the new WS format.
- `Err(internal_error)` means something went wrong with host execution.
This corresponds to `ReducerOutcome::InternalError` in the new WS
format.
Internally, the SDK stores the callbacks in its `ReducerCallbacks` map.
This is keyed on `request_id: u32`, a number that is generated for each
reducer call (from an `AtomicU32` that we increment each time), and
included in the `ClientMessage::CallReducer` request. The
`ServerMessage::ReducerResult` includes the same `request_id`, so the
SDK pops out of the `ReducerCallbacks` and invokes the appropriate
callback when processing that message.
These new callbacks are very similar to the existing procedure
callbacks.
### The `Event` exposed to row callbacks
Row callbacks caused by a reducer invoked by this client will see
`Event::Reducer`, the same as they would prior to this PR. These
callbacks will be the result of a `ServerMessage::ReducerResult` with
`ReducerOutcome::Ok`. In order to expose the reducer name and arguments
to this event, the client stores them in its `ReducerCallbacks` map,
alongside the callback for when the reducer is complete.
Row callbacks caused by any other reducer, or any non-reducer
transaction, are now indistinguishable to the client. These will see
`Event::Transaction`, which is renamed from the old
`Event::UnknownTransaction`.
### Less metadata in `ReducerEvent`
Some metadata is removed from `ReducerEvent`, as the V2 WebSocket format
no longer publishes it, even to the caller.
## `CallReducerFlags` are removed
All machinery for setting, storing and applying call reducer flags is
removed from the SDK, as the new WS format does not have any non-default
flags.
## Requesting rows in unsubscribe
When sending a `ClientMessage::Unsubscribe`, we always request that the
server include the matching rows in its response
`ServerMessage::UnsubscribeApplied`. This saves us having to update the
SDK to store query sets separately, at least for now. (We'll do that
later.)
## Handling rows
The new SDK does some additional parsing to wrangle rows in the new
WebSocket format into the same internal data structures as before,
rather than re-writing the client cache. (We'll do that later.)
Specifically, parsing of `DbUpdate` is changed so that:
- We parse raw `TransactionUpdate` into the generated `DbUpdate` type,
which requires an additional loop compared to the previous version, to
cope with the new WS format's dividing updates by query set. We define a
function `transaction_update_iter_table_updates` which encapsulates this
nested loop in an iterator.
- We have two new functions for parsing raw `QueryRows` into the
generated `DbUpdate` type, one for when they come from a
`SubscribeApplied`, and the other when they come from an
`UnsubscribeApplied`. `QueryRows` from `SubscribeApplied` translate to a
`DbUpdate` of all inserts, while one from `UnsubscribeApplied` will be
all deletes.
## Legacy subscriptions
"Legacy subscriptions" are removed. These were only used for
`subscribe_to_all_tables`, which as of now is stubbed. I will follow up
with a change to re-implement `subscribe_to_all_tables` by
code-generating a list of all known tables, and having it subscribe to
`select * from {table}` for every table in that list.
## `subscribe_to_all_tables` via a list
Previously, `subscribe_to_all_tables` worked by sending a legacy
subscription with the query `SELECT * FROM *`, which the host had
special handling to expand to subscribing to all tables. As legacy
subscriptions are no longer usable in V2 clients, this can't work.
Instead, we code-generate `SpacetimeModule::ALL_TABLE_NAMES`, a list of
all the known table names. `subscribe_to_all_tables` then maps across
this list to construct a list of queries in the form `SELECT * FROM
{table_name}`, and subscribes to all of those queries. This has the
upside that defining a new table in the module without regenerating
client bindings will no longer result in the client seeing rows of
tables it does not know about and cannot parse.
## Light mode removed
Light mode is no longer meaningful in the V2 WS format, so all code
related to it is removed.
## Internal changes
### Renamed WS messages
The SDK's internal code is updated to account for various renames:
- `QueryId` -> `QuerySetId`, `query_id` -> `query_set_id`.
- `SubscribeMulti` -> `Subscribe`, `UnsubscribeMulti` -> `Unsubscribe`.
## Incidental changes in this PR, not necessary for other client SDKs
### Don't filter out empty ranges in `RowSizeHint`
The Rust implementation of `RowSizeHint` in `BsatnRowList` got regressed
in the base branch to not work with zero-sized rows. This change fixes
that.
# API and ABI breaking changes
Boy howdy is it!
# Expected complexity level and risk
3? Changes ended up being less complicated than I feared, but we do have
some fiddly code here, and we have internal dependencies on the SDK.
# Testing
<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->
- [x] Updated automated test suite.
- Known failures:
- [ ] `subscribe_all_select_star`, which is currently broken because
it's trying to subscribe to rows from private tables. #4241 will fix
this.
---------
Co-authored-by: Jeffrey Dallatezza <jeffreydallatezza@gmail.com>
Co-authored-by: = <cloutiertyler@gmail.com>
99 lines
2.4 KiB
JSON
99 lines
2.4 KiB
JSON
{
|
|
"nodes": {
|
|
"crane": {
|
|
"locked": {
|
|
"lastModified": 1770419512,
|
|
"narHash": "sha256-o8Vcdz6B6bkiGUYkZqFwH3Pv1JwZyXht3dMtS7RchIo=",
|
|
"owner": "ipetkov",
|
|
"repo": "crane",
|
|
"rev": "2510f2cbc3ccd237f700bb213756a8f35c32d8d7",
|
|
"type": "github"
|
|
},
|
|
"original": {
|
|
"owner": "ipetkov",
|
|
"repo": "crane",
|
|
"type": "github"
|
|
}
|
|
},
|
|
"flake-utils": {
|
|
"inputs": {
|
|
"systems": "systems"
|
|
},
|
|
"locked": {
|
|
"lastModified": 1731533236,
|
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
"owner": "numtide",
|
|
"repo": "flake-utils",
|
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
"type": "github"
|
|
},
|
|
"original": {
|
|
"owner": "numtide",
|
|
"repo": "flake-utils",
|
|
"type": "github"
|
|
}
|
|
},
|
|
"nixpkgs": {
|
|
"locked": {
|
|
"lastModified": 1770537093,
|
|
"narHash": "sha256-pF1quXG5wsgtyuPOHcLfYg/ft/QMr8NnX0i6tW2187s=",
|
|
"owner": "NixOS",
|
|
"repo": "nixpkgs",
|
|
"rev": "fef9403a3e4d31b0a23f0bacebbec52c248fbb51",
|
|
"type": "github"
|
|
},
|
|
"original": {
|
|
"owner": "NixOS",
|
|
"ref": "nixpkgs-unstable",
|
|
"repo": "nixpkgs",
|
|
"type": "github"
|
|
}
|
|
},
|
|
"root": {
|
|
"inputs": {
|
|
"crane": "crane",
|
|
"flake-utils": "flake-utils",
|
|
"nixpkgs": "nixpkgs",
|
|
"rust-overlay": "rust-overlay"
|
|
}
|
|
},
|
|
"rust-overlay": {
|
|
"inputs": {
|
|
"nixpkgs": [
|
|
"nixpkgs"
|
|
]
|
|
},
|
|
"locked": {
|
|
"lastModified": 1770606655,
|
|
"narHash": "sha256-rpJf+kxvLWv32ivcgu8d+JeJooog3boJCT8J3joJvvM=",
|
|
"owner": "oxalica",
|
|
"repo": "rust-overlay",
|
|
"rev": "11a396520bf911e4ed01e78e11633d3fc63b350e",
|
|
"type": "github"
|
|
},
|
|
"original": {
|
|
"owner": "oxalica",
|
|
"repo": "rust-overlay",
|
|
"type": "github"
|
|
}
|
|
},
|
|
"systems": {
|
|
"locked": {
|
|
"lastModified": 1681028828,
|
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
"owner": "nix-systems",
|
|
"repo": "default",
|
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
"type": "github"
|
|
},
|
|
"original": {
|
|
"owner": "nix-systems",
|
|
"repo": "default",
|
|
"type": "github"
|
|
}
|
|
}
|
|
},
|
|
"root": "root",
|
|
"version": 7
|
|
}
|