@@ -38,6 +38,7 @@ use spacetimedb::worker_metrics::WORKER_METRICS;
3838use spacetimedb:: Identity ;
3939use spacetimedb_client_api_messages:: websocket:: v1 as ws_v1;
4040use spacetimedb_client_api_messages:: websocket:: v2 as ws_v2;
41+ use spacetimedb_client_api_messages:: websocket:: v3 as ws_v3;
4142use spacetimedb_datastore:: execution_context:: WorkloadType ;
4243use spacetimedb_lib:: connection_id:: { ConnectionId , ConnectionIdForUrl } ;
4344use tokio:: sync:: { mpsc, watch} ;
@@ -62,6 +63,8 @@ pub const TEXT_PROTOCOL: HeaderValue = HeaderValue::from_static(ws_v1::TEXT_PROT
6263pub const BIN_PROTOCOL : HeaderValue = HeaderValue :: from_static ( ws_v1:: BIN_PROTOCOL ) ;
6364#[ allow( clippy:: declare_interior_mutable_const) ]
6465pub const V2_BIN_PROTOCOL : HeaderValue = HeaderValue :: from_static ( ws_v2:: BIN_PROTOCOL ) ;
66+ #[ allow( clippy:: declare_interior_mutable_const) ]
67+ pub const V3_BIN_PROTOCOL : HeaderValue = HeaderValue :: from_static ( ws_v3:: BIN_PROTOCOL ) ;
6568
6669pub trait HasWebSocketOptions {
6770 fn websocket_options ( & self ) -> WebSocketOptions ;
@@ -101,7 +104,7 @@ fn resolve_confirmed_reads_default(version: WsVersion, confirmed: Option<bool>)
101104 }
102105 match version {
103106 WsVersion :: V1 => false ,
104- WsVersion :: V2 => crate :: DEFAULT_CONFIRMED_READS ,
107+ WsVersion :: V2 | WsVersion :: V3 => crate :: DEFAULT_CONFIRMED_READS ,
105108 }
106109}
107110
@@ -151,6 +154,13 @@ where
151154 }
152155
153156 let ( res, ws_upgrade, protocol) = ws. select_protocol ( [
157+ (
158+ V3_BIN_PROTOCOL ,
159+ NegotiatedProtocol {
160+ protocol : Protocol :: Binary ,
161+ version : WsVersion :: V3 ,
162+ } ,
163+ ) ,
154164 (
155165 V2_BIN_PROTOCOL ,
156166 NegotiatedProtocol {
@@ -284,7 +294,7 @@ where
284294 } ;
285295 client. send_message ( None , OutboundMessage :: V1 ( message. into ( ) ) )
286296 }
287- WsVersion :: V2 => {
297+ WsVersion :: V2 | WsVersion :: V3 => {
288298 let message = ws_v2:: ServerMessage :: InitialConnection ( ws_v2:: InitialConnection {
289299 identity : client_identity,
290300 connection_id,
@@ -1296,7 +1306,7 @@ async fn ws_encode_task(
12961306 let buf_pool = ArrayQueue :: new ( BUF_POOL_CAPACITY ) ;
12971307 let mut in_use_bufs: Vec < ScopeGuard < InUseSerializeBuffer , _ > > = Vec :: with_capacity ( BUF_POOL_CAPACITY ) ;
12981308
1299- while let Some ( message) = messages. recv ( ) . await {
1309+ ' send : while let Some ( message) = messages. recv ( ) . await {
13001310 // Drop serialize buffers with no external referent,
13011311 // returning them to the pool.
13021312 in_use_bufs. retain ( |in_use| !in_use. is_unique ( ) ) ;
@@ -1306,55 +1316,62 @@ async fn ws_encode_task(
13061316
13071317 let in_use_buf = match message {
13081318 OutboundWsMessage :: Error ( message) => {
1309- if config. version == WsVersion :: V2 {
1310- log:: error!( "dropping v1 error message sent to a v2 client: {:?}" , message) ;
1319+ if config. version != WsVersion :: V1 {
1320+ log:: error!(
1321+ "dropping v1 error message sent to a binary websocket client: {:?}" ,
1322+ message
1323+ ) ;
13111324 continue ;
13121325 }
1313- let ( stats, in_use, mut frames) = ws_encode_message ( config, buf, message, false , & bsatn_rlb_pool) . await ;
1314- metrics. report ( None , None , stats) ;
1315- if frames. try_for_each ( |frame| outgoing_frames. send ( frame) ) . is_err ( ) {
1316- break ;
1317- }
1318-
1326+ let Ok ( in_use) = ws_forward_frames (
1327+ & metrics,
1328+ & outgoing_frames,
1329+ None ,
1330+ None ,
1331+ ws_encode_message ( config, buf, message, false , & bsatn_rlb_pool) . await ,
1332+ ) else {
1333+ break ' send;
1334+ } ;
13191335 in_use
13201336 }
13211337 OutboundWsMessage :: Message ( message) => {
13221338 let workload = message. workload ( ) ;
13231339 let num_rows = message. num_rows ( ) ;
13241340 match message {
13251341 OutboundMessage :: V2 ( server_message) => {
1326- if config. version != WsVersion :: V2 {
1342+ if config. version == WsVersion :: V1 {
13271343 log:: error!( "dropping v2 message on v1 connection" ) ;
13281344 continue ;
13291345 }
13301346
1331- let ( stats, in_use, mut frames) =
1332- ws_encode_message_v2 ( config, buf, server_message, false , & bsatn_rlb_pool) . await ;
1333- metrics. report ( workload, num_rows, stats) ;
1334- if frames. try_for_each ( |frame| outgoing_frames. send ( frame) ) . is_err ( ) {
1335- break ;
1336- }
1337-
1347+ let Ok ( in_use) = ws_forward_frames (
1348+ & metrics,
1349+ & outgoing_frames,
1350+ workload,
1351+ num_rows,
1352+ ws_encode_binary_message ( config, buf, server_message, false , & bsatn_rlb_pool) . await ,
1353+ ) else {
1354+ break ' send;
1355+ } ;
13381356 in_use
13391357 }
13401358 OutboundMessage :: V1 ( message) => {
1341- if config. version == WsVersion :: V2 {
1342- log:: error!(
1343- "dropping v1 message for v2 connection until v2 serialization is implemented: {:?}" ,
1344- message
1345- ) ;
1359+ if config. version != WsVersion :: V1 {
1360+ log:: error!( "dropping v1 message for a binary websocket connection: {:?}" , message) ;
13461361 continue ;
13471362 }
13481363
13491364 let is_large = num_rows. is_some_and ( |n| n > 1024 ) ;
13501365
1351- let ( stats, in_use, mut frames) =
1352- ws_encode_message ( config, buf, message, is_large, & bsatn_rlb_pool) . await ;
1353- metrics. report ( workload, num_rows, stats) ;
1354- if frames. try_for_each ( |frame| outgoing_frames. send ( frame) ) . is_err ( ) {
1355- break ;
1356- }
1357-
1366+ let Ok ( in_use) = ws_forward_frames (
1367+ & metrics,
1368+ & outgoing_frames,
1369+ workload,
1370+ num_rows,
1371+ ws_encode_message ( config, buf, message, is_large, & bsatn_rlb_pool) . await ,
1372+ ) else {
1373+ break ' send;
1374+ } ;
13581375 in_use
13591376 }
13601377 }
@@ -1370,6 +1387,21 @@ async fn ws_encode_task(
13701387 }
13711388}
13721389
1390+ /// Reports encode metrics for an already-encoded message and forwards its
1391+ /// frames to the websocket send task.
1392+ fn ws_forward_frames (
1393+ metrics : & SendMetrics ,
1394+ outgoing_frames : & mpsc:: UnboundedSender < Frame > ,
1395+ workload : Option < WorkloadType > ,
1396+ num_rows : Option < usize > ,
1397+ encoded : ( EncodeMetrics , InUseSerializeBuffer , impl IntoIterator < Item = Frame > ) ,
1398+ ) -> Result < InUseSerializeBuffer , mpsc:: error:: SendError < Frame > > {
1399+ let ( stats, in_use, frames) = encoded;
1400+ metrics. report ( workload, num_rows, stats) ;
1401+ frames. into_iter ( ) . try_for_each ( |frame| outgoing_frames. send ( frame) ) ?;
1402+ Ok ( in_use)
1403+ }
1404+
13731405/// Some stats about serialization and compression.
13741406///
13751407/// Returned by [`ws_encode_message`].
@@ -1443,21 +1475,21 @@ async fn ws_encode_message(
14431475 ( metrics, msg_alloc, frames)
14441476}
14451477
1446- #[ allow( dead_code, unused_variables) ]
1447- async fn ws_encode_message_v2 (
1478+ async fn ws_encode_binary_message (
14481479 config : ClientConfig ,
14491480 buf : SerializeBuffer ,
14501481 message : ws_v2:: ServerMessage ,
14511482 is_large_message : bool ,
14521483 bsatn_rlb_pool : & BsatnRowListBuilderPool ,
14531484) -> ( EncodeMetrics , InUseSerializeBuffer , impl Iterator < Item = Frame > + use < > ) {
14541485 let start = Instant :: now ( ) ;
1486+ let compression = config. compression ;
14551487
14561488 let ( in_use, data) = if is_large_message {
14571489 let bsatn_rlb_pool = bsatn_rlb_pool. clone ( ) ;
1458- spawn_rayon ( move || serialize_v2 ( & bsatn_rlb_pool, buf, message, config . compression ) ) . await
1490+ spawn_rayon ( move || serialize_v2 ( & bsatn_rlb_pool, buf, message, compression) ) . await
14591491 } else {
1460- serialize_v2 ( bsatn_rlb_pool, buf, message, config . compression )
1492+ serialize_v2 ( bsatn_rlb_pool, buf, message, compression)
14611493 } ;
14621494
14631495 let metrics = EncodeMetrics {
@@ -2298,9 +2330,11 @@ mod tests {
22982330
22992331 #[ test]
23002332 fn confirmed_reads_default_depends_on_ws_version ( ) {
2333+ assert ! ( resolve_confirmed_reads_default( WsVersion :: V3 , None ) ) ;
23012334 assert ! ( resolve_confirmed_reads_default( WsVersion :: V2 , None ) ) ;
23022335 assert ! ( !resolve_confirmed_reads_default( WsVersion :: V1 , None ) ) ;
23032336 assert ! ( resolve_confirmed_reads_default( WsVersion :: V1 , Some ( true ) ) ) ;
2337+ assert ! ( !resolve_confirmed_reads_default( WsVersion :: V3 , Some ( false ) ) ) ;
23042338 assert ! ( !resolve_confirmed_reads_default( WsVersion :: V2 , Some ( false ) ) ) ;
23052339 }
23062340
0 commit comments