1mod client_timeout;
57mod too_many_requests;
58
59use crate::error::Error;
60use crate::retry_result::RetryResult;
61use crate::retry_state::RetryState;
62use crate::throttle_result::ThrottleResult;
63use std::sync::Arc;
64use std::time::Duration;
65
66pub use client_timeout::ClientTimeout;
67pub use too_many_requests::TooManyRequests;
68
69pub trait RetryPolicy: Send + Sync + std::fmt::Debug {
74 #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
80 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult;
81
82 #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
94 fn on_throttle(&self, _state: &RetryState, error: Error) -> ThrottleResult {
95 ThrottleResult::Continue(error)
96 }
97
98 #[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
109 fn remaining_time(&self, _state: &RetryState) -> Option<Duration> {
110 None
111 }
112}
113
114#[derive(Clone, Debug)]
116pub struct RetryPolicyArg(Arc<dyn RetryPolicy>);
117
118impl<T> std::convert::From<T> for RetryPolicyArg
119where
120 T: RetryPolicy + 'static,
121{
122 fn from(value: T) -> Self {
123 Self(Arc::new(value))
124 }
125}
126
127impl std::convert::From<Arc<dyn RetryPolicy>> for RetryPolicyArg {
128 fn from(value: Arc<dyn RetryPolicy>) -> Self {
129 Self(value)
130 }
131}
132
133impl From<RetryPolicyArg> for Arc<dyn RetryPolicy> {
134 fn from(value: RetryPolicyArg) -> Arc<dyn RetryPolicy> {
135 value.0
136 }
137}
138
139pub trait RetryPolicyExt: RetryPolicy + Sized {
141 fn with_time_limit(self, maximum_duration: Duration) -> LimitedElapsedTime<Self> {
162 LimitedElapsedTime::custom(self, maximum_duration)
163 }
164
165 fn with_attempt_limit(self, maximum_attempts: u32) -> LimitedAttemptCount<Self> {
192 LimitedAttemptCount::custom(self, maximum_attempts)
193 }
194
195 fn continue_on_too_many_requests(self) -> TooManyRequests<Self> {
228 TooManyRequests::new(self)
229 }
230
231 fn continue_on_client_timeout(self) -> ClientTimeout<Self> {
256 ClientTimeout::new(self)
257 }
258}
259
260impl<T: RetryPolicy> RetryPolicyExt for T {}
261
262#[derive(Clone, Debug)]
286pub struct Aip194Strict;
287
288impl RetryPolicy for Aip194Strict {
289 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult {
290 use crate::error::rpc::Code;
291 use http::StatusCode;
292
293 if error.is_transient_and_before_rpc() {
294 return RetryResult::Continue(error);
295 }
296 if !state.idempotent {
297 return RetryResult::Permanent(error);
298 }
299 if error.is_io() {
300 return RetryResult::Continue(error);
301 }
302 if error.status().is_some_and(|s| s.code == Code::Unavailable) {
303 return RetryResult::Continue(error);
304 }
305 if error
309 .http_status_code()
310 .is_some_and(|code| code == StatusCode::SERVICE_UNAVAILABLE.as_u16())
311 {
312 return RetryResult::Continue(error);
313 }
314 RetryResult::Permanent(error)
315 }
316}
317
318#[derive(Clone, Debug)]
339pub struct AlwaysRetry;
340
341impl RetryPolicy for AlwaysRetry {
342 fn on_error(&self, _state: &RetryState, error: Error) -> RetryResult {
343 RetryResult::Continue(error)
344 }
345}
346
347#[derive(Clone, Debug)]
365pub struct NeverRetry;
366
367impl RetryPolicy for NeverRetry {
368 fn on_error(&self, _state: &RetryState, error: Error) -> RetryResult {
369 RetryResult::Exhausted(error)
370 }
371}
372
373#[derive(thiserror::Error, Debug)]
375pub struct LimitedElapsedTimeError {
376 maximum_duration: Duration,
377 #[source]
378 source: Error,
379}
380
381impl LimitedElapsedTimeError {
382 pub(crate) fn new(maximum_duration: Duration, source: Error) -> Self {
383 Self {
384 maximum_duration,
385 source,
386 }
387 }
388
389 pub fn maximum_duration(&self) -> Duration {
391 self.maximum_duration
392 }
393}
394
395impl std::fmt::Display for LimitedElapsedTimeError {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 write!(
398 f,
399 "retry policy is exhausted after {}s, the last retry attempt was throttled",
400 self.maximum_duration.as_secs_f64()
401 )
402 }
403}
404
405#[derive(Debug)]
420pub struct LimitedElapsedTime<P = Aip194Strict>
421where
422 P: RetryPolicy,
423{
424 inner: P,
425 maximum_duration: Duration,
426}
427
428impl LimitedElapsedTime {
429 pub fn new(maximum_duration: Duration) -> Self {
440 Self {
441 inner: Aip194Strict,
442 maximum_duration,
443 }
444 }
445}
446
447impl<P> LimitedElapsedTime<P>
448where
449 P: RetryPolicy,
450{
451 pub fn custom(inner: P, maximum_duration: Duration) -> Self {
469 Self {
470 inner,
471 maximum_duration,
472 }
473 }
474
475 fn error_if_exhausted(&self, state: &RetryState, error: Error) -> ThrottleResult {
476 let deadline = state.start + self.maximum_duration;
477 let now = tokio::time::Instant::now().into_std();
478 if now < deadline {
479 ThrottleResult::Continue(error)
480 } else {
481 ThrottleResult::Exhausted(Error::exhausted(LimitedElapsedTimeError::new(
482 self.maximum_duration,
483 error,
484 )))
485 }
486 }
487}
488
489impl<P> RetryPolicy for LimitedElapsedTime<P>
490where
491 P: RetryPolicy + 'static,
492{
493 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult {
494 match self.inner.on_error(state, error) {
495 RetryResult::Permanent(e) => RetryResult::Permanent(e),
496 RetryResult::Exhausted(e) => RetryResult::Exhausted(e),
497 RetryResult::Continue(e) => {
498 if tokio::time::Instant::now().into_std() >= state.start + self.maximum_duration {
499 RetryResult::Exhausted(e)
500 } else {
501 RetryResult::Continue(e)
502 }
503 }
504 }
505 }
506
507 fn on_throttle(&self, state: &RetryState, error: Error) -> ThrottleResult {
508 match self.inner.on_throttle(state, error) {
509 ThrottleResult::Continue(e) => self.error_if_exhausted(state, e),
510 ThrottleResult::Exhausted(e) => ThrottleResult::Exhausted(e),
511 }
512 }
513
514 fn remaining_time(&self, state: &RetryState) -> Option<Duration> {
515 let deadline = state.start + self.maximum_duration;
516 let remaining = deadline.saturating_duration_since(tokio::time::Instant::now().into_std());
517 if let Some(inner) = self.inner.remaining_time(state) {
518 return Some(std::cmp::min(remaining, inner));
519 }
520 Some(remaining)
521 }
522}
523
524#[derive(Debug)]
539pub struct LimitedAttemptCount<P = Aip194Strict>
540where
541 P: RetryPolicy,
542{
543 inner: P,
544 maximum_attempts: u32,
545}
546
547impl LimitedAttemptCount {
548 pub fn new(maximum_attempts: u32) -> Self {
556 Self {
557 inner: Aip194Strict,
558 maximum_attempts,
559 }
560 }
561}
562
563impl<P> LimitedAttemptCount<P>
564where
565 P: RetryPolicy,
566{
567 pub fn custom(inner: P, maximum_attempts: u32) -> Self {
581 Self {
582 inner,
583 maximum_attempts,
584 }
585 }
586}
587
588impl<P> RetryPolicy for LimitedAttemptCount<P>
589where
590 P: RetryPolicy,
591{
592 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult {
593 match self.inner.on_error(state, error) {
594 RetryResult::Permanent(e) => RetryResult::Permanent(e),
595 RetryResult::Exhausted(e) => RetryResult::Exhausted(e),
596 RetryResult::Continue(e) => {
597 if state.attempt_count >= self.maximum_attempts {
598 RetryResult::Exhausted(e)
599 } else {
600 RetryResult::Continue(e)
601 }
602 }
603 }
604 }
605
606 fn on_throttle(&self, state: &RetryState, error: Error) -> ThrottleResult {
607 assert!(state.attempt_count < self.maximum_attempts);
610 self.inner.on_throttle(state, error)
611 }
612
613 fn remaining_time(&self, state: &RetryState) -> Option<Duration> {
614 self.inner.remaining_time(state)
615 }
616}
617
618#[cfg(test)]
619pub(crate) mod tests {
620 use super::*;
621 use http::HeaderMap;
622 use std::error::Error as StdError;
623 use std::time::Instant;
624
625 #[test]
627 fn retry_policy_arg() {
628 let policy = LimitedAttemptCount::new(3);
629 let _ = RetryPolicyArg::from(policy);
630
631 let policy: Arc<dyn RetryPolicy> = Arc::new(LimitedAttemptCount::new(3));
632 let _ = RetryPolicyArg::from(policy);
633 }
634
635 #[test]
636 fn aip194_strict() {
637 let p = Aip194Strict;
638
639 let now = Instant::now();
640 assert!(
641 p.on_error(&idempotent_state(now), unavailable())
642 .is_continue()
643 );
644 assert!(
645 p.on_error(&non_idempotent_state(now), unavailable())
646 .is_permanent()
647 );
648 assert!(matches!(
649 p.on_throttle(&idempotent_state(now), unavailable()),
650 ThrottleResult::Continue(_)
651 ));
652
653 assert!(
654 p.on_error(&idempotent_state(now), unknown_and_503())
655 .is_continue()
656 );
657 assert!(
658 p.on_error(&non_idempotent_state(now), unknown_and_503())
659 .is_permanent()
660 );
661 assert!(matches!(
662 p.on_throttle(&idempotent_state(now), unknown_and_503()),
663 ThrottleResult::Continue(_)
664 ));
665
666 assert!(
667 p.on_error(&idempotent_state(now), permission_denied())
668 .is_permanent()
669 );
670 assert!(
671 p.on_error(&non_idempotent_state(now), permission_denied())
672 .is_permanent()
673 );
674
675 assert!(
676 p.on_error(&idempotent_state(now), http_unavailable())
677 .is_continue()
678 );
679 assert!(
680 p.on_error(&non_idempotent_state(now), http_unavailable())
681 .is_permanent()
682 );
683 assert!(matches!(
684 p.on_throttle(&idempotent_state(now), http_unavailable()),
685 ThrottleResult::Continue(_)
686 ));
687
688 assert!(
689 p.on_error(&idempotent_state(now), http_permission_denied())
690 .is_permanent()
691 );
692 assert!(
693 p.on_error(&non_idempotent_state(now), http_permission_denied())
694 .is_permanent()
695 );
696
697 assert!(
698 p.on_error(&idempotent_state(now), Error::io("err".to_string()))
699 .is_continue()
700 );
701 assert!(
702 p.on_error(&non_idempotent_state(now), Error::io("err".to_string()))
703 .is_permanent()
704 );
705
706 assert!(
707 p.on_error(&idempotent_state(now), pre_rpc_transient())
708 .is_continue()
709 );
710 assert!(
711 p.on_error(&non_idempotent_state(now), pre_rpc_transient())
712 .is_continue()
713 );
714
715 assert!(
716 p.on_error(&idempotent_state(now), Error::ser("err"))
717 .is_permanent()
718 );
719 assert!(
720 p.on_error(&non_idempotent_state(now), Error::ser("err"))
721 .is_permanent()
722 );
723 assert!(
724 p.on_error(&idempotent_state(now), Error::deser("err"))
725 .is_permanent()
726 );
727 assert!(
728 p.on_error(&non_idempotent_state(now), Error::deser("err"))
729 .is_permanent()
730 );
731
732 assert!(
733 p.remaining_time(&idempotent_state(now)).is_none(),
734 "p={p:?}, now={now:?}"
735 );
736 }
737
738 #[test]
739 fn always_retry() {
740 let p = AlwaysRetry;
741
742 let now = Instant::now();
743 assert!(
744 p.remaining_time(&idempotent_state(now)).is_none(),
745 "p={p:?}, now={now:?}"
746 );
747 assert!(
748 p.on_error(&idempotent_state(now), http_unavailable())
749 .is_continue()
750 );
751 assert!(
752 p.on_error(&non_idempotent_state(now), http_unavailable())
753 .is_continue()
754 );
755 assert!(matches!(
756 p.on_throttle(&idempotent_state(now), http_unavailable()),
757 ThrottleResult::Continue(_)
758 ));
759
760 assert!(
761 p.on_error(&idempotent_state(now), unavailable())
762 .is_continue()
763 );
764 assert!(
765 p.on_error(&non_idempotent_state(now), unavailable())
766 .is_continue()
767 );
768 }
769
770 #[test_case::test_case(true, Error::io("err"))]
771 #[test_case::test_case(true, pre_rpc_transient())]
772 #[test_case::test_case(true, Error::ser("err"))]
773 #[test_case::test_case(false, Error::io("err"))]
774 #[test_case::test_case(false, pre_rpc_transient())]
775 #[test_case::test_case(false, Error::ser("err"))]
776 fn always_retry_error_kind(idempotent: bool, error: Error) {
777 let p = AlwaysRetry;
778 let now = Instant::now();
779 let state = if idempotent {
780 idempotent_state(now)
781 } else {
782 non_idempotent_state(now)
783 };
784 assert!(p.on_error(&state, error).is_continue());
785 }
786
787 #[test]
788 fn never_retry() {
789 let p = NeverRetry;
790
791 let now = Instant::now();
792 assert!(
793 p.remaining_time(&idempotent_state(now)).is_none(),
794 "p={p:?}, now={now:?}"
795 );
796 assert!(
797 p.on_error(&idempotent_state(now), http_unavailable())
798 .is_exhausted()
799 );
800 assert!(
801 p.on_error(&non_idempotent_state(now), http_unavailable())
802 .is_exhausted()
803 );
804 assert!(matches!(
805 p.on_throttle(&idempotent_state(now), http_unavailable()),
806 ThrottleResult::Continue(_)
807 ));
808
809 assert!(
810 p.on_error(&idempotent_state(now), unavailable())
811 .is_exhausted()
812 );
813 assert!(
814 p.on_error(&non_idempotent_state(now), unavailable())
815 .is_exhausted()
816 );
817
818 assert!(
819 p.on_error(&idempotent_state(now), http_permission_denied())
820 .is_exhausted()
821 );
822 assert!(
823 p.on_error(&non_idempotent_state(now), http_permission_denied())
824 .is_exhausted()
825 );
826 }
827
828 #[test_case::test_case(true, Error::io("err"))]
829 #[test_case::test_case(true, pre_rpc_transient())]
830 #[test_case::test_case(true, Error::ser("err"))]
831 #[test_case::test_case(false, Error::io("err"))]
832 #[test_case::test_case(false, pre_rpc_transient())]
833 #[test_case::test_case(false, Error::ser("err"))]
834 fn never_retry_error_kind(idempotent: bool, error: Error) {
835 let p = NeverRetry;
836 let now = Instant::now();
837 let state = if idempotent {
838 idempotent_state(now)
839 } else {
840 non_idempotent_state(now)
841 };
842 assert!(p.on_error(&state, error).is_exhausted());
843 }
844
845 fn pre_rpc_transient() -> Error {
846 use crate::error::CredentialsError;
847 Error::authentication(CredentialsError::from_msg(true, "err"))
848 }
849
850 fn http_unavailable() -> Error {
851 Error::http(
852 503_u16,
853 HeaderMap::new(),
854 bytes::Bytes::from_owner("SERVICE UNAVAILABLE".to_string()),
855 )
856 }
857
858 fn http_permission_denied() -> Error {
859 Error::http(
860 403_u16,
861 HeaderMap::new(),
862 bytes::Bytes::from_owner("PERMISSION DENIED".to_string()),
863 )
864 }
865
866 fn unavailable() -> Error {
867 use crate::error::rpc::Code;
868 let status = crate::error::rpc::Status::default()
869 .set_code(Code::Unavailable)
870 .set_message("UNAVAILABLE");
871 Error::service(status)
872 }
873
874 fn unknown_and_503() -> Error {
875 use crate::error::rpc::Code;
876 let status = crate::error::rpc::Status::default()
877 .set_code(Code::Unknown)
878 .set_message("UNAVAILABLE");
879 Error::service_full(status, Some(503), None, Some("source error".into()))
880 }
881
882 fn permission_denied() -> Error {
883 use crate::error::rpc::Code;
884 let status = crate::error::rpc::Status::default()
885 .set_code(Code::PermissionDenied)
886 .set_message("PERMISSION_DENIED");
887 Error::service(status)
888 }
889
890 mockall::mock! {
891 #[derive(Debug)]
892 pub(crate) Policy {}
893 impl RetryPolicy for Policy {
894 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult;
895 fn on_throttle(&self, state: &RetryState, error: Error) -> ThrottleResult;
896 fn remaining_time(&self, state: &RetryState) -> Option<Duration>;
897 }
898 }
899
900 #[test]
901 fn limited_elapsed_time_error() {
902 let limit = Duration::from_secs(123) + Duration::from_millis(567);
903 let err = LimitedElapsedTimeError::new(limit, unavailable());
904 assert_eq!(err.maximum_duration(), limit);
905 let fmt = err.to_string();
906 assert!(fmt.contains("123.567s"), "display={fmt}, debug={err:?}");
907 assert!(err.source().is_some(), "{err:?}");
908 }
909
910 #[test]
911 fn test_limited_time_forwards() {
912 let mut mock = MockPolicy::new();
913 mock.expect_on_error()
914 .times(1..)
915 .returning(|_, e| RetryResult::Continue(e));
916 mock.expect_on_throttle()
917 .times(1..)
918 .returning(|_, e| ThrottleResult::Continue(e));
919 mock.expect_remaining_time().times(1).returning(|_| None);
920
921 let now = Instant::now();
922 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
923 let rf = policy.on_error(&idempotent_state(now), transient_error());
924 assert!(rf.is_continue());
925
926 let rt = policy.remaining_time(&idempotent_state(now));
927 assert!(rt.is_some(), "policy={policy:?}, now={now:?}");
928
929 let e = policy.on_throttle(&idempotent_state(now), transient_error());
930 assert!(matches!(e, ThrottleResult::Continue(_)));
931 }
932
933 #[test]
934 fn test_limited_time_on_throttle_continue() {
935 let mut mock = MockPolicy::new();
936 mock.expect_on_throttle()
937 .times(1..)
938 .returning(|_, e| ThrottleResult::Continue(e));
939
940 let now = Instant::now();
941 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
942
943 let rf = policy.on_throttle(
945 &idempotent_state(now - Duration::from_secs(50)),
946 unavailable(),
947 );
948 assert!(matches!(rf, ThrottleResult::Continue(_)), "{rf:?}");
949
950 let rf = policy.on_throttle(
952 &idempotent_state(now - Duration::from_secs(70)),
953 unavailable(),
954 );
955 assert!(matches!(rf, ThrottleResult::Exhausted(_)), "{rf:?}");
956 }
957
958 #[test]
959 fn test_limited_time_on_throttle_exhausted() {
960 let mut mock = MockPolicy::new();
961 mock.expect_on_throttle()
962 .times(1..)
963 .returning(|_, e| ThrottleResult::Exhausted(e));
964
965 let now = Instant::now();
966 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
967
968 let rf = policy.on_throttle(
970 &idempotent_state(now - Duration::from_secs(50)),
971 unavailable(),
972 );
973 assert!(matches!(rf, ThrottleResult::Exhausted(_)), "{rf:?}");
974 }
975
976 #[test]
977 fn test_limited_time_inner_continues() {
978 let mut mock = MockPolicy::new();
979 mock.expect_on_error()
980 .times(1..)
981 .returning(|_, e| RetryResult::Continue(e));
982
983 let now = Instant::now();
984 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
985 let rf = policy.on_error(
986 &idempotent_state(now - Duration::from_secs(10)),
987 transient_error(),
988 );
989 assert!(rf.is_continue());
990
991 let rf = policy.on_error(
992 &idempotent_state(now - Duration::from_secs(70)),
993 transient_error(),
994 );
995 assert!(rf.is_exhausted());
996 }
997
998 #[test]
999 fn test_limited_time_inner_permanent() {
1000 let mut mock = MockPolicy::new();
1001 mock.expect_on_error()
1002 .times(2)
1003 .returning(|_, e| RetryResult::Permanent(e));
1004
1005 let now = Instant::now();
1006 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
1007
1008 let rf = policy.on_error(
1009 &non_idempotent_state(now - Duration::from_secs(10)),
1010 transient_error(),
1011 );
1012 assert!(rf.is_permanent());
1013
1014 let rf = policy.on_error(
1015 &non_idempotent_state(now + Duration::from_secs(10)),
1016 transient_error(),
1017 );
1018 assert!(rf.is_permanent());
1019 }
1020
1021 #[test]
1022 fn test_limited_time_inner_exhausted() {
1023 let mut mock = MockPolicy::new();
1024 mock.expect_on_error()
1025 .times(2)
1026 .returning(|_, e| RetryResult::Exhausted(e));
1027
1028 let now = Instant::now();
1029 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
1030
1031 let rf = policy.on_error(
1032 &non_idempotent_state(now - Duration::from_secs(10)),
1033 transient_error(),
1034 );
1035 assert!(rf.is_exhausted());
1036
1037 let rf = policy.on_error(
1038 &non_idempotent_state(now + Duration::from_secs(10)),
1039 transient_error(),
1040 );
1041 assert!(rf.is_exhausted());
1042 }
1043
1044 #[test]
1045 fn test_limited_time_remaining_inner_longer() {
1046 let mut mock = MockPolicy::new();
1047 mock.expect_remaining_time()
1048 .times(1)
1049 .returning(|_| Some(Duration::from_secs(30)));
1050
1051 let now = Instant::now();
1052 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
1053
1054 let remaining = policy.remaining_time(&idempotent_state(now - Duration::from_secs(55)));
1055 assert!(remaining <= Some(Duration::from_secs(5)), "{remaining:?}");
1056 }
1057
1058 #[test]
1059 fn test_limited_time_remaining_inner_shorter() {
1060 let mut mock = MockPolicy::new();
1061 mock.expect_remaining_time()
1062 .times(1)
1063 .returning(|_| Some(Duration::from_secs(5)));
1064 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
1065
1066 let now = Instant::now();
1067 let remaining = policy.remaining_time(&idempotent_state(now - Duration::from_secs(5)));
1068 assert!(remaining <= Some(Duration::from_secs(10)), "{remaining:?}");
1069 }
1070
1071 #[test]
1072 fn test_limited_time_remaining_inner_is_none() {
1073 let mut mock = MockPolicy::new();
1074 mock.expect_remaining_time().times(1).returning(|_| None);
1075 let policy = LimitedElapsedTime::custom(mock, Duration::from_secs(60));
1076
1077 let now = Instant::now();
1078 let remaining = policy.remaining_time(&idempotent_state(now - Duration::from_secs(50)));
1079 assert!(remaining <= Some(Duration::from_secs(10)), "{remaining:?}");
1080 }
1081
1082 #[test]
1083 fn test_limited_attempt_count_on_error() {
1084 let mut mock = MockPolicy::new();
1085 mock.expect_on_error()
1086 .times(1..)
1087 .returning(|_, e| RetryResult::Continue(e));
1088
1089 let now = Instant::now();
1090 let policy = LimitedAttemptCount::custom(mock, 3);
1091 assert!(
1092 policy
1093 .on_error(
1094 &idempotent_state(now).set_attempt_count(1_u32),
1095 transient_error()
1096 )
1097 .is_continue()
1098 );
1099 assert!(
1100 policy
1101 .on_error(
1102 &idempotent_state(now).set_attempt_count(2_u32),
1103 transient_error()
1104 )
1105 .is_continue()
1106 );
1107 assert!(
1108 policy
1109 .on_error(
1110 &idempotent_state(now).set_attempt_count(3_u32),
1111 transient_error()
1112 )
1113 .is_exhausted()
1114 );
1115 }
1116
1117 #[test]
1118 fn test_limited_attempt_count_on_throttle_continue() {
1119 let mut mock = MockPolicy::new();
1120 mock.expect_on_throttle()
1121 .times(1..)
1122 .returning(|_, e| ThrottleResult::Continue(e));
1123
1124 let now = Instant::now();
1125 let policy = LimitedAttemptCount::custom(mock, 3);
1126 assert!(matches!(
1127 policy.on_throttle(
1128 &idempotent_state(now).set_attempt_count(2_u32),
1129 unavailable()
1130 ),
1131 ThrottleResult::Continue(_)
1132 ));
1133 }
1134
1135 #[test]
1136 fn test_limited_attempt_count_on_throttle_error() {
1137 let mut mock = MockPolicy::new();
1138 mock.expect_on_throttle()
1139 .times(1..)
1140 .returning(|_, e| ThrottleResult::Exhausted(e));
1141
1142 let now = Instant::now();
1143 let policy = LimitedAttemptCount::custom(mock, 3);
1144 assert!(matches!(
1145 policy.on_throttle(&idempotent_state(now), unavailable()),
1146 ThrottleResult::Exhausted(_)
1147 ));
1148 }
1149
1150 #[test]
1151 fn test_limited_attempt_count_remaining_none() {
1152 let mut mock = MockPolicy::new();
1153 mock.expect_remaining_time().times(1).returning(|_| None);
1154 let policy = LimitedAttemptCount::custom(mock, 3);
1155
1156 let now = Instant::now();
1157 assert!(
1158 policy.remaining_time(&idempotent_state(now)).is_none(),
1159 "policy={policy:?} now={now:?}"
1160 );
1161 }
1162
1163 #[test]
1164 fn test_limited_attempt_count_remaining_some() {
1165 let mut mock = MockPolicy::new();
1166 mock.expect_remaining_time()
1167 .times(1)
1168 .returning(|_| Some(Duration::from_secs(123)));
1169 let policy = LimitedAttemptCount::custom(mock, 3);
1170
1171 let now = Instant::now();
1172 assert_eq!(
1173 policy.remaining_time(&idempotent_state(now)),
1174 Some(Duration::from_secs(123))
1175 );
1176 }
1177
1178 #[test]
1179 fn test_limited_attempt_count_inner_permanent() {
1180 let mut mock = MockPolicy::new();
1181 mock.expect_on_error()
1182 .times(2)
1183 .returning(|_, e| RetryResult::Permanent(e));
1184 let policy = LimitedAttemptCount::custom(mock, 2);
1185 let now = Instant::now();
1186
1187 let rf = policy.on_error(&non_idempotent_state(now), transient_error());
1188 assert!(rf.is_permanent());
1189
1190 let rf = policy.on_error(&non_idempotent_state(now), transient_error());
1191 assert!(rf.is_permanent());
1192 }
1193
1194 #[test]
1195 fn test_limited_attempt_count_inner_exhausted() {
1196 let mut mock = MockPolicy::new();
1197 mock.expect_on_error()
1198 .times(2)
1199 .returning(|_, e| RetryResult::Exhausted(e));
1200 let policy = LimitedAttemptCount::custom(mock, 2);
1201 let now = Instant::now();
1202
1203 let rf = policy.on_error(&non_idempotent_state(now), transient_error());
1204 assert!(rf.is_exhausted());
1205
1206 let rf = policy.on_error(&non_idempotent_state(now), transient_error());
1207 assert!(rf.is_exhausted());
1208 }
1209
1210 fn transient_error() -> Error {
1211 use crate::error::rpc::{Code, Status};
1212 Error::service(
1213 Status::default()
1214 .set_code(Code::Unavailable)
1215 .set_message("try-again"),
1216 )
1217 }
1218
1219 pub(crate) fn idempotent_state(now: Instant) -> RetryState {
1220 RetryState::new(true).set_start(now)
1221 }
1222
1223 pub(crate) fn non_idempotent_state(now: Instant) -> RetryState {
1224 RetryState::new(false).set_start(now)
1225 }
1226}