diff --git a/google/cloud/spanner/retry_policy.h b/google/cloud/spanner/retry_policy.h index 39edfbe5783e..e1de780aa2d9 100644 --- a/google/cloud/spanner/retry_policy.h +++ b/google/cloud/spanner/retry_policy.h @@ -59,33 +59,270 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END namespace spanner { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -/// The base class for retry policies. -using RetryPolicy = ::google::cloud::internal::TraitBasedRetryPolicy< - spanner_internal::SafeGrpcRetry>; - -/// A retry policy that limits based on time. -using LimitedTimeRetryPolicy = - ::google::cloud::internal::LimitedTimeRetryPolicy< - spanner_internal::SafeGrpcRetry>; - -/// A retry policy that limits the number of times a request can fail. -using LimitedErrorCountRetryPolicy = - google::cloud::internal::LimitedErrorCountRetryPolicy< - spanner_internal::SafeGrpcRetry>; - -/// The base class for transaction rerun policies. -using TransactionRerunPolicy = ::google::cloud::internal::TraitBasedRetryPolicy< - spanner_internal::SafeTransactionRerun>; - -/// A transaction rerun policy that limits the duration of the rerun loop. -using LimitedTimeTransactionRerunPolicy = - google::cloud::internal::LimitedTimeRetryPolicy< - spanner_internal::SafeTransactionRerun>; - -/// A transaction rerun policy that limits the number of failures. -using LimitedErrorCountTransactionRerunPolicy = - google::cloud::internal::LimitedErrorCountRetryPolicy< - spanner_internal::SafeTransactionRerun>; +/// The base class for the Spanner library retry policies. +class RetryPolicy : public google::cloud::RetryPolicy { + public: + /// Creates a new instance of the policy, reset to the initial state. + virtual std::unique_ptr clone() const = 0; +}; + +/** + * A retry policy for the Spanner library based on counting errors. + * + * This policy stops retrying if: + * - An RPC returns a non-transient error. + * - More than a prescribed number of transient failures is detected. + * + * In this class the following status codes are treated as transient errors: + * - [`kUnavailable`](@ref google::cloud::StatusCode) + * - [`kResourceExhausted`](@ref google::cloud::StatusCode) + * - [`kInternal`](@ref google::cloud::StatusCode) if the error message + * indicates a connection reset. + */ +class LimitedErrorCountRetryPolicy : public RetryPolicy { + public: + /** + * Create an instance that tolerates up to @p maximum_failures transient + * errors. + * + * @note Disable the retry loop by providing an instance of this policy with + * @p maximum_failures == 0. + */ + explicit LimitedErrorCountRetryPolicy(int maximum_failures) + : impl_(maximum_failures) {} + + LimitedErrorCountRetryPolicy(LimitedErrorCountRetryPolicy&& rhs) noexcept + : LimitedErrorCountRetryPolicy(rhs.maximum_failures()) {} + LimitedErrorCountRetryPolicy(LimitedErrorCountRetryPolicy const& rhs) noexcept + : LimitedErrorCountRetryPolicy(rhs.maximum_failures()) {} + + int maximum_failures() const { return impl_.maximum_failures(); } + + bool OnFailure(Status const& s) override { return impl_.OnFailure(s); } + bool IsExhausted() const override { return impl_.IsExhausted(); } + bool IsPermanentFailure(Status const& s) const override { + return impl_.IsPermanentFailure(s); + } + std::unique_ptr clone() const override { + return std::make_unique( + impl_.maximum_failures()); + } + + // This is provided only for backwards compatibility. + using BaseType = RetryPolicy; + + private: + google::cloud::internal::LimitedErrorCountRetryPolicy< + spanner_internal::SafeGrpcRetry> + impl_; +}; + +/** + * A retry policy for the Spanner library based on elapsed time. + * + * This policy stops retrying if: + * - An RPC returns a non-transient error. + * - The elapsed time in the retry loop exceeds a prescribed duration. + * + * The following status codes are treated as transient errors: + * - [`kUnavailable`](@ref google::cloud::StatusCode) + * - [`kResourceExhausted`](@ref google::cloud::StatusCode) + * - [`kInternal`](@ref google::cloud::StatusCode) if the error message + * indicates a connection reset. + */ +class LimitedTimeRetryPolicy : public RetryPolicy { + public: + /** + * Constructor given a `std::chrono::duration<>` object. + * + * @tparam DurationRep a placeholder to match the `Rep` tparam for + * @p maximum_duration's type. The semantics of this template parameter + * are documented in `std::chrono::duration<>`. In brief, the underlying + * arithmetic type used to store the number of ticks. For our purposes it + * is simply a formal parameter. + * @tparam DurationPeriod a placeholder to match the `Period` tparam for + * @p maximum_duration's type. The semantics of this template parameter + * are documented in `std::chrono::duration<>`. In brief, the length of + * the tick in seconds, expressed as a `std::ratio<>`. For our purposes it + * is simply a formal parameter. + * @param maximum_duration the maximum time allowed before the policy expires, + * while the application can express this time in any units they desire, + * the class truncates to milliseconds. + * + * @see https://en.cppreference.com/w/cpp/chrono/duration for more details + * about `std::chrono::duration`. + */ + template + explicit LimitedTimeRetryPolicy( + std::chrono::duration maximum_duration) + : impl_(maximum_duration) {} + + LimitedTimeRetryPolicy(LimitedTimeRetryPolicy&& rhs) noexcept + : LimitedTimeRetryPolicy(rhs.maximum_duration()) {} + LimitedTimeRetryPolicy(LimitedTimeRetryPolicy const& rhs) noexcept + : LimitedTimeRetryPolicy(rhs.maximum_duration()) {} + + std::chrono::milliseconds maximum_duration() const { + return impl_.maximum_duration(); + } + + std::unique_ptr clone() const override { + return std::make_unique(impl_.maximum_duration()); + } + bool OnFailure(Status const& s) override { return impl_.OnFailure(s); } + bool IsExhausted() const override { return impl_.IsExhausted(); } + bool IsPermanentFailure(Status const& s) const override { + return impl_.IsPermanentFailure(s); + } + + // This is provided only for backwards compatibility. + using BaseType = RetryPolicy; + + private: + google::cloud::internal::LimitedTimeRetryPolicy< + spanner_internal::SafeGrpcRetry> + impl_; +}; + +/** + * The base class for the Spanner library transaction rerun policies. + * + * The [`Client::Commit()`] functions (there are several overloads) consume a + * callable to create mutations or a list of mutations. `Commit()` creates a + * transaction and applies the mutations. If the transaction fails, an instance + * of this class is used to control whether the transaction will be attempted + * again. + * + * [`Client::Commit()`]: @ref google::cloud::spanner::Client::Commit() + */ +class TransactionRerunPolicy : public google::cloud::RetryPolicy { + public: + /// Creates a new instance of the policy, reset to the initial state. + virtual std::unique_ptr clone() const = 0; +}; + +/** + * A transaction rerun policy based on counting errors. + * + * This policy stops running if: + * - An RPC returns a non-transient error. + * - More than a prescribed number of transient failures is detected. + * + * The following status codes are treated as transient errors: + * - [`kAborted`](@ref google::cloud::StatusCode) + * - [`kNotFound`](@ref google::cloud::StatusCode) when the error message + * indicates the missing (or expired) resource is the spanner session + * associated with the transaction. + */ +class LimitedErrorCountTransactionRerunPolicy : public TransactionRerunPolicy { + public: + /** + * Create an instance that tolerates up to @p maximum_failures transient + * errors. + * + * @note Disable the retry loop by providing an instance of this policy with + * @p maximum_failures == 0. + */ + explicit LimitedErrorCountTransactionRerunPolicy(int maximum_failures) + : impl_(maximum_failures) {} + + LimitedErrorCountTransactionRerunPolicy( + LimitedErrorCountTransactionRerunPolicy&& rhs) noexcept + : LimitedErrorCountTransactionRerunPolicy(rhs.maximum_failures()) {} + LimitedErrorCountTransactionRerunPolicy( + LimitedErrorCountTransactionRerunPolicy const& rhs) noexcept + : LimitedErrorCountTransactionRerunPolicy(rhs.maximum_failures()) {} + + int maximum_failures() const { return impl_.maximum_failures(); } + + bool OnFailure(Status const& s) override { return impl_.OnFailure(s); } + bool IsExhausted() const override { return impl_.IsExhausted(); } + bool IsPermanentFailure(Status const& s) const override { + return impl_.IsPermanentFailure(s); + } + std::unique_ptr clone() const override { + return std::make_unique( + impl_.maximum_failures()); + } + + // This is provided only for backwards compatibility. + using BaseType = TransactionRerunPolicy; + + private: + google::cloud::internal::LimitedErrorCountRetryPolicy< + spanner_internal::SafeTransactionRerun> + impl_; +}; + +/** + * A transaction rerun policy based on elapsed time. + * + * This policy stops retrying if: + * - An RPC returns a non-transient error. + * - The elapsed time in the retry loop exceeds a prescribed duration. + * + * The following status codes are treated as transient errors: + * - [`kAborted`](@ref google::cloud::StatusCode) + * - [`kNotFound`](@ref google::cloud::StatusCode) when the error message + * indicates the missing (or expired) resource is the spanner session + * associated with the transaction. + */ +class LimitedTimeTransactionRerunPolicy : public TransactionRerunPolicy { + public: + /** + * Constructor given a `std::chrono::duration<>` object. + * + * @tparam DurationRep a placeholder to match the `Rep` tparam for + * @p maximum_duration's type. The semantics of this template parameter + * are documented in `std::chrono::duration<>`. In brief, the underlying + * arithmetic type used to store the number of ticks. For our purposes it + * is simply a formal parameter. + * @tparam DurationPeriod a placeholder to match the `Period` tparam for + * @p maximum_duration's type. The semantics of this template parameter + * are documented in `std::chrono::duration<>`. In brief, the length of + * the tick in seconds, expressed as a `std::ratio<>`. For our purposes it + * is simply a formal parameter. + * @param maximum_duration the maximum time allowed before the policy expires, + * while the application can express this time in any units they desire, + * the class truncates to milliseconds. + * + * @see https://en.cppreference.com/w/cpp/chrono/duration for more details + * about `std::chrono::duration`. + */ + template + explicit LimitedTimeTransactionRerunPolicy( + std::chrono::duration maximum_duration) + : impl_(maximum_duration) {} + + LimitedTimeTransactionRerunPolicy( + LimitedTimeTransactionRerunPolicy&& rhs) noexcept + : LimitedTimeTransactionRerunPolicy(rhs.maximum_duration()) {} + LimitedTimeTransactionRerunPolicy( + LimitedTimeTransactionRerunPolicy const& rhs) noexcept + : LimitedTimeTransactionRerunPolicy(rhs.maximum_duration()) {} + + std::chrono::milliseconds maximum_duration() const { + return impl_.maximum_duration(); + } + + bool OnFailure(Status const& s) override { return impl_.OnFailure(s); } + bool IsExhausted() const override { return impl_.IsExhausted(); } + bool IsPermanentFailure(Status const& s) const override { + return impl_.IsPermanentFailure(s); + } + std::unique_ptr clone() const override { + return std::make_unique( + impl_.maximum_duration()); + } + + // This is provided only for backwards compatibility. + using BaseType = RetryPolicy; + + private: + google::cloud::internal::LimitedTimeRetryPolicy< + spanner_internal::SafeTransactionRerun> + impl_; +}; GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace spanner