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)]
120pub struct TimeoutLayer {
121 timeout: Duration,
122 io_timeout: Duration,
123}
124
125impl Default for TimeoutLayer {
126 fn default() -> Self {
127 Self {
128 timeout: Duration::from_secs(60),
129 io_timeout: Duration::from_secs(10),
130 }
131 }
132}
133
134impl TimeoutLayer {
135 pub fn new() -> Self {
137 Self::default()
138 }
139
140 pub fn with_timeout(mut self, timeout: Duration) -> Self {
144 self.timeout = timeout;
145 self
146 }
147
148 pub fn with_io_timeout(mut self, timeout: Duration) -> Self {
152 self.io_timeout = timeout;
153 self
154 }
155}
156
157impl<A: Access> Layer<A> for TimeoutLayer {
158 type LayeredAccess = TimeoutAccessor<A>;
159
160 fn layer(&self, inner: A) -> Self::LayeredAccess {
161 let info = inner.info();
162 info.update_executor(|exec| {
163 Executor::with(TimeoutExecutor::new(exec.into_inner(), self.io_timeout))
164 });
165
166 TimeoutAccessor {
167 inner,
168
169 timeout: self.timeout,
170 io_timeout: self.io_timeout,
171 }
172 }
173}
174
175#[doc(hidden)]
176#[derive(Debug)]
177pub struct TimeoutAccessor<A: Access> {
178 inner: A,
179
180 timeout: Duration,
181 io_timeout: Duration,
182}
183
184impl<A: Access> TimeoutAccessor<A> {
185 async fn timeout<F: Future<Output = Result<T>>, T>(&self, op: Operation, fut: F) -> Result<T> {
186 tokio::time::timeout(self.timeout, fut).await.map_err(|_| {
187 Error::new(ErrorKind::Unexpected, "operation timeout reached")
188 .with_operation(op)
189 .with_context("timeout", self.timeout.as_secs_f64().to_string())
190 .set_temporary()
191 })?
192 }
193
194 async fn io_timeout<F: Future<Output = Result<T>>, T>(
195 &self,
196 op: Operation,
197 fut: F,
198 ) -> Result<T> {
199 tokio::time::timeout(self.io_timeout, fut)
200 .await
201 .map_err(|_| {
202 Error::new(ErrorKind::Unexpected, "io timeout reached")
203 .with_operation(op)
204 .with_context("timeout", self.io_timeout.as_secs_f64().to_string())
205 .set_temporary()
206 })?
207 }
208}
209
210impl<A: Access> LayeredAccess for TimeoutAccessor<A> {
211 type Inner = A;
212 type Reader = TimeoutWrapper<A::Reader>;
213 type Writer = TimeoutWrapper<A::Writer>;
214 type Lister = TimeoutWrapper<A::Lister>;
215 type Deleter = TimeoutWrapper<A::Deleter>;
216 type Copier = TimeoutWrapper<A::Copier>;
217
218 fn inner(&self) -> &Self::Inner {
219 &self.inner
220 }
221
222 async fn create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
223 self.timeout(Operation::CreateDir, self.inner.create_dir(path, args))
224 .await
225 }
226
227 async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
228 self.io_timeout(Operation::Read, self.inner.read(path, args))
229 .await
230 .map(|(rp, r)| (rp, TimeoutWrapper::new(r, self.io_timeout)))
231 }
232
233 async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
234 self.io_timeout(Operation::Write, self.inner.write(path, args))
235 .await
236 .map(|(rp, r)| (rp, TimeoutWrapper::new(r, self.io_timeout)))
237 }
238
239 async fn copy(
240 &self,
241 from: &str,
242 to: &str,
243 args: OpCopy,
244 opts: OpCopier,
245 ) -> Result<(RpCopy, Self::Copier)> {
246 self.timeout(
247 Operation::Copy,
248 self.inner.copy(from, to, args, opts.clone()),
249 )
250 .await
251 .map(|(rp, c)| (rp, TimeoutWrapper::new(c, self.io_timeout)))
252 }
253
254 async fn rename(&self, from: &str, to: &str, args: OpRename) -> Result<RpRename> {
255 self.timeout(Operation::Rename, self.inner.rename(from, to, args))
256 .await
257 }
258
259 async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
260 self.timeout(Operation::Stat, self.inner.stat(path, args))
261 .await
262 }
263
264 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
265 self.timeout(Operation::Delete, self.inner.delete())
266 .await
267 .map(|(rp, r)| (rp, TimeoutWrapper::new(r, self.io_timeout)))
268 }
269
270 async fn list(&self, path: &str, args: OpList) -> Result<(RpList, Self::Lister)> {
271 self.io_timeout(Operation::List, self.inner.list(path, args))
272 .await
273 .map(|(rp, r)| (rp, TimeoutWrapper::new(r, self.io_timeout)))
274 }
275
276 async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
277 self.timeout(Operation::Presign, self.inner.presign(path, args))
278 .await
279 }
280}
281
282struct TimeoutExecutor {
283 exec: Arc<dyn Execute>,
284 timeout: Duration,
285}
286
287impl TimeoutExecutor {
288 fn new(exec: Arc<dyn Execute>, timeout: Duration) -> Self {
289 Self { exec, timeout }
290 }
291}
292
293impl Execute for TimeoutExecutor {
294 fn execute(&self, f: BoxedStaticFuture<()>) {
295 self.exec.execute(f)
296 }
297
298 fn timeout(&self) -> Option<BoxedStaticFuture<()>> {
299 Some(Box::pin(tokio::time::sleep(self.timeout)))
300 }
301}
302
303#[doc(hidden)]
304pub struct TimeoutWrapper<R> {
305 inner: R,
306
307 timeout: Duration,
308}
309
310impl<R> TimeoutWrapper<R> {
311 fn new(inner: R, timeout: Duration) -> Self {
312 Self { inner, timeout }
313 }
314
315 #[inline]
316 async fn io_timeout<F: Future<Output = Result<T>>, T>(
317 timeout: Duration,
318 op: &'static str,
319 fut: F,
320 ) -> Result<T> {
321 tokio::time::timeout(timeout, fut).await.map_err(|_| {
322 Error::new(ErrorKind::Unexpected, "io operation timeout reached")
323 .with_operation(op)
324 .with_context("timeout", timeout.as_secs_f64().to_string())
325 .set_temporary()
326 })?
327 }
328}
329
330impl<R: oio::Read> oio::Read for TimeoutWrapper<R> {
331 async fn read(&mut self) -> Result<Buffer> {
332 let fut = self.inner.read();
333 Self::io_timeout(self.timeout, Operation::Read.into_static(), fut).await
334 }
335}
336
337impl<R: oio::Write> oio::Write for TimeoutWrapper<R> {
338 async fn write(&mut self, bs: Buffer) -> Result<()> {
339 let fut = self.inner.write(bs);
340 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
341 }
342
343 async fn close(&mut self) -> Result<Metadata> {
344 let fut = self.inner.close();
345 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
346 }
347
348 async fn abort(&mut self) -> Result<()> {
349 let fut = self.inner.abort();
350 Self::io_timeout(self.timeout, Operation::Write.into_static(), fut).await
351 }
352}
353
354impl<R: oio::List> oio::List for TimeoutWrapper<R> {
355 async fn next(&mut self) -> Result<Option<oio::Entry>> {
356 let fut = self.inner.next();
357 Self::io_timeout(self.timeout, Operation::List.into_static(), fut).await
358 }
359}
360
361impl<R: oio::Delete> oio::Delete for TimeoutWrapper<R> {
362 async fn delete(&mut self, path: &str, args: OpDelete) -> Result<()> {
363 let fut = self.inner.delete(path, args);
364 Self::io_timeout(self.timeout, Operation::Delete.into_static(), fut).await
365 }
366
367 async fn close(&mut self) -> Result<()> {
368 let fut = self.inner.close();
369 Self::io_timeout(self.timeout, Operation::Delete.into_static(), fut).await
370 }
371}
372
373impl<C: oio::Copy> oio::Copy for TimeoutWrapper<C> {
374 async fn next(&mut self) -> Result<Option<usize>> {
375 let fut = self.inner.next();
376 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
377 }
378
379 async fn close(&mut self) -> Result<Metadata> {
380 let fut = self.inner.close();
381 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
382 }
383
384 async fn abort(&mut self) -> Result<()> {
385 let fut = self.inner.abort();
386 Self::io_timeout(self.timeout, Operation::Copy.into_static(), fut).await
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use std::future::pending;
393
394 use futures::StreamExt;
395 use tokio::time::sleep;
396 use tokio::time::timeout;
397
398 use super::*;
399
400 #[derive(Debug, Clone, Default)]
401 struct MockService;
402
403 impl Access for MockService {
404 type Reader = oio::Reader;
405 type Writer = oio::Writer;
406 type Lister = oio::Lister;
407 type Deleter = oio::Deleter;
408 type Copier = oio::Copier;
409
410 fn info(&self) -> Arc<AccessorInfo> {
411 let am = AccessorInfo::default();
412 am.set_native_capability(Capability {
413 read: true,
414 delete: true,
415 ..Default::default()
416 });
417
418 am.into()
419 }
420
421 async fn read(&self, _: &str, _: OpRead) -> Result<(RpRead, Self::Reader)> {
423 Ok((
424 RpRead::new(Metadata::new(EntryMode::FILE).with_content_length(0)),
425 Box::new(MockReader),
426 ))
427 }
428
429 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
431 sleep(Duration::from_secs(u64::MAX)).await;
432
433 Ok((RpDelete::default(), Box::new(())))
434 }
435
436 async fn list(&self, _: &str, _: OpList) -> Result<(RpList, Self::Lister)> {
437 Ok((RpList::default(), Box::new(MockLister)))
438 }
439
440 async fn copy(
441 &self,
442 _: &str,
443 _: &str,
444 _: OpCopy,
445 _: OpCopier,
446 ) -> Result<(RpCopy, Self::Copier)> {
447 Ok((RpCopy::default(), Box::new(MockCopier)))
448 }
449 }
450
451 #[derive(Debug, Clone, Default)]
452 struct MockReader;
453
454 impl oio::Read for MockReader {
455 fn read(&mut self) -> impl Future<Output = Result<Buffer>> {
456 pending()
457 }
458 }
459
460 #[derive(Debug, Clone, Default)]
461 struct MockLister;
462
463 impl oio::List for MockLister {
464 fn next(&mut self) -> impl Future<Output = Result<Option<oio::Entry>>> {
465 pending()
466 }
467 }
468
469 #[derive(Debug, Clone, Default)]
470 struct MockDeleter;
471
472 impl oio::Delete for MockDeleter {
473 fn delete(&mut self, _: &str, _: OpDelete) -> impl Future<Output = Result<()>> {
474 pending()
475 }
476
477 async fn close(&mut self) -> Result<()> {
478 Ok(())
479 }
480 }
481
482 #[derive(Debug, Clone, Default)]
483 struct MockCopier;
484
485 impl oio::Copy for MockCopier {
486 fn next(&mut self) -> impl Future<Output = Result<Option<usize>>> {
487 pending()
488 }
489
490 fn close(&mut self) -> impl Future<Output = Result<Metadata>> {
491 pending()
492 }
493
494 fn abort(&mut self) -> impl Future<Output = Result<()>> {
495 pending()
496 }
497 }
498
499 #[tokio::test]
500 async fn test_operation_timeout() {
501 let srv = MockService;
502 let op = Operator::from_inner(Arc::new(srv))
503 .layer(TimeoutLayer::default().with_timeout(Duration::from_secs(1)));
504
505 let fut = async {
506 let res = op.delete("test").await;
507 assert!(res.is_err());
508 let err = res.unwrap_err();
509 assert_eq!(err.kind(), ErrorKind::Unexpected);
510 assert!(err.to_string().contains("timeout"))
511 };
512
513 timeout(Duration::from_secs(2), fut)
514 .await
515 .expect("this test should not exceed 2 seconds")
516 }
517
518 #[tokio::test]
519 async fn test_io_timeout() {
520 let srv = MockService;
521 let op = Operator::from_inner(Arc::new(srv))
522 .layer(TimeoutLayer::default().with_io_timeout(Duration::from_secs(1)));
523
524 let reader = op.reader("test").await.unwrap();
525
526 let res = reader.read(0..4).await;
527 assert!(res.is_err());
528 let err = res.unwrap_err();
529 assert_eq!(err.kind(), ErrorKind::Unexpected);
530 assert!(err.to_string().contains("timeout"))
531 }
532
533 #[tokio::test]
534 async fn test_list_timeout() {
535 let srv = MockService;
536 let op = Operator::from_inner(Arc::new(srv)).layer(
537 TimeoutLayer::default()
538 .with_timeout(Duration::from_secs(1))
539 .with_io_timeout(Duration::from_secs(1)),
540 );
541
542 let mut lister = op.lister("test").await.unwrap();
543
544 let res = lister.next().await.unwrap();
545 assert!(res.is_err());
546 let err = res.unwrap_err();
547 assert_eq!(err.kind(), ErrorKind::Unexpected);
548 assert!(err.to_string().contains("timeout"))
549 }
550
551 #[tokio::test]
552 async fn test_delete_io_timeout() {
553 use oio::Delete;
554
555 let mut deleter = TimeoutWrapper::new(MockDeleter, Duration::from_secs(1));
556
557 let res = deleter.delete("test", OpDelete::default()).await;
558 assert!(res.is_err());
559 let err = res.unwrap_err();
560 assert_eq!(err.kind(), ErrorKind::Unexpected);
561 assert!(err.to_string().contains("timeout"));
562 }
563
564 #[tokio::test]
565 async fn test_copy_io_timeout() {
566 use oio::Copy;
567
568 let acc = TimeoutLayer::default()
569 .with_io_timeout(Duration::from_millis(100))
570 .layer(MockService);
571 let (_, mut copier) = Access::copy(&acc, "f", "t", OpCopy::default(), OpCopier::default())
572 .await
573 .unwrap();
574
575 let err = copier.next().await.unwrap_err();
576 assert!(err.to_string().contains("timeout"));
577 }
578
579 #[tokio::test]
580 async fn test_list_timeout_raw() {
581 use oio::List;
582
583 let acc = MockService;
584 let timeout_layer = TimeoutLayer::default()
585 .with_timeout(Duration::from_secs(1))
586 .with_io_timeout(Duration::from_secs(1));
587 let timeout_acc = timeout_layer.layer(acc);
588
589 let (_, mut lister) = Access::list(&timeout_acc, "test", OpList::default())
590 .await
591 .unwrap();
592
593 let res = lister.next().await;
594 assert!(res.is_err());
595 let err = res.unwrap_err();
596 assert_eq!(err.kind(), ErrorKind::Unexpected);
597 assert!(err.to_string().contains("timeout"));
598 }
599}