11use crate :: db:: persistence:: Durability ;
2- use futures:: { channel:: mpsc, StreamExt } ;
32use spacetimedb_commitlog:: payload:: {
43 txdata:: { Mutations , Ops } ,
54 Txdata ,
@@ -9,11 +8,15 @@ use spacetimedb_datastore::{execution_context::ReducerContext, traits::TxData};
98use spacetimedb_durability:: DurableOffset ;
109use spacetimedb_primitives:: TableId ;
1110use std:: sync:: Arc ;
12-
13- /// A request to persist a transaction.
14- pub struct DurabilityRequest {
15- reducer_context : Option < ReducerContext > ,
16- tx_data : Arc < TxData > ,
11+ use tokio:: sync:: mpsc:: { unbounded_channel, UnboundedReceiver , UnboundedSender } ;
12+
13+ /// A request to persist a transaction or to terminate the actor.
14+ pub enum DurabilityRequest {
15+ Work {
16+ reducer_context : Option < ReducerContext > ,
17+ tx_data : Arc < TxData > ,
18+ } ,
19+ Close ,
1720}
1821
1922/// Represents a handle to a background task that persists transactions
@@ -23,14 +26,28 @@ pub struct DurabilityRequest {
2326/// before sending over to the `Durability` layer.
2427#[ derive( Clone ) ]
2528pub struct DurabilityWorker {
26- request_tx : mpsc :: UnboundedSender < DurabilityRequest > ,
29+ request_tx : UnboundedSender < DurabilityRequest > ,
2730 durability : Arc < Durability > ,
2831}
2932
33+ impl Drop for DurabilityWorker {
34+ fn drop ( & mut self ) {
35+ // Try to close the actor.
36+ // If the actor paniced, or a clone of `self` was `Drop`ped,
37+ // this can return `Err(_)`,
38+ // in which case we need only drop `self.durability`.
39+ if self . request_tx . send ( DurabilityRequest :: Close ) . is_ok ( ) {
40+ // Wait until the actor's `Arc<Durability>` has been dropped.
41+ // After that, we drop `self.durability` as normal.
42+ futures:: executor:: block_on ( self . request_tx . closed ( ) ) ;
43+ }
44+ }
45+ }
46+
3047impl DurabilityWorker {
3148 /// Create a new [`DurabilityWorker`] using the given `durability` policy.
3249 pub fn new ( durability : Arc < Durability > ) -> Self {
33- let ( request_tx, request_rx) = mpsc :: unbounded ( ) ;
50+ let ( request_tx, request_rx) = unbounded_channel ( ) ;
3451
3552 let actor = DurabilityWorkerActor {
3653 request_rx,
@@ -54,14 +71,15 @@ impl DurabilityWorker {
5471 /// and sends the work to an actor that collects data and calls `durability.append_tx`.
5572 ///
5673 /// Panics if the durability worker has closed the receive end of its queue(s),
57- /// which is likely due to it having panicked.
74+ /// which is likely due to it having panicked
75+ /// or because `DurabilityWorker` and thus `RelationalDB` was cloned.
5876 pub fn request_durability ( & self , reducer_context : Option < ReducerContext > , tx_data : & Arc < TxData > ) {
5977 self . request_tx
60- . unbounded_send ( DurabilityRequest {
78+ . send ( DurabilityRequest :: Work {
6179 reducer_context,
6280 tx_data : tx_data. clone ( ) ,
6381 } )
64- . expect ( "durability worker panicked" ) ;
82+ . expect ( "durability actor hung up / panicked" ) ;
6583 }
6684
6785 /// Get the [`DurableOffset`] of this database.
@@ -70,24 +88,39 @@ impl DurabilityWorker {
7088 }
7189}
7290
73- pub struct DurabilityWorkerActor {
74- request_rx : mpsc :: UnboundedReceiver < DurabilityRequest > ,
91+ struct DurabilityWorkerActor {
92+ request_rx : UnboundedReceiver < DurabilityRequest > ,
7593 durability : Arc < Durability > ,
7694}
7795
7896impl DurabilityWorkerActor {
7997 /// Processes requests to do durability.
8098 async fn run ( mut self ) {
81- while let Some ( DurabilityRequest {
82- reducer_context,
83- tx_data,
84- } ) = self . request_rx . next ( ) . await
85- {
86- Self :: do_durability ( & * self . durability , reducer_context, & tx_data) ;
99+ while let Some ( req) = self . request_rx . recv ( ) . await {
100+ match req {
101+ DurabilityRequest :: Work {
102+ reducer_context,
103+ tx_data,
104+ } => Self :: do_durability ( & * self . durability , reducer_context, & tx_data) ,
105+
106+ // Terminate the actor
107+ // and make sure we drop `self.durability`
108+ // before we drop `self.request_tx`.
109+ //
110+ // After a `Close`,
111+ // there should be no more `Work` incoming or buffered,
112+ // as `Close` hangs up the receiver end of the channel,
113+ // so nothing can be sent to it.
114+ DurabilityRequest :: Close => {
115+ drop ( self . durability ) ;
116+ drop ( self . request_rx ) ;
117+ return ;
118+ }
119+ }
87120 }
88121 }
89122
90- pub fn do_durability ( durability : & Durability , reducer_context : Option < ReducerContext > , tx_data : & TxData ) {
123+ fn do_durability ( durability : & Durability , reducer_context : Option < ReducerContext > , tx_data : & TxData ) {
91124 if tx_data. tx_offset ( ) . is_none ( ) {
92125 let name = reducer_context. as_ref ( ) . map ( |rcx| & * rcx. name ) ;
93126 debug_assert ! (
0 commit comments