1#![cfg_attr(docsrs, feature(doc_cfg))]
21#![deny(missing_docs)]
22
23use std::future::Future;
24use std::sync::Arc;
25use std::time::Duration;
26
27use opendal_core::raw::*;
28use opendal_core::*;
29
30#[derive(Clone, Debug)]
123pub struct TimeoutLayer {
124 timeout: Duration,
125 io_timeout: Duration,
126}
127
128impl Default for TimeoutLayer {
129 fn default() -> Self {
130 Self {
131 timeout: Duration::from_secs(60),
132 io_timeout: Duration::from_secs(10),
133 }
134 }
135}
136
137impl TimeoutLayer {
138 pub fn new() -> Self {
140 Self::default()
141 }
142
143 pub fn with_timeout(mut self, timeout: Duration) -> Self {
147 self.timeout = timeout;
148 self
149 }
150
151 pub fn with_io_timeout(mut self, timeout: Duration) -> Self {
155 self.io_timeout = timeout;
156 self
157 }
158}
159
160impl Layer for TimeoutLayer {
161 fn apply_service(&self, inner: Servicer) -> Servicer {
162 Arc::new(self.layer(inner))
163 }
164
165 fn apply_context(&self, _srv: Servicer, inner: OperationContext) -> OperationContext {
166 let executor = Executor::with(TimeoutExecutor::new(
168 inner.executor().clone().into_inner(),
169 self.io_timeout,
170 ));
171 inner.with_executor(executor)
172 }
173}
174
175impl TimeoutLayer {
176 fn layer(&self, inner: Servicer) -> TimeoutService {
177 TimeoutService {
178 inner,
179 timeout: self.timeout,
180 io_timeout: self.io_timeout,
181 }
182 }
183}
184
185#[doc(hidden)]
186#[derive(Debug)]
187pub struct TimeoutService {
188 inner: Servicer,
189 timeout: Duration,
190 io_timeout: Duration,
191}
192
193impl TimeoutService {
194 async fn timeout<F: Future<Output = Result<T>>, T>(&self, op: Operation, fut: F) -> Result<T> {
195 tokio::time::timeout(self.timeout, fut).await.map_err(|_| {
196 Error::new(ErrorKind::Unexpected, "operation timeout reached")
197 .with_operation(op)
198 .with_context("timeout", self.timeout.as_secs_f64().to_string())
199 .set_temporary()
200 })?
201 }
202}
203
204impl Service for TimeoutService {
205 type Reader = TimeoutWrapper<oio::Reader>;
206 type Writer = TimeoutWrapper<oio::Writer>;
207 type Lister = TimeoutWrapper<oio::Lister>;
208 type Deleter = TimeoutWrapper<oio::Deleter>;
209 type Copier = TimeoutWrapper<oio::Copier>;
210
211 fn info(&self) -> ServiceInfo {
212 self.inner.info()
213 }
214
215 fn capability(&self) -> Capability {
216 self.inner.capability()
217 }
218
219 async fn create_dir(
220 &self,
221 ctx: &OperationContext,
222 path: &str,
223 args: OpCreateDir,
224 ) -> Result<RpCreateDir> {
225 self.timeout(Operation::CreateDir, self.inner.create_dir(ctx, path, args))
226 .await
227 }
228
229 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
230 self.inner
231 .read(ctx, path, args)
232 .map(|r| TimeoutWrapper::new(r, self.io_timeout))
233 }
234
235 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
236 self.inner
237 .write(ctx, path, args)
238 .map(|r| TimeoutWrapper::new(r, self.io_timeout))
239 }
240
241 fn copy(
242 &self,
243 ctx: &OperationContext,
244 from: &str,
245 to: &str,
246 args: OpCopy,
247 opts: OpCopier,
248 ) -> Result<Self::Copier> {
249 self.inner
250 .copy(ctx, from, to, args, opts)
251 .map(|c| TimeoutWrapper::new(c, self.io_timeout))
252 }
253
254 async fn rename(
255 &self,
256 ctx: &OperationContext,
257 from: &str,
258 to: &str,
259 args: OpRename,
260 ) -> Result<RpRename> {
261 self.timeout(Operation::Rename, self.inner.rename(ctx, from, to, args))
262 .await
263 }
264
265 async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
266 self.timeout(Operation::Stat, self.inner.stat(ctx, path, args))
267 .await
268 }
269
270 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
271 self.inner
272 .delete(ctx)
273 .map(|r| TimeoutWrapper::new(r, self.io_timeout))
274 }
275
276 fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
277 self.inner
278 .list(ctx, path, args)
279 .map(|r| TimeoutWrapper::new(r, self.io_timeout))
280 }
281
282 async fn presign(
283 &self,
284 ctx: &OperationContext,
285 path: &str,
286 args: OpPresign,
287 ) -> Result<RpPresign> {
288 self.timeout(Operation::Presign, self.inner.presign(ctx, path, args))
289 .await
290 }
291}
292
293struct TimeoutExecutor {
294 exec: Arc<dyn Execute>,
295 timeout: Duration,
296}
297
298impl TimeoutExecutor {
299 fn new(exec: Arc<dyn Execute>, timeout: Duration) -> Self {
300 Self { exec, timeout }
301 }
302}
303
304impl Execute for TimeoutExecutor {
305 fn execute(&self, f: BoxedStaticFuture<()>) {
306 self.exec.execute(f)
307 }
308
309 fn timeout(&self) -> Option<BoxedStaticFuture<()>> {
310 Some(Box::pin(tokio::time::sleep(self.timeout)))
311 }
312}
313
314#[doc(hidden)]
315pub struct TimeoutWrapper<R> {
316 inner: R,
317
318 timeout: Duration,
319}
320
321impl<R> TimeoutWrapper<R> {
322 fn new(inner: R, timeout: Duration) -> Self {
323 Self { inner, timeout }
324 }
325
326 #[inline]
327 async fn io_timeout<F: Future<Output = Result<T>>, T>(
328 timeout: Duration,
329 op: &'static str,
330 fut: F,
331 ) -> Result<T> {
332 tokio::time::timeout(timeout, fut).await.map_err(|_| {
333 Error::new(ErrorKind::Unexpected, "io operation timeout reached")
334 .with_operation(op)
335 .with_context("timeout", timeout.as_secs_f64().to_string())
336 .set_temporary()
337 })?
338 }
339}
340
341impl<R: oio::ReadStream> oio::ReadStream for TimeoutWrapper<R> {
342 async fn read(&mut self) -> Result<Buffer> {
343 let fut = self.inner.read();
344 Self::io_timeout(self.timeout, Operation::Read.into_static(), fut).await
345 }
346}
347
348impl<R: oio::Read> oio::Read for TimeoutWrapper<R> {
349 async fn open(&self, range: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
350 let (rp, stream) = Self::io_timeout(
351 self.timeout,
352 Operation::Read.into_static(),
353 self.inner.open(range),
354 )
355 .await?;
356 Ok((
357 rp,
358 Box::new(TimeoutWrapper::new(stream, self.timeout)) as Box<dyn oio::ReadStreamDyn>,
359 ))
360 }
361
362 async fn read(&self, range: BytesRange) -> Result<(RpRead, Buffer)> {
363 Self::io_timeout(
364 self.timeout,
365 Operation::Read.into_static(),
366 self.inner.read(range),
367 )
368 .await
369 }
370}
371
372impl<R: oio::Write> oio::Write for TimeoutWrapper<R> {
373 async fn write(&mut self, bs: Buffer) -> Result<()> {
374 let fut = self.inner.write(bs);
375 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
376 }
377
378 async fn close(&mut self) -> Result<Metadata> {
379 let fut = self.inner.close();
380 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
381 }
382
383 async fn abort(&mut self) -> Result<()> {
384 let fut = self.inner.abort();
385 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
386 }
387}
388
389impl<R: oio::List> oio::List for TimeoutWrapper<R> {
390 async fn next(&mut self) -> Result<Option<oio::Entry>> {
391 let fut = self.inner.next();
392 Self::io_timeout(self.timeout, Operation::List.into_static(), fut).await
393 }
394}
395
396impl<R: oio::Delete> oio::Delete for TimeoutWrapper<R> {
397 async fn delete(&mut self, path: &str, args: OpDelete) -> Result<()> {
398 let fut = self.inner.delete(path, args);
399 Self::io_timeout(self.timeout, Operation::Delete.into_static(), fut).await
400 }
401
402 async fn close(&mut self) -> Result<()> {
403 let fut = self.inner.close();
404 Self::io_timeout(self.timeout, Operation::Delete.into_static(), fut).await
405 }
406}
407
408impl<C: oio::Copy> oio::Copy for TimeoutWrapper<C> {
409 async fn next(&mut self) -> Result<Option<usize>> {
410 let fut = self.inner.next();
411 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
412 }
413
414 async fn close(&mut self) -> Result<Metadata> {
415 let fut = self.inner.close();
416 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
417 }
418
419 async fn abort(&mut self) -> Result<()> {
420 let fut = self.inner.abort();
421 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use std::future::pending;
428
429 use futures::StreamExt;
430 use tokio::time::timeout;
431
432 use super::*;
433
434 #[derive(Debug, Clone, Default)]
435 struct MockService;
436
437 impl Service for MockService {
438 type Reader = MockReader;
439 type Writer = ();
440 type Lister = MockLister;
441 type Deleter = MockDeleter;
442 type Copier = MockCopier;
443
444 fn info(&self) -> ServiceInfo {
445 ServiceInfo::with_scheme("mock")
446 }
447
448 fn capability(&self) -> Capability {
449 Capability {
450 read: true,
451 delete: true,
452 list: true,
453 copy: true,
454 ..Default::default()
455 }
456 }
457
458 async fn create_dir(
459 &self,
460 _: &OperationContext,
461 _: &str,
462 _: OpCreateDir,
463 ) -> Result<RpCreateDir> {
464 Err(Error::new(
465 ErrorKind::Unsupported,
466 "operation is not supported",
467 ))
468 }
469
470 async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
471 Err(Error::new(
472 ErrorKind::Unsupported,
473 "operation is not supported",
474 ))
475 }
476
477 fn read(&self, _ctx: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
479 Ok(MockReader)
480 }
481
482 fn write(&self, _ctx: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
483 Err(Error::new(
484 ErrorKind::Unsupported,
485 "operation is not supported",
486 ))
487 }
488
489 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
491 Ok(MockDeleter)
492 }
493
494 fn list(&self, _ctx: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
495 Ok(MockLister)
496 }
497
498 fn copy(
499 &self,
500 _: &OperationContext,
501 _: &str,
502 _: &str,
503 _: OpCopy,
504 _: OpCopier,
505 ) -> Result<Self::Copier> {
506 Ok(MockCopier)
507 }
508
509 async fn rename(
510 &self,
511 _: &OperationContext,
512 _: &str,
513 _: &str,
514 _: OpRename,
515 ) -> Result<RpRename> {
516 Err(Error::new(
517 ErrorKind::Unsupported,
518 "operation is not supported",
519 ))
520 }
521
522 async fn presign(&self, _: &OperationContext, _: &str, _: OpPresign) -> Result<RpPresign> {
523 Err(Error::new(
524 ErrorKind::Unsupported,
525 "operation is not supported",
526 ))
527 }
528 }
529
530 #[derive(Debug, Clone, Default)]
531 struct MockReader;
532
533 impl oio::Read for MockReader {
534 async fn open(&self, _: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
535 pending().await
536 }
537
538 async fn read(&self, _: BytesRange) -> Result<(RpRead, Buffer)> {
539 pending().await
540 }
541 }
542
543 #[derive(Debug, Clone, Default)]
544 struct MockLister;
545
546 impl oio::List for MockLister {
547 async fn next(&mut self) -> Result<Option<oio::Entry>> {
548 pending().await
549 }
550 }
551
552 #[derive(Debug, Clone, Default)]
553 struct MockDeleter;
554
555 impl oio::Delete for MockDeleter {
556 async fn delete(&mut self, _: &str, _: OpDelete) -> Result<()> {
557 pending().await
558 }
559
560 async fn close(&mut self) -> Result<()> {
561 Ok(())
562 }
563 }
564
565 #[derive(Debug, Clone, Default)]
566 struct MockCopier;
567
568 impl oio::Copy for MockCopier {
569 async fn next(&mut self) -> Result<Option<usize>> {
570 pending().await
571 }
572
573 async fn close(&mut self) -> Result<Metadata> {
574 pending().await
575 }
576
577 async fn abort(&mut self) -> Result<()> {
578 pending().await
579 }
580 }
581
582 #[tokio::test]
583 async fn test_delete_timeout() {
584 let srv = MockService;
585 let op = Operator::from_parts(OperationContext::default(), Arc::new(srv))
586 .layer(TimeoutLayer::default().with_io_timeout(Duration::from_secs(1)));
587
588 let fut = async {
589 let res = op.delete("test").await;
590 assert!(res.is_err());
591 let err = res.unwrap_err();
592 assert_eq!(err.kind(), ErrorKind::Unexpected);
593 assert!(err.to_string().contains("timeout"))
594 };
595
596 timeout(Duration::from_secs(2), fut)
597 .await
598 .expect("this test should not exceed 2 seconds")
599 }
600
601 #[tokio::test]
602 async fn test_io_timeout() {
603 let srv = MockService;
604 let op = Operator::from_parts(OperationContext::default(), Arc::new(srv))
605 .layer(TimeoutLayer::default().with_io_timeout(Duration::from_secs(1)));
606
607 let reader = op.reader("test").await.unwrap();
608
609 let res = reader.read(0..4).await;
610 assert!(res.is_err());
611 let err = res.unwrap_err();
612 assert_eq!(err.kind(), ErrorKind::Unexpected);
613 assert!(err.to_string().contains("timeout"))
614 }
615
616 #[tokio::test]
617 async fn test_list_timeout() {
618 let srv = MockService;
619 let op = Operator::from_parts(OperationContext::default(), Arc::new(srv)).layer(
620 TimeoutLayer::default()
621 .with_timeout(Duration::from_secs(1))
622 .with_io_timeout(Duration::from_secs(1)),
623 );
624
625 let mut lister = op.lister("test").await.unwrap();
626
627 let res = lister.next().await.unwrap();
628 assert!(res.is_err());
629 let err = res.unwrap_err();
630 assert_eq!(err.kind(), ErrorKind::Unexpected);
631 assert!(err.to_string().contains("timeout"))
632 }
633
634 #[tokio::test]
635 async fn test_delete_io_timeout() {
636 use oio::Delete;
637
638 let mut deleter = TimeoutWrapper::new(MockDeleter, Duration::from_secs(1));
639
640 let res = deleter.delete("test", OpDelete::default()).await;
641 assert!(res.is_err());
642 let err = res.unwrap_err();
643 assert_eq!(err.kind(), ErrorKind::Unexpected);
644 assert!(err.to_string().contains("timeout"));
645 }
646
647 #[tokio::test]
648 async fn test_copy_io_timeout() {
649 use oio::Copy;
650
651 let service = TimeoutLayer::default()
652 .with_io_timeout(Duration::from_millis(100))
653 .apply_service(Arc::new(MockService));
654 let ctx = OperationContext::new();
655 let mut copier = service
656 .copy(&ctx, "f", "t", OpCopy::default(), OpCopier::default())
657 .unwrap();
658
659 let err = copier.next().await.unwrap_err();
660 assert!(err.to_string().contains("timeout"));
661 }
662
663 #[tokio::test]
664 async fn test_list_timeout_raw() {
665 use oio::List;
666
667 let timeout_layer = TimeoutLayer::default()
668 .with_timeout(Duration::from_secs(1))
669 .with_io_timeout(Duration::from_secs(1));
670 let service = timeout_layer.apply_service(Arc::new(MockService));
671 let ctx = OperationContext::new();
672
673 let mut lister = service.list(&ctx, "test", OpList::default()).unwrap();
674
675 let res = lister.next().await;
676 assert!(res.is_err());
677 let err = res.unwrap_err();
678 assert_eq!(err.kind(), ErrorKind::Unexpected);
679 assert!(err.to_string().contains("timeout"));
680 }
681}