diff options
Diffstat (limited to 'Firestore/core/src/firebase/firestore/remote/exponential_backoff.h')
-rw-r--r-- | Firestore/core/src/firebase/firestore/remote/exponential_backoff.h | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/Firestore/core/src/firebase/firestore/remote/exponential_backoff.h b/Firestore/core/src/firebase/firestore/remote/exponential_backoff.h new file mode 100644 index 0000000..8836e7e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/remote/exponential_backoff.h @@ -0,0 +1,116 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_EXPONENTIAL_BACKOFF_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_EXPONENTIAL_BACKOFF_H_ + +#include <chrono> // NOLINT(build/c++11) + +#include "Firestore/core/src/firebase/firestore/util/async_queue.h" +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" + +namespace firebase { +namespace firestore { +namespace remote { + +/** + * + * A helper for running delayed operations following an exponential backoff + * curve between attempts. + * + * The first attempt will be done immediately. After that, each retry will + * have a delay that is made up of a "base" delay which follows the + * exponential backoff curve, and a +/- <=50% "jitter" that is calculated and + * added to the base delay. This prevents clients from accidentally + * synchronizing their delays causing spikes of load to the backend. + * + */ +class ExponentialBackoff { + public: + /** + * @param queue The queue to run operations on. + * @param timer_id The id to use when scheduling backoff operations on the + * queue. + * @param backoff_factor The multiplier to use to determine the extended base + * delay after each attempt. + * @param initial_delay The initial delay (used as the base delay on the first + * retry attempt, that is, the second attempt). Note that jitter will + * still be applied, so the actual delay could be as little as + * `0.5*initial_delay`. + * @param max_delay The maximum base delay after which no further backoff is + * performed. Note that jitter will still be applied, so the actual delay + * could be as much as `1.5*max_delay`. + */ + ExponentialBackoff(util::AsyncQueue* queue, + util::TimerId timer_id, + double backoff_factor, + util::AsyncQueue::Milliseconds initial_delay, + util::AsyncQueue::Milliseconds max_delay); + + /** + * Resets the backoff delay. + * + * The very next `backoffAndRun` will have no delay. If it is called again + * (i.e. due to an error), `initial_delay` (plus jitter) will be used, and + * subsequent ones will increase according to the `backoff_factor`. + */ + void Reset() { + current_base_ = Milliseconds{0}; + } + + /** + * Resets the backoff to the maximum delay (e.g. for use after + * a RESOURCE_EXHAUSTED error). + */ + void ResetToMax() { + current_base_ = max_delay_; + } + + /** + * Waits for `current_base` seconds (which may be zero), increases the delay + * and runs the specified operation. If there was a pending operation waiting + * to be run already, it will be canceled. + */ + void BackoffAndRun(util::AsyncQueue::Operation&& operation); + + /** Cancels any pending backoff operation scheduled via `BackoffAndRun`. */ + void Cancel() { + delayed_operation_.Cancel(); + } + + private: + using Milliseconds = util::AsyncQueue::Milliseconds; + + // Returns a random value in the range [-current_base_/2, current_base_/2]. + Milliseconds GetDelayWithJitter(); + Milliseconds ClampDelay(Milliseconds delay) const; + + util::AsyncQueue* const queue_; + const util::TimerId timer_id_; + util::DelayedOperation delayed_operation_; + + const double backoff_factor_; + Milliseconds current_base_{0}; + const Milliseconds initial_delay_; + const Milliseconds max_delay_; + util::SecureRandom secure_random_; +}; + +} // namespace remote +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_EXPONENTIAL_BACKOFF_H_ |