exponentialbackoff.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Utility class to manage the mathematics behind computing an
  16. * exponential backoff model. Given an initial backoff value and a maximum
  17. * backoff value, every call to backoff() will double the value until maximum
  18. * backoff value is reached.
  19. *
  20. */
  21. goog.provide('goog.math.ExponentialBackoff');
  22. goog.require('goog.asserts');
  23. /**
  24. * @struct
  25. * @constructor
  26. *
  27. * @param {number} initialValue The initial backoff value.
  28. * @param {number} maxValue The maximum backoff value.
  29. * @param {number=} opt_randomFactor When set, adds randomness to the backoff
  30. * and decay to avoid a thundering herd problem. Should be a number between
  31. * 0 and 1, where 0 means no randomness and 1 means a factor of 0x to 2x.
  32. * @param {number=} opt_backoffFactor The factor to backoff by. Defaults to 2.
  33. * Should be a number greater than 1.
  34. * @param {number=} opt_decayFactor The factor to decay by. Defaults to 2.
  35. * Should be a number between greater than one.
  36. */
  37. goog.math.ExponentialBackoff = function(
  38. initialValue, maxValue, opt_randomFactor, opt_backoffFactor,
  39. opt_decayFactor) {
  40. goog.asserts.assert(
  41. initialValue > 0, 'Initial value must be greater than zero.');
  42. goog.asserts.assert(
  43. maxValue >= initialValue,
  44. 'Max value should be at least as large as initial value.');
  45. if (goog.isDef(opt_randomFactor)) {
  46. goog.asserts.assert(
  47. opt_randomFactor >= 0 && opt_randomFactor <= 1,
  48. 'Randomness factor should be between 0 and 1.');
  49. }
  50. if (goog.isDef(opt_backoffFactor)) {
  51. goog.asserts.assert(
  52. opt_backoffFactor > 1, 'Backoff factor should be greater than 1');
  53. }
  54. if (goog.isDef(opt_decayFactor)) {
  55. goog.asserts.assert(
  56. opt_decayFactor >= 1, 'Decay factor should be greater than 1');
  57. }
  58. /**
  59. * @type {number}
  60. * @private
  61. */
  62. this.initialValue_ = initialValue;
  63. /**
  64. * @type {number}
  65. * @private
  66. */
  67. this.maxValue_ = maxValue;
  68. /**
  69. * The current backoff value.
  70. * @type {number}
  71. * @private
  72. */
  73. this.currValue_ = initialValue;
  74. /**
  75. * The current backoff value minus the random wait (if there is any).
  76. * @type {number}
  77. * @private
  78. */
  79. this.currBaseValue_ = initialValue;
  80. /**
  81. * The random factor to apply to the backoff value to avoid a thundering herd
  82. * problem. Should be a number between 0 and 1, where 0 means no randomness
  83. * and 1 means a factor of 0x to 2x.
  84. * @type {number}
  85. * @private
  86. */
  87. this.randomFactor_ = opt_randomFactor || 0;
  88. /**
  89. * Factor to backoff by.
  90. * @type {number}
  91. * @private
  92. */
  93. this.backoffFactor_ = opt_backoffFactor || 2;
  94. /**
  95. * Factor to decay by.
  96. * @type {number}
  97. * @private
  98. */
  99. this.decayFactor_ = opt_decayFactor || 2;
  100. };
  101. /**
  102. * The number of backoffs that have happened.
  103. * @type {number}
  104. * @private
  105. */
  106. goog.math.ExponentialBackoff.prototype.currBackoffCount_ = 0;
  107. /**
  108. * The number of decays that have happened.
  109. * @type {number}
  110. * @private
  111. */
  112. goog.math.ExponentialBackoff.prototype.currDecayCount_ = 0;
  113. /**
  114. * Resets the backoff value to its initial value.
  115. */
  116. goog.math.ExponentialBackoff.prototype.reset = function() {
  117. this.currValue_ = this.initialValue_;
  118. this.currBaseValue_ = this.initialValue_;
  119. this.currBackoffCount_ = 0;
  120. this.currDecayCount_ = 0;
  121. };
  122. /**
  123. * @return {number} The current backoff value.
  124. */
  125. goog.math.ExponentialBackoff.prototype.getValue = function() {
  126. return this.currValue_;
  127. };
  128. /**
  129. * @return {number} The number of times this class has backed off.
  130. */
  131. goog.math.ExponentialBackoff.prototype.getBackoffCount = function() {
  132. return this.currBackoffCount_;
  133. };
  134. /**
  135. * @return {number} The number of times this class has decayed.
  136. */
  137. goog.math.ExponentialBackoff.prototype.getDecayCount = function() {
  138. return this.currDecayCount_;
  139. };
  140. /**
  141. * Initiates a backoff.
  142. */
  143. goog.math.ExponentialBackoff.prototype.backoff = function() {
  144. // If we haven't hit the maximum value yet, keep increasing the base value.
  145. this.currBaseValue_ =
  146. Math.min(this.maxValue_, this.currBaseValue_ * this.backoffFactor_);
  147. var randomWait = this.randomFactor_ ?
  148. Math.round(
  149. this.randomFactor_ * (Math.random() - 0.5) * 2 *
  150. this.currBaseValue_) :
  151. 0;
  152. this.currValue_ = Math.min(this.maxValue_, this.currBaseValue_ + randomWait);
  153. this.currBackoffCount_++;
  154. };
  155. /**
  156. * Initiates a decay.
  157. */
  158. goog.math.ExponentialBackoff.prototype.decay = function() {
  159. // If we haven't hit the initial value yet, keep decreasing the base value.
  160. this.currBaseValue_ =
  161. Math.max(this.initialValue_, this.currBaseValue_ / this.decayFactor_);
  162. var randomWait = this.randomFactor_ ?
  163. Math.round(
  164. this.randomFactor_ * (Math.random() - 0.5) * 2 *
  165. this.currBaseValue_) :
  166. 0;
  167. this.currValue_ =
  168. Math.max(this.initialValue_, this.currBaseValue_ + randomWait);
  169. this.currDecayCount_++;
  170. };