@@ -15,7 +15,7 @@ use tokio::{
1515 runtime,
1616 sync:: {
1717 futures:: OwnedNotified ,
18- mpsc:: { channel , unbounded_channel , Receiver , Sender , UnboundedReceiver , UnboundedSender } ,
18+ mpsc:: { self , channel , Receiver , Sender } ,
1919 oneshot, Notify ,
2020 } ,
2121 time:: timeout,
@@ -69,7 +69,7 @@ type ShutdownReply = oneshot::Sender<OwnedNotified>;
6969/// [RelationalDB]: crate::db::relational_db::RelationalDB
7070pub struct DurabilityWorker {
7171 database : Identity ,
72- request_tx : UnboundedSender < DurabilityRequest > ,
72+ request_tx : Sender < DurabilityRequest > ,
7373 shutdown : Sender < ShutdownReply > ,
7474 durability : Arc < Durability > ,
7575 runtime : runtime:: Handle ,
@@ -86,7 +86,7 @@ impl DurabilityWorker {
8686 next_tx_offset : TxOffset ,
8787 reorder_window_size : NonZeroUsize ,
8888 ) -> Self {
89- let ( request_tx, request_rx) = unbounded_channel ( ) ;
89+ let ( request_tx, request_rx) = channel ( 4 * 4096 ) ;
9090 let ( shutdown_tx, shutdown_rx) = channel ( 1 ) ;
9191
9292 let actor = DurabilityWorkerActor {
@@ -123,8 +123,8 @@ impl DurabilityWorker {
123123 /// this method is responsible only for reading its decision out of the `tx_data`
124124 /// and calling `durability.append_tx`.
125125 ///
126- /// This method does not block,
127- /// and sends the work to an actor that collects data and calls `durability.append_tx` .
126+ /// This method sends the work to an actor that collects data and calls `durability.append_tx`.
127+ /// It blocks if the queue is at capacity .
128128 ///
129129 /// # Panics
130130 ///
@@ -135,12 +135,40 @@ impl DurabilityWorker {
135135 /// - [Self::shutdown] was called
136136 ///
137137 pub fn request_durability ( & self , reducer_context : Option < ReducerContext > , tx_data : & Arc < TxData > ) {
138- self . request_tx
139- . send ( DurabilityRequest {
140- reducer_context,
141- tx_data : tx_data. clone ( ) ,
142- } )
143- . unwrap_or_else ( |_| panic ! ( "durability actor vanished database={}" , self . database) ) ;
138+ // We first try to send it without blocking.
139+ match self . request_tx . try_reserve ( ) {
140+ Ok ( permit) => {
141+ permit. send ( DurabilityRequest {
142+ reducer_context,
143+ tx_data : tx_data. clone ( ) ,
144+ } ) ;
145+ }
146+ Err ( mpsc:: error:: TrySendError :: Closed ( _) ) => {
147+ panic ! ( "durability actor vanished database={}" , self . database) ;
148+ }
149+ Err ( mpsc:: error:: TrySendError :: Full ( _) ) => {
150+ // If the channel was full, we use the blocking version.
151+ let start = std:: time:: Instant :: now ( ) ;
152+ let send = || {
153+ self . request_tx . blocking_send ( DurabilityRequest {
154+ reducer_context,
155+ tx_data : tx_data. clone ( ) ,
156+ } )
157+ } ;
158+ if tokio:: runtime:: Handle :: try_current ( ) . is_ok ( ) {
159+ tokio:: task:: block_in_place ( send)
160+ } else {
161+ send ( )
162+ }
163+ . unwrap_or_else ( |_| panic ! ( "durability actor vanished database={}" , self . database) ) ;
164+ // We could cache this metric, but if we are already in the blocking code path,
165+ // the extra time of looking up the metric is probably negligible.
166+ WORKER_METRICS
167+ . durability_blocking_send_duration
168+ . with_label_values ( & self . database )
169+ . observe ( start. elapsed ( ) . as_secs_f64 ( ) ) ;
170+ }
171+ }
144172 }
145173
146174 /// Get the [`DurableOffset`] of this database.
@@ -281,8 +309,8 @@ impl<T> ReorderWindow<T> {
281309 }
282310}
283311
284- struct DurabilityWorkerActor {
285- request_rx : UnboundedReceiver < DurabilityRequest > ,
312+ pub struct DurabilityWorkerActor {
313+ request_rx : mpsc :: Receiver < DurabilityRequest > ,
286314 shutdown : Receiver < ShutdownReply > ,
287315 durability : Arc < Durability > ,
288316 reorder_window : ReorderWindow < DurabilityRequest > ,
@@ -483,7 +511,7 @@ mod tests {
483511 }
484512 }
485513
486- #[ tokio:: test]
514+ #[ tokio:: test( flavor = "multi_thread" ) ]
487515 async fn shutdown_waits_until_durable ( ) {
488516 let durability = Arc :: new ( CountingDurability :: default ( ) ) ;
489517 let worker = DurabilityWorker :: new (
0 commit comments