@@ -25,6 +25,7 @@ use prost::Message;
25
25
use proto:: build:: bazel:: remote:: execution:: v2:: digest_function;
26
26
use rand:: { rngs:: OsRng , Rng } ;
27
27
use tokio:: time:: sleep;
28
+ use tonic:: transport:: Channel ;
28
29
use tonic:: { transport, IntoRequest , Request , Response , Streaming } ;
29
30
use uuid:: Uuid ;
30
31
@@ -111,16 +112,20 @@ impl GrpcStore {
111
112
} )
112
113
}
113
114
115
+ fn get_retry_config ( & self ) -> impl Iterator < Item = Duration > + ' _ {
116
+ ExponentialBackoff :: new ( Duration :: from_millis ( self . retry . delay as u64 ) )
117
+ . map ( |d| ( self . jitter_fn ) ( d) )
118
+ . take ( self . retry . max_retries ) // Remember this is number of retries, so will run max_retries + 1.
119
+ }
120
+
114
121
async fn perform_request < F , Fut , R , I > ( & self , input : I , mut request : F ) -> Result < R , Error >
115
122
where
116
123
F : FnMut ( I ) -> Fut + Send + Copy ,
117
124
Fut : Future < Output = Result < R , Error > > + Send ,
118
125
R : Send ,
119
126
I : Send + Clone ,
120
127
{
121
- let retry_config = ExponentialBackoff :: new ( Duration :: from_millis ( self . retry . delay as u64 ) )
122
- . map ( |d| ( self . jitter_fn ) ( d) )
123
- . take ( self . retry . max_retries ) ; // Remember this is number of retries, so will run max_retries + 1.
128
+ let retry_config = self . get_retry_config ( ) ;
124
129
self . retrier
125
130
. retry (
126
131
retry_config,
@@ -263,50 +268,86 @@ impl GrpcStore {
263
268
"CAS operation on AC store"
264
269
) ;
265
270
266
- let mut client = self . bytestream_client . clone ( ) ;
267
-
268
- let error = Arc :: new ( Mutex :: new ( None ) ) ;
269
- struct LocalState {
271
+ struct LocalState < T , E >
272
+ where
273
+ T : Stream < Item = Result < WriteRequest , E > > + Unpin + Send + ' static ,
274
+ E : Into < Error > + ' static ,
275
+ {
270
276
instance_name : String ,
271
- error : Arc < Mutex < Option < Error > > > ,
277
+ error : Mutex < Option < Error > > ,
278
+ read_stream : Mutex < Option < WriteRequestStreamWrapper < T , E > > > ,
279
+ client : ByteStreamClient < Channel > ,
272
280
}
273
281
274
- let local_state = LocalState {
282
+ let local_state = Arc :: new ( LocalState {
275
283
instance_name : self . instance_name . clone ( ) ,
276
- error : error. clone ( ) ,
277
- } ;
284
+ error : Mutex :: new ( None ) ,
285
+ read_stream : Mutex :: new ( Some ( stream) ) ,
286
+ client : self . bytestream_client . clone ( ) ,
287
+ } ) ;
278
288
279
- let stream = unfold ( ( stream, local_state) , move |( mut stream, local_state) | async {
280
- let maybe_message = stream. next ( ) . await ;
281
- if let Ok ( maybe_message) = maybe_message {
282
- if let Some ( mut message) = maybe_message {
283
- // `resource_name` pattern is: "{instance_name}/uploads/{uuid}/blobs/{hash}/{size}".
284
- let first_slash_pos = match message. resource_name . find ( '/' ) {
285
- Some ( pos) => pos,
286
- None => {
287
- log:: error!( "{}" , "Resource name should follow pattern {instance_name}/uploads/{uuid}/blobs/{hash}/{size}" ) ;
289
+ let retry_config = self . get_retry_config ( ) ;
290
+ let result = self
291
+ . retrier
292
+ . retry (
293
+ retry_config,
294
+ unfold ( local_state, move |local_state| async move {
295
+ let stream = unfold ( ( None , local_state. clone ( ) ) , move |( stream, local_state) | async {
296
+ // Only consume the stream on the first request to read,
297
+ // then pass it for future requests in the unfold.
298
+ let Some ( mut stream) = stream. or_else ( || local_state. read_stream . lock ( ) . take ( ) ) else {
299
+ return None ;
300
+ } ;
301
+ let maybe_message = stream. next ( ) . await ;
302
+ if let Ok ( maybe_message) = maybe_message {
303
+ if let Some ( mut message) = maybe_message {
304
+ // `resource_name` pattern is: "{instance_name}/uploads/{uuid}/blobs/{hash}/{size}".
305
+ let first_slash_pos = match message. resource_name . find ( '/' ) {
306
+ Some ( pos) => pos,
307
+ None => {
308
+ log:: error!( "{}" , "Resource name should follow pattern {instance_name}/uploads/{uuid}/blobs/{hash}/{size}" ) ;
309
+ return None ;
310
+ }
311
+ } ;
312
+ message. resource_name = format ! (
313
+ "{}/{}" ,
314
+ & local_state. instance_name,
315
+ message. resource_name. get( ( first_slash_pos + 1 ) ..) . unwrap( )
316
+ ) ;
317
+ return Some ( ( message, ( Some ( stream) , local_state) ) ) ;
318
+ }
288
319
return None ;
289
320
}
321
+ // TODO(allada) I'm sure there's a way to do this without a mutex, but rust can be super
322
+ // picky with borrowing through a stream await.
323
+ * local_state. error . lock ( ) = Some ( maybe_message. unwrap_err ( ) ) ;
324
+ None
325
+ } ) ;
326
+
327
+ let result = local_state. client . clone ( )
328
+ . write ( stream)
329
+ . await
330
+ . err_tip ( || "in GrpcStore::write" ) ;
331
+
332
+ // If the stream has been consumed, don't retry, but
333
+ // otherwise it's ok to try again.
334
+ let result = if local_state. read_stream . lock ( ) . is_some ( ) {
335
+ result. map_or_else ( RetryResult :: Retry , RetryResult :: Ok )
336
+ } else {
337
+ result. map_or_else ( RetryResult :: Err , RetryResult :: Ok )
290
338
} ;
291
- message. resource_name = format ! (
292
- "{}/{}" ,
293
- & local_state. instance_name,
294
- message. resource_name. get( ( first_slash_pos + 1 ) ..) . unwrap( )
295
- ) ;
296
- return Some ( ( message, ( stream, local_state) ) ) ;
297
- }
298
- return None ;
299
- }
300
- // TODO(allada) I'm sure there's a way to do this without a mutex, but rust can be super
301
- // picky with borrowing through a stream await.
302
- * local_state. error . lock ( ) = Some ( maybe_message. unwrap_err ( ) ) ;
303
- None
304
- } ) ;
305
339
306
- let result = client. write ( stream) . await . err_tip ( || "in GrpcStore::write" ) ?;
307
- if let Some ( err) = error. lock ( ) . take ( ) {
308
- return Err ( err) ;
309
- }
340
+ // If there was an error with the stream, then don't retry.
341
+ let result = if let Some ( err) = local_state. error . lock ( ) . take ( ) {
342
+ RetryResult :: Err ( err)
343
+ } else {
344
+ result
345
+ } ;
346
+
347
+ Some ( ( result, local_state) )
348
+ } ) ,
349
+ )
350
+ . await ?;
310
351
Ok ( result)
311
352
}
312
353
0 commit comments