@@ -24,7 +24,7 @@ use crate::{
24
24
use anyhow:: anyhow;
25
25
use core:: { convert:: Infallible , ops:: RangeBounds } ;
26
26
use itertools:: Itertools ;
27
- use spacetimedb_data_structures:: map:: { HashSet , IntMap } ;
27
+ use spacetimedb_data_structures:: map:: { HashSet , IntMap , IntSet } ;
28
28
use spacetimedb_lib:: {
29
29
db:: auth:: { StAccess , StTableType } ,
30
30
Identity ,
@@ -66,6 +66,11 @@ pub struct CommittedState {
66
66
/// Pages are shared between all modules running on a particular host,
67
67
/// not allocated per-module.
68
68
pub ( super ) page_pool : PagePool ,
69
+ /// Whether the table was dropped during replay.
70
+ /// TODO(centril): Only used during bootstrap and is otherwise unused.
71
+ /// We should split `CommittedState` into two types
72
+ /// where one, e.g., `ReplayCommittedState`, has this field.
73
+ table_dropped : IntSet < TableId > ,
69
74
}
70
75
71
76
impl MemoryUsage for CommittedState {
@@ -76,9 +81,14 @@ impl MemoryUsage for CommittedState {
76
81
blob_store,
77
82
index_id_map,
78
83
page_pool : _,
84
+ table_dropped,
79
85
} = self ;
80
86
// NOTE(centril): We do not want to include the heap usage of `page_pool` as it's a shared resource.
81
- next_tx_offset. heap_usage ( ) + tables. heap_usage ( ) + blob_store. heap_usage ( ) + index_id_map. heap_usage ( )
87
+ next_tx_offset. heap_usage ( )
88
+ + tables. heap_usage ( )
89
+ + blob_store. heap_usage ( )
90
+ + index_id_map. heap_usage ( )
91
+ + table_dropped. heap_usage ( )
82
92
}
83
93
}
84
94
@@ -157,6 +167,7 @@ impl CommittedState {
157
167
tables : <_ >:: default ( ) ,
158
168
blob_store : <_ >:: default ( ) ,
159
169
index_id_map : <_ >:: default ( ) ,
170
+ table_dropped : <_ >:: default ( ) ,
160
171
page_pool,
161
172
}
162
173
}
@@ -334,17 +345,30 @@ impl CommittedState {
334
345
Ok ( ( ) )
335
346
}
336
347
337
- pub ( super ) fn replay_delete_by_rel ( & mut self , table_id : TableId , rel : & ProductValue ) -> Result < ( ) > {
338
- let table = self
339
- . tables
340
- . get_mut ( & table_id)
341
- . ok_or ( TableError :: IdNotFoundState ( table_id) ) ?;
348
+ pub ( super ) fn replay_delete_by_rel ( & mut self , table_id : TableId , row : & ProductValue ) -> Result < ( ) > {
349
+ // Get the table for mutation.
350
+ // If it was dropped, avoid an error and just ignore the row instead.
351
+ let table = match self . tables . get_mut ( & table_id) {
352
+ Some ( t) => t,
353
+ None if self . table_dropped . contains ( & table_id) => return Ok ( ( ) ) ,
354
+ None => return Err ( TableError :: IdNotFoundState ( table_id) . into ( ) ) ,
355
+ } ;
356
+
357
+ // Delete the row.
342
358
let blob_store = & mut self . blob_store ;
343
359
table
344
- . delete_equal_row ( & self . page_pool , blob_store, rel )
360
+ . delete_equal_row ( & self . page_pool , blob_store, row )
345
361
. map_err ( TableError :: Bflatn ) ?
346
362
. ok_or_else ( || anyhow ! ( "Delete for non-existent row when replaying transaction" ) ) ?;
347
363
364
+ if table_id == ST_TABLE_ID {
365
+ // A row was removed from `st_table`, so a table was dropped.
366
+ // Remove that table from the in-memory structures.
367
+ self . tables
368
+ . remove ( & Self :: read_table_id ( row) )
369
+ . expect ( "table to remove should exist" ) ;
370
+ }
371
+
348
372
Ok ( ( ) )
349
373
}
350
374
@@ -379,8 +403,7 @@ impl CommittedState {
379
403
///
380
404
/// The `row_ptr` is a pointer to `row`.
381
405
fn st_column_changed ( & mut self , row : & ProductValue , row_ptr : RowPointer ) -> Result < ( ) > {
382
- let target_table_id = TableId :: deserialize ( ValueDeserializer :: from_ref ( & row. elements [ 0 ] ) )
383
- . expect ( "first field in `st_column` should decode to a `TableId`" ) ;
406
+ let target_table_id = Self :: read_table_id ( row) ;
384
407
let target_col_id = ColId :: deserialize ( ValueDeserializer :: from_ref ( & row. elements [ 1 ] ) )
385
408
. expect ( "second field in `st_column` should decode to a `ColId`" ) ;
386
409
@@ -411,6 +434,12 @@ impl CommittedState {
411
434
Ok ( ( ) )
412
435
}
413
436
437
+ /// Assuming that a `TableId` is stored as the first field in `row`, read it.
438
+ fn read_table_id ( row : & ProductValue ) -> TableId {
439
+ TableId :: deserialize ( ValueDeserializer :: from_ref ( & row. elements [ 0 ] ) )
440
+ . expect ( "first field in `st_column` should decode to a `TableId`" )
441
+ }
442
+
414
443
pub ( super ) fn build_sequence_state ( & mut self , sequence_state : & mut SequencesState ) -> Result < ( ) > {
415
444
let st_sequences = self . tables . get ( & ST_SEQUENCE_ID ) . unwrap ( ) ;
416
445
for row_ref in st_sequences. scan_rows ( & self . blob_store ) {
0 commit comments