@@ -23,8 +23,8 @@ use crate::worker_metrics::WORKER_METRICS;
23
23
use parking_lot:: RwLock ;
24
24
use prometheus:: IntGauge ;
25
25
use spacetimedb_client_api_messages:: websocket:: {
26
- self as ws, BsatnFormat , FormatSwitch , JsonFormat , SubscribeMulti , SubscribeSingle , TableUpdate , Unsubscribe ,
27
- UnsubscribeMulti ,
26
+ self as ws, BsatnFormat , Compression , FormatSwitch , JsonFormat , SubscribeMulti , SubscribeSingle , TableUpdate ,
27
+ Unsubscribe , UnsubscribeMulti ,
28
28
} ;
29
29
use spacetimedb_execution:: pipelined:: PipelinedProject ;
30
30
use spacetimedb_expr:: check:: parse_and_type_sub;
@@ -170,7 +170,6 @@ impl ModuleSubscriptions {
170
170
auth,
171
171
) ?;
172
172
173
- let comp = sender. config . compression ;
174
173
let table_id = query. subscribed_table_id ( ) ;
175
174
let table_name = query. subscribed_table_name ( ) ;
176
175
@@ -187,10 +186,34 @@ impl ModuleSubscriptions {
187
186
let tx = DeltaTx :: from ( tx) ;
188
187
189
188
Ok ( match sender. config . protocol {
190
- Protocol :: Binary => collect_table_update ( & plans, table_id, table_name. into ( ) , comp, & tx, update_type)
191
- . map ( |( table_update, metrics) | ( FormatSwitch :: Bsatn ( table_update) , metrics) ) ,
192
- Protocol :: Text => collect_table_update ( & plans, table_id, table_name. into ( ) , comp, & tx, update_type)
193
- . map ( |( table_update, metrics) | ( FormatSwitch :: Json ( table_update) , metrics) ) ,
189
+ Protocol :: Binary => {
190
+ collect_table_update (
191
+ & plans,
192
+ table_id,
193
+ table_name. into ( ) ,
194
+ // We will compress the outer server message,
195
+ // after we release the tx lock.
196
+ // There's no need to compress the inner table update too.
197
+ Compression :: None ,
198
+ & tx,
199
+ update_type,
200
+ )
201
+ . map ( |( table_update, metrics) | ( FormatSwitch :: Bsatn ( table_update) , metrics) )
202
+ }
203
+ Protocol :: Text => {
204
+ collect_table_update (
205
+ & plans,
206
+ table_id,
207
+ table_name. into ( ) ,
208
+ // We will compress the outer server message,
209
+ // after we release the tx lock,
210
+ // There's no need to compress the inner table update too.
211
+ Compression :: None ,
212
+ & tx,
213
+ update_type,
214
+ )
215
+ . map ( |( table_update, metrics) | ( FormatSwitch :: Json ( table_update) , metrics) )
216
+ }
194
217
} ?)
195
218
}
196
219
@@ -213,16 +236,31 @@ impl ModuleSubscriptions {
213
236
} ,
214
237
auth,
215
238
) ?;
216
- let comp = sender. config . compression ;
217
239
218
240
let tx = DeltaTx :: from ( tx) ;
219
241
match sender. config . protocol {
220
242
Protocol :: Binary => {
221
- let ( update, metrics) = execute_plans ( queries, comp, & tx, update_type) ?;
243
+ let ( update, metrics) = execute_plans (
244
+ queries,
245
+ // We will compress the outer server message,
246
+ // after we release the tx lock.
247
+ // There's no need to compress the inner table updates too.
248
+ Compression :: None ,
249
+ & tx,
250
+ update_type,
251
+ ) ?;
222
252
Ok ( ( FormatSwitch :: Bsatn ( update) , metrics) )
223
253
}
224
254
Protocol :: Text => {
225
- let ( update, metrics) = execute_plans ( queries, comp, & tx, update_type) ?;
255
+ let ( update, metrics) = execute_plans (
256
+ queries,
257
+ // We will compress the outer server message,
258
+ // after we release the tx lock.
259
+ // There's no need to compress the inner table updates too.
260
+ Compression :: None ,
261
+ & tx,
262
+ update_type,
263
+ ) ?;
226
264
Ok ( ( FormatSwitch :: Json ( update) , metrics) )
227
265
}
228
266
}
@@ -598,8 +636,6 @@ impl ModuleSubscriptions {
598
636
599
637
drop ( guard) ;
600
638
601
- let comp = sender. config . compression ;
602
-
603
639
check_row_limit (
604
640
& queries,
605
641
& self . relational_db ,
@@ -614,10 +650,26 @@ impl ModuleSubscriptions {
614
650
615
651
let tx = DeltaTx :: from ( & * tx) ;
616
652
let ( database_update, metrics) = match sender. config . protocol {
617
- Protocol :: Binary => execute_plans ( & queries, comp, & tx, TableUpdateType :: Subscribe )
618
- . map ( |( table_update, metrics) | ( FormatSwitch :: Bsatn ( table_update) , metrics) ) ?,
619
- Protocol :: Text => execute_plans ( & queries, comp, & tx, TableUpdateType :: Subscribe )
620
- . map ( |( table_update, metrics) | ( FormatSwitch :: Json ( table_update) , metrics) ) ?,
653
+ Protocol :: Binary => execute_plans (
654
+ & queries,
655
+ // We will compress the outer server message,
656
+ // after we release the tx lock.
657
+ // There's no need to compress the inner table updates too.
658
+ Compression :: None ,
659
+ & tx,
660
+ TableUpdateType :: Subscribe ,
661
+ )
662
+ . map ( |( table_update, metrics) | ( FormatSwitch :: Bsatn ( table_update) , metrics) ) ?,
663
+ Protocol :: Text => execute_plans (
664
+ & queries,
665
+ // We will compress the outer server message,
666
+ // after we release the tx lock.
667
+ // There's no need to compress the inner table updates too.
668
+ Compression :: None ,
669
+ & tx,
670
+ TableUpdateType :: Subscribe ,
671
+ )
672
+ . map ( |( table_update, metrics) | ( FormatSwitch :: Json ( table_update) , metrics) ) ?,
621
673
} ;
622
674
623
675
record_exec_metrics (
@@ -719,8 +771,8 @@ pub struct WriteConflict;
719
771
mod tests {
720
772
use super :: { AssertTxFn , ModuleSubscriptions } ;
721
773
use crate :: client:: messages:: {
722
- SerializableMessage , SubscriptionError , SubscriptionMessage , SubscriptionResult , SubscriptionUpdateMessage ,
723
- TransactionUpdateMessage ,
774
+ SerializableMessage , SubscriptionData , SubscriptionError , SubscriptionMessage , SubscriptionResult ,
775
+ SubscriptionUpdateMessage , TransactionUpdateMessage ,
724
776
} ;
725
777
use crate :: client:: { ClientActorId , ClientConfig , ClientConnectionSender , ClientName , Protocol } ;
726
778
use crate :: db:: datastore:: system_tables:: { StRowLevelSecurityRow , ST_ROW_LEVEL_SECURITY_ID } ;
@@ -740,7 +792,7 @@ mod tests {
740
792
use spacetimedb_client_api_messages:: energy:: EnergyQuanta ;
741
793
use spacetimedb_client_api_messages:: websocket:: {
742
794
CompressableQueryUpdate , Compression , FormatSwitch , QueryId , Subscribe , SubscribeMulti , SubscribeSingle ,
743
- Unsubscribe , UnsubscribeMulti ,
795
+ TableUpdate , Unsubscribe , UnsubscribeMulti ,
744
796
} ;
745
797
use spacetimedb_execution:: dml:: MutDatastore ;
746
798
use spacetimedb_lib:: bsatn:: ToBsatn ;
@@ -856,19 +908,27 @@ mod tests {
856
908
}
857
909
}
858
910
859
- /// Instantiate a client connection
860
- fn client_connection ( client_id : ClientActorId ) -> ( Arc < ClientConnectionSender > , Receiver < SerializableMessage > ) {
911
+ /// Instantiate a client connection with compression
912
+ fn client_connection_with_compression (
913
+ client_id : ClientActorId ,
914
+ compression : Compression ,
915
+ ) -> ( Arc < ClientConnectionSender > , Receiver < SerializableMessage > ) {
861
916
let ( sender, rx) = ClientConnectionSender :: dummy_with_channel (
862
917
client_id,
863
918
ClientConfig {
864
919
protocol : Protocol :: Binary ,
865
- compression : Compression :: None ,
920
+ compression,
866
921
tx_update_full : true ,
867
922
} ,
868
923
) ;
869
924
( Arc :: new ( sender) , rx)
870
925
}
871
926
927
+ /// Instantiate a client connection
928
+ fn client_connection ( client_id : ClientActorId ) -> ( Arc < ClientConnectionSender > , Receiver < SerializableMessage > ) {
929
+ client_connection_with_compression ( client_id, Compression :: None )
930
+ }
931
+
872
932
/// Insert rules into the RLS system table
873
933
fn insert_rls_rules (
874
934
db : & RelationalDB ,
@@ -1472,6 +1532,50 @@ mod tests {
1472
1532
Ok ( ( ) )
1473
1533
}
1474
1534
1535
+ /// Test that we do not compress the results of an initial subscribe call
1536
+ #[ tokio:: test]
1537
+ async fn test_no_compression_for_subscribe ( ) -> anyhow:: Result < ( ) > {
1538
+ // Establish a client connection with compression
1539
+ let ( tx, mut rx) = client_connection_with_compression ( client_id_from_u8 ( 1 ) , Compression :: Brotli ) ;
1540
+
1541
+ let db = relational_db ( ) ?;
1542
+ let subs = module_subscriptions ( db. clone ( ) ) ;
1543
+
1544
+ let table_id = db. create_table_for_test ( "t" , & [ ( "x" , AlgebraicType :: U64 ) ] , & [ ] ) ?;
1545
+
1546
+ let mut inserts = vec ! [ ] ;
1547
+
1548
+ for i in 0 ..16_000u64 {
1549
+ inserts. push ( ( table_id, product ! [ i] ) ) ;
1550
+ }
1551
+
1552
+ // Insert a lot of rows into `t`.
1553
+ // We want to insert enough to cross any threshold there might be for compression.
1554
+ commit_tx ( & db, & subs, [ ] , inserts) ?;
1555
+
1556
+ // Subscribe to the entire table
1557
+ subscribe_multi ( & subs, & [ "select * from t" ] , tx, & mut 0 ) ?;
1558
+
1559
+ // Assert the table updates within this message are all be uncompressed
1560
+ match rx. recv ( ) . await {
1561
+ Some ( SerializableMessage :: Subscription ( SubscriptionMessage {
1562
+ result :
1563
+ SubscriptionResult :: SubscribeMulti ( SubscriptionData {
1564
+ data : FormatSwitch :: Bsatn ( ws:: DatabaseUpdate { tables } ) ,
1565
+ } ) ,
1566
+ ..
1567
+ } ) ) => {
1568
+ assert ! ( tables. iter( ) . all( |TableUpdate { updates, .. } | updates
1569
+ . iter( )
1570
+ . all( |query_update| matches!( query_update, CompressableQueryUpdate :: Uncompressed ( _) ) ) ) ) ;
1571
+ }
1572
+ Some ( _) => panic ! ( "unexpected message from subscription" ) ,
1573
+ None => panic ! ( "channel unexpectedly closed" ) ,
1574
+ } ;
1575
+
1576
+ Ok ( ( ) )
1577
+ }
1578
+
1475
1579
/// In this test we subscribe to a join query, update the lhs table,
1476
1580
/// and assert that the server sends the correct delta to the client.
1477
1581
#[ tokio:: test]
0 commit comments