@@ -4,12 +4,15 @@ use crate::db::relational_db::{MutTx, RelationalDB};
44use crate :: error:: { DBError , DatastoreError , IndexError , NodesError } ;
55use crate :: host:: wasm_common:: TimingSpan ;
66use crate :: replica_context:: ReplicaContext ;
7+ use crate :: util:: asyncify;
78use chrono:: { DateTime , Utc } ;
89use core:: mem;
910use parking_lot:: { Mutex , MutexGuard } ;
1011use smallvec:: SmallVec ;
12+ use spacetimedb_datastore:: execution_context:: Workload ;
1113use spacetimedb_datastore:: locking_tx_datastore:: state_view:: StateView ;
1214use spacetimedb_datastore:: locking_tx_datastore:: { FuncCallType , MutTxId } ;
15+ use spacetimedb_datastore:: traits:: IsolationLevel ;
1316use spacetimedb_lib:: { ConnectionId , Identity , Timestamp } ;
1417use spacetimedb_primitives:: { ColId , ColList , IndexId , TableId } ;
1518use spacetimedb_sats:: {
@@ -204,6 +207,14 @@ impl InstanceEnv {
204207 self . tx . get ( )
205208 }
206209
210+ pub ( crate ) fn take_tx ( & self ) -> Result < MutTxId , GetTxError > {
211+ self . tx . take ( )
212+ }
213+
214+ pub ( crate ) fn relational_db ( & self ) -> & Arc < RelationalDB > {
215+ & self . replica_ctx . relational_db
216+ }
217+
207218 pub ( crate ) fn get_jwt_payload ( & self , connection_id : ConnectionId ) -> Result < Option < String > , NodesError > {
208219 let tx = & mut * self . get_tx ( ) ?;
209220 Ok ( tx. get_jwt_payload ( connection_id) . map_err ( DBError :: from) ?)
@@ -274,7 +285,7 @@ impl InstanceEnv {
274285 }
275286
276287 pub fn insert ( & self , table_id : TableId , buffer : & mut [ u8 ] ) -> Result < usize , NodesError > {
277- let stdb = & * self . replica_ctx . relational_db ;
288+ let stdb = self . relational_db ( ) ;
278289 let tx = & mut * self . get_tx ( ) ?;
279290
280291 let ( row_len, row_ptr, insert_flags) = stdb
@@ -343,7 +354,7 @@ impl InstanceEnv {
343354 }
344355
345356 pub fn update ( & self , table_id : TableId , index_id : IndexId , buffer : & mut [ u8 ] ) -> Result < usize , NodesError > {
346- let stdb = & * self . replica_ctx . relational_db ;
357+ let stdb = self . relational_db ( ) ;
347358 let tx = & mut * self . get_tx ( ) ?;
348359
349360 let ( row_len, row_ptr, update_flags) = stdb
@@ -386,8 +397,8 @@ impl InstanceEnv {
386397 rstart : & [ u8 ] ,
387398 rend : & [ u8 ] ,
388399 ) -> Result < u32 , NodesError > {
389- let stdb = & * self . replica_ctx . relational_db ;
390- let tx = & mut * self . tx . get ( ) ?;
400+ let stdb = self . relational_db ( ) ;
401+ let tx = & mut * self . get_tx ( ) ?;
391402
392403 // Find all rows in the table to delete.
393404 let ( table_id, _, _, iter) = stdb. index_scan_range ( tx, index_id, prefix, prefix_elems, rstart, rend) ?;
@@ -416,7 +427,7 @@ impl InstanceEnv {
416427 /// - a row couldn't be decoded to the table schema type.
417428 #[ tracing:: instrument( level = "trace" , skip( self , relation) ) ]
418429 pub fn datastore_delete_all_by_eq_bsatn ( & self , table_id : TableId , relation : & [ u8 ] ) -> Result < u32 , NodesError > {
419- let stdb = & * self . replica_ctx . relational_db ;
430+ let stdb = self . relational_db ( ) ;
420431 let tx = & mut * self . get_tx ( ) ?;
421432
422433 // Track the number of bytes coming from the caller
@@ -443,7 +454,7 @@ impl InstanceEnv {
443454 /// and `TableNotFound` if the table does not exist.
444455 #[ tracing:: instrument( level = "trace" , skip_all) ]
445456 pub fn table_id_from_name ( & self , table_name : & str ) -> Result < TableId , NodesError > {
446- let stdb = & * self . replica_ctx . relational_db ;
457+ let stdb = self . relational_db ( ) ;
447458 let tx = & mut * self . get_tx ( ) ?;
448459
449460 // Query the table id from the name.
@@ -457,7 +468,7 @@ impl InstanceEnv {
457468 /// and `IndexNotFound` if the index does not exist.
458469 #[ tracing:: instrument( level = "trace" , skip_all) ]
459470 pub fn index_id_from_name ( & self , index_name : & str ) -> Result < IndexId , NodesError > {
460- let stdb = & * self . replica_ctx . relational_db ;
471+ let stdb = self . relational_db ( ) ;
461472 let tx = & mut * self . get_tx ( ) ?;
462473
463474 // Query the index id from the name.
@@ -471,7 +482,7 @@ impl InstanceEnv {
471482 /// and `TableNotFound` if the table does not exist.
472483 #[ tracing:: instrument( level = "trace" , skip_all) ]
473484 pub fn datastore_table_row_count ( & self , table_id : TableId ) -> Result < u64 , NodesError > {
474- let stdb = & * self . replica_ctx . relational_db ;
485+ let stdb = self . relational_db ( ) ;
475486 let tx = & mut * self . get_tx ( ) ?;
476487
477488 // Query the row count for id.
@@ -488,8 +499,8 @@ impl InstanceEnv {
488499 pool : & mut ChunkPool ,
489500 table_id : TableId ,
490501 ) -> Result < Vec < Vec < u8 > > , NodesError > {
491- let stdb = & * self . replica_ctx . relational_db ;
492- let tx = & mut * self . tx . get ( ) ?;
502+ let stdb = self . relational_db ( ) ;
503+ let tx = & mut * self . get_tx ( ) ?;
493504
494505 // Track the number of rows and the number of bytes scanned by the iterator
495506 let mut rows_scanned = 0 ;
@@ -521,8 +532,8 @@ impl InstanceEnv {
521532 rstart : & [ u8 ] ,
522533 rend : & [ u8 ] ,
523534 ) -> Result < Vec < Vec < u8 > > , NodesError > {
524- let stdb = & * self . replica_ctx . relational_db ;
525- let tx = & mut * self . tx . get ( ) ?;
535+ let stdb = self . relational_db ( ) ;
536+ let tx = & mut * self . get_tx ( ) ?;
526537
527538 // Track rows and bytes scanned by the iterator
528539 let mut rows_scanned = 0 ;
@@ -569,26 +580,52 @@ impl InstanceEnv {
569580
570581 written
571582 }
583+
584+ pub async fn start_mutable_tx ( & mut self ) -> Result < ( ) , NodesError > {
585+ if self . get_tx ( ) . is_ok ( ) {
586+ return Err ( NodesError :: WouldBlockTransaction ) ;
587+ }
588+
589+ let stdb = self . replica_ctx . relational_db . clone ( ) ;
590+ // TODO(procedure-tx): should we add a new workload, e.g., `AnonTx`?
591+ let tx = asyncify ( move || stdb. begin_mut_tx ( IsolationLevel :: Serializable , Workload :: Internal ) ) . await ;
592+ self . tx . set_raw ( tx) ;
593+
594+ Ok ( ( ) )
595+ }
572596}
573597
574598impl TxSlot {
575- pub fn set < T > ( & mut self , tx : MutTxId , f : impl FnOnce ( ) -> T ) -> ( MutTxId , T ) {
599+ /// Sets the slot to `tx`, ensuring that there was no tx before.
600+ pub fn set_raw ( & mut self , tx : MutTxId ) {
576601 let prev = self . inner . lock ( ) . replace ( tx) ;
577602 assert ! ( prev. is_none( ) , "reentrant TxSlot::set" ) ;
578- let remove_tx = || self . inner . lock ( ) . take ( ) ;
603+ }
604+
605+ /// Sets the slot to `tx` runs `work`, and returns back `tx`.
606+ pub fn set < T > ( & mut self , tx : MutTxId , work : impl FnOnce ( ) -> T ) -> ( MutTxId , T ) {
607+ self . set_raw ( tx) ;
608+
609+ let remove_tx = || self . take ( ) . expect ( "tx was removed during transaction" ) ;
579610
580611 let res = {
581612 scopeguard:: defer_on_unwind! { remove_tx( ) ; }
582- f ( )
613+ work ( )
583614 } ;
584615
585- let tx = remove_tx ( ) . expect ( "tx was removed during transaction" ) ;
616+ let tx = remove_tx ( ) ;
586617 ( tx, res)
587618 }
588619
620+ /// Returns the tx in the slot.
589621 pub fn get ( & self ) -> Result < impl DerefMut < Target = MutTxId > + ' _ , GetTxError > {
590622 MutexGuard :: try_map ( self . inner . lock ( ) , |map| map. as_mut ( ) ) . map_err ( |_| GetTxError )
591623 }
624+
625+ /// Steals th tx from the slot.
626+ pub fn take ( & self ) -> Result < MutTxId , GetTxError > {
627+ self . inner . lock ( ) . take ( ) . ok_or ( GetTxError )
628+ }
592629}
593630
594631#[ derive( Debug ) ]
0 commit comments