Files
SpacetimeDB/crates/client-api-messages/ws_schema-2.json
Tyler Cloutier 184d4e9d3f Implement server-side support for the v2 websocket protocol (#4213)
# Description of Changes

This adds the v2 websocket protocol and adds support on the server side.
For context on many of the changes/decisions, you can look at the
discussion on https://github.com/clockworklabs/SpacetimeDB/pull/4023.

To restate some of the key changes:
- The reducer event information is no longer sent with transaction
updates (because we don't want to broadcast reducer call information
anymore).
- If a client calls a reducer, they are sent a `ReducerResult` which
includes the outcome of the reducer call and and related row updates for
queries that the client is subscribed to.
- We no longer dedupe queries that appear in multiple query sets for the
same client. This is because we are moving toward per-query storage.
- Related to that, Unsubscribe requests have an option to send the
related rows. We need this for now, since clients don't have per-query
storage implemented yet.
 - We don't have the json format in v2.

Notes for reviewers:
- This moves around the messages in
`crates/client-api-messages/src/websocket` (into `common`, `v1`, and
`v2`), and this renaming of existing messages adds a lot of noise to the
PR.
- In many places, I chose to duplicate a lot of code to have a v1
version and a v2 version. I went with this to make it easier to remove
the v1 version in the future (hopefully we can just fully delete most of
the v1 functions).
- `module_subscription_manager.rs` has probably has the biggest changes,
since we now track queries by query_set_id, and we get to remove some
complexity of v1's FormatSwitch.

<!-- Please describe your change, mention any related tickets, and so on
here. -->

# API and ABI breaking changes

The v1 protocol still works, though we won't send the reducer event info
for v10 modules.

# Expected complexity level and risk

4. This touches a lot of places.

# Testing

Unit testing is pretty minimal for the new code paths. I've done some
manual e2e testing with the typescript quickstart, and this has been
tested with a different branch implementing the v2 rust client.

---------

Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: Jeffrey Dallatezza <jeffreydallatezza@gmail.com>
2026-02-12 20:39:26 +00:00

1 line
9.8 KiB
JSON

{"V8BackCompat":{"typespace":{"types":[{"Sum":{"variants":[{"name":{"some":"CallReducer"},"algebraic_type":{"Ref":1}},{"name":{"some":"Subscribe"},"algebraic_type":{"Ref":2}},{"name":{"some":"OneOffQuery"},"algebraic_type":{"Ref":3}},{"name":{"some":"SubscribeSingle"},"algebraic_type":{"Ref":4}},{"name":{"some":"SubscribeMulti"},"algebraic_type":{"Ref":6}},{"name":{"some":"Unsubscribe"},"algebraic_type":{"Ref":7}},{"name":{"some":"UnsubscribeMulti"},"algebraic_type":{"Ref":8}}]}},{"Product":{"elements":[{"name":{"some":"reducer"},"algebraic_type":{"String":[]}},{"name":{"some":"args"},"algebraic_type":{"Array":{"U8":[]}}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"flags"},"algebraic_type":{"U8":[]}}]}},{"Product":{"elements":[{"name":{"some":"query_strings"},"algebraic_type":{"Array":{"String":[]}}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}}]}},{"Product":{"elements":[{"name":{"some":"message_id"},"algebraic_type":{"Array":{"U8":[]}}},{"name":{"some":"query_string"},"algebraic_type":{"String":[]}}]}},{"Product":{"elements":[{"name":{"some":"query"},"algebraic_type":{"String":[]}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}}]}},{"Product":{"elements":[{"name":{"some":"id"},"algebraic_type":{"U32":[]}}]}},{"Product":{"elements":[{"name":{"some":"query_strings"},"algebraic_type":{"Array":{"String":[]}}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}}]}},{"Sum":{"variants":[{"name":{"some":"InitialSubscription"},"algebraic_type":{"Ref":10}},{"name":{"some":"TransactionUpdate"},"algebraic_type":{"Ref":17}},{"name":{"some":"TransactionUpdateLight"},"algebraic_type":{"Ref":21}},{"name":{"some":"IdentityToken"},"algebraic_type":{"Ref":22}},{"name":{"some":"OneOffQueryResponse"},"algebraic_type":{"Ref":23}},{"name":{"some":"SubscribeApplied"},"algebraic_type":{"Ref":25}},{"name":{"some":"UnsubscribeApplied"},"algebraic_type":{"Ref":27}},{"name":{"some":"SubscriptionError"},"algebraic_type":{"Ref":28}},{"name":{"some":"SubscribeMultiApplied"},"algebraic_type":{"Ref":29}},{"name":{"some":"UnsubscribeMultiApplied"},"algebraic_type":{"Ref":30}}]}},{"Product":{"elements":[{"name":{"some":"database_update"},"algebraic_type":{"Ref":11}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"total_host_execution_duration"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__time_duration_micros__"},"algebraic_type":{"I64":[]}}]}}}]}},{"Product":{"elements":[{"name":{"some":"tables"},"algebraic_type":{"Array":{"Ref":12}}}]}},{"Product":{"elements":[{"name":{"some":"table_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"table_name"},"algebraic_type":{"String":[]}},{"name":{"some":"num_rows"},"algebraic_type":{"U64":[]}},{"name":{"some":"updates"},"algebraic_type":{"Array":{"Ref":13}}}]}},{"Sum":{"variants":[{"name":{"some":"Uncompressed"},"algebraic_type":{"Ref":14}},{"name":{"some":"Brotli"},"algebraic_type":{"Array":{"U8":[]}}},{"name":{"some":"Gzip"},"algebraic_type":{"Array":{"U8":[]}}}]}},{"Product":{"elements":[{"name":{"some":"deletes"},"algebraic_type":{"Ref":15}},{"name":{"some":"inserts"},"algebraic_type":{"Ref":15}}]}},{"Product":{"elements":[{"name":{"some":"size_hint"},"algebraic_type":{"Ref":16}},{"name":{"some":"rows_data"},"algebraic_type":{"Array":{"U8":[]}}}]}},{"Sum":{"variants":[{"name":{"some":"FixedSize"},"algebraic_type":{"U16":[]}},{"name":{"some":"RowOffsets"},"algebraic_type":{"Array":{"U64":[]}}}]}},{"Product":{"elements":[{"name":{"some":"status"},"algebraic_type":{"Ref":18}},{"name":{"some":"timestamp"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__timestamp_micros_since_unix_epoch__"},"algebraic_type":{"I64":[]}}]}}},{"name":{"some":"caller_identity"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__identity__"},"algebraic_type":{"U256":[]}}]}}},{"name":{"some":"caller_connection_id"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__connection_id__"},"algebraic_type":{"U128":[]}}]}}},{"name":{"some":"reducer_call"},"algebraic_type":{"Ref":19}},{"name":{"some":"energy_quanta_used"},"algebraic_type":{"Ref":20}},{"name":{"some":"total_host_execution_duration"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__time_duration_micros__"},"algebraic_type":{"I64":[]}}]}}}]}},{"Sum":{"variants":[{"name":{"some":"Committed"},"algebraic_type":{"Ref":11}},{"name":{"some":"Failed"},"algebraic_type":{"String":[]}},{"name":{"some":"OutOfEnergy"},"algebraic_type":{"Product":{"elements":[]}}}]}},{"Product":{"elements":[{"name":{"some":"reducer_name"},"algebraic_type":{"String":[]}},{"name":{"some":"reducer_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"args"},"algebraic_type":{"Array":{"U8":[]}}},{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}}]}},{"Product":{"elements":[{"name":{"some":"quanta"},"algebraic_type":{"U128":[]}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"update"},"algebraic_type":{"Ref":11}}]}},{"Product":{"elements":[{"name":{"some":"identity"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__identity__"},"algebraic_type":{"U256":[]}}]}}},{"name":{"some":"token"},"algebraic_type":{"String":[]}},{"name":{"some":"connection_id"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__connection_id__"},"algebraic_type":{"U128":[]}}]}}}]}},{"Product":{"elements":[{"name":{"some":"message_id"},"algebraic_type":{"Array":{"U8":[]}}},{"name":{"some":"error"},"algebraic_type":{"Sum":{"variants":[{"name":{"some":"some"},"algebraic_type":{"String":[]}},{"name":{"some":"none"},"algebraic_type":{"Product":{"elements":[]}}}]}}},{"name":{"some":"tables"},"algebraic_type":{"Array":{"Ref":24}}},{"name":{"some":"total_host_execution_duration"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__time_duration_micros__"},"algebraic_type":{"I64":[]}}]}}}]}},{"Product":{"elements":[{"name":{"some":"table_name"},"algebraic_type":{"String":[]}},{"name":{"some":"rows"},"algebraic_type":{"Ref":15}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"total_host_execution_duration_micros"},"algebraic_type":{"U64":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}},{"name":{"some":"rows"},"algebraic_type":{"Ref":26}}]}},{"Product":{"elements":[{"name":{"some":"table_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"table_name"},"algebraic_type":{"String":[]}},{"name":{"some":"table_rows"},"algebraic_type":{"Ref":12}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"total_host_execution_duration_micros"},"algebraic_type":{"U64":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}},{"name":{"some":"rows"},"algebraic_type":{"Ref":26}}]}},{"Product":{"elements":[{"name":{"some":"total_host_execution_duration_micros"},"algebraic_type":{"U64":[]}},{"name":{"some":"request_id"},"algebraic_type":{"Sum":{"variants":[{"name":{"some":"some"},"algebraic_type":{"U32":[]}},{"name":{"some":"none"},"algebraic_type":{"Product":{"elements":[]}}}]}}},{"name":{"some":"query_id"},"algebraic_type":{"Sum":{"variants":[{"name":{"some":"some"},"algebraic_type":{"U32":[]}},{"name":{"some":"none"},"algebraic_type":{"Product":{"elements":[]}}}]}}},{"name":{"some":"table_id"},"algebraic_type":{"Sum":{"variants":[{"name":{"some":"some"},"algebraic_type":{"U32":[]}},{"name":{"some":"none"},"algebraic_type":{"Product":{"elements":[]}}}]}}},{"name":{"some":"error"},"algebraic_type":{"String":[]}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"total_host_execution_duration_micros"},"algebraic_type":{"U64":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}},{"name":{"some":"update"},"algebraic_type":{"Ref":11}}]}},{"Product":{"elements":[{"name":{"some":"request_id"},"algebraic_type":{"U32":[]}},{"name":{"some":"total_host_execution_duration_micros"},"algebraic_type":{"U64":[]}},{"name":{"some":"query_id"},"algebraic_type":{"Ref":5}},{"name":{"some":"update"},"algebraic_type":{"Ref":11}}]}}]},"tables":[],"reducers":[],"misc_exports":[{"TypeAlias":{"name":"ClientMessage","ty":0}},{"TypeAlias":{"name":"CallReducer","ty":1}},{"TypeAlias":{"name":"Subscribe","ty":2}},{"TypeAlias":{"name":"OneOffQuery","ty":3}},{"TypeAlias":{"name":"SubscribeSingle","ty":4}},{"TypeAlias":{"name":"QueryId","ty":5}},{"TypeAlias":{"name":"SubscribeMulti","ty":6}},{"TypeAlias":{"name":"Unsubscribe","ty":7}},{"TypeAlias":{"name":"UnsubscribeMulti","ty":8}},{"TypeAlias":{"name":"ServerMessage","ty":9}},{"TypeAlias":{"name":"InitialSubscription","ty":10}},{"TypeAlias":{"name":"DatabaseUpdate","ty":11}},{"TypeAlias":{"name":"TableUpdate","ty":12}},{"TypeAlias":{"name":"CompressableQueryUpdate","ty":13}},{"TypeAlias":{"name":"QueryUpdate","ty":14}},{"TypeAlias":{"name":"BsatnRowList","ty":15}},{"TypeAlias":{"name":"RowSizeHint","ty":16}},{"TypeAlias":{"name":"TransactionUpdate","ty":17}},{"TypeAlias":{"name":"UpdateStatus","ty":18}},{"TypeAlias":{"name":"ReducerCallInfo","ty":19}},{"TypeAlias":{"name":"EnergyQuanta","ty":20}},{"TypeAlias":{"name":"TransactionUpdateLight","ty":21}},{"TypeAlias":{"name":"IdentityToken","ty":22}},{"TypeAlias":{"name":"OneOffQueryResponse","ty":23}},{"TypeAlias":{"name":"OneOffTable","ty":24}},{"TypeAlias":{"name":"SubscribeApplied","ty":25}},{"TypeAlias":{"name":"SubscribeRows","ty":26}},{"TypeAlias":{"name":"UnsubscribeApplied","ty":27}},{"TypeAlias":{"name":"SubscriptionError","ty":28}},{"TypeAlias":{"name":"SubscribeMultiApplied","ty":29}},{"TypeAlias":{"name":"UnsubscribeMultiApplied","ty":30}}]}}