Add more backoff functions
This patch introduces more retry backoff function the delivery retry policy into Zaqar. It will work when Zaqar failed to send the notification to the subscriber. Users can define the retry backoff function in the options of subscription or metadata of queue. Change-Id: I0bffd9249f8a0d466ecea1ac36d8adc37b742238 Implement: blueprint support-more-backoff-functions
This commit is contained in:
parent
fa44489ede
commit
ef8631899e
@ -46,7 +46,8 @@ Webhook
|
||||
- 'minimum_delay' and 'maximum_delay' mean delay time in seconds.
|
||||
- 'retry_backoff_function' mean name of retry backoff function.
|
||||
There will be a enum in Zaqar that contain all valid values.
|
||||
At first step, Zaqar only supports one function: 'linear'.
|
||||
Zaqar now supports retry backoff function including 'linear',
|
||||
'arithmetic','geometric' and 'exponential'.
|
||||
- 'minimum_delay_retries' and 'maximum_delay_retries' mean the number of
|
||||
retries with 'minimum_delay' or 'maximum_delay' delay time.
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- Support more retry backoff function in webhook type. It will work
|
||||
when Zaqar failed to send the notification to the subscriber.
|
||||
Users can define the retry backoff function in metadata of queue.
|
||||
There are four retry backoff functions including 'linear',
|
||||
'arithmetic', 'geometric' and 'exponential'.
|
@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import math
|
||||
import time
|
||||
|
||||
import json
|
||||
@ -27,7 +28,33 @@ LOG = logging.getLogger(__name__)
|
||||
def _Linear_function(minimum_delay, maximum_delay, times):
|
||||
return range(minimum_delay, maximum_delay, times)
|
||||
|
||||
RETRY_BACKOFF_FUNCTION_MAP = {'linear': _Linear_function}
|
||||
|
||||
def _Geometric_function(minimum_delay, maximum_delay, times):
|
||||
x_max = int((maximum_delay - minimum_delay) / times)
|
||||
k = math.pow(10, math.log10(maximum_delay/minimum_delay)/(x_max-1))
|
||||
xarray = range(1, x_max+1)
|
||||
return [int(minimum_delay*math.pow(k, a-1)) for a in xarray]
|
||||
|
||||
|
||||
def _Exponential_function(minimum_delay, maximum_delay, times):
|
||||
x_max = int((maximum_delay - minimum_delay) / times)
|
||||
k = math.pow(10, math.log10(maximum_delay/minimum_delay)/(x_max-1))
|
||||
p = minimum_delay/k
|
||||
xarray = range(1, x_max+1)
|
||||
return [int(p*math.pow(k, a)) for a in xarray]
|
||||
|
||||
|
||||
def _Arithmetic_function(minimum_delay, maximum_delay, times):
|
||||
x_max = int((maximum_delay - minimum_delay) / times)
|
||||
d = 2.0 * (maximum_delay - minimum_delay) / (x_max * (x_max - 1))
|
||||
xarray = range(1, x_max+1)
|
||||
return [int(minimum_delay+(a-1)*a*d/2) for a in xarray]
|
||||
|
||||
|
||||
RETRY_BACKOFF_FUNCTION_MAP = {'linear': _Linear_function,
|
||||
'arithmetic': _Arithmetic_function,
|
||||
'geometric': _Geometric_function,
|
||||
'exponential': _Exponential_function}
|
||||
|
||||
|
||||
class WebhookTask(object):
|
||||
@ -65,9 +92,8 @@ class WebhookTask(object):
|
||||
time.sleep(retry_policy.get('minimum_delay', consts.MINIMUM_DELAY))
|
||||
if self._post_request_success(subscriber, data, headers):
|
||||
return
|
||||
# Backoff Phase: Linear retry
|
||||
# TODO(wanghao): Now we only support the linear function, we should
|
||||
# support more in Queens.
|
||||
# Now we support linear,arithmetic,
|
||||
# exponential and geometric retry backoff function.
|
||||
retry_function = retry_policy.get('retry_backoff_function', 'linear')
|
||||
backoff_function = RETRY_BACKOFF_FUNCTION_MAP[retry_function]
|
||||
for i in backoff_function(retry_policy.get('minimum_delay',
|
||||
@ -75,8 +101,8 @@ class WebhookTask(object):
|
||||
retry_policy.get('maximum_delay',
|
||||
consts.MAXIMUM_DELAY),
|
||||
consts.LINEAR_INTERVAL):
|
||||
LOG.debug('Retry with retry_backoff_function, sleep: %s seconds',
|
||||
i)
|
||||
LOG.debug('Retry with function:%s, sleep: %s seconds',
|
||||
retry_function, i)
|
||||
time.sleep(i)
|
||||
if self._post_request_success(subscriber, data, headers):
|
||||
return
|
||||
|
@ -21,6 +21,7 @@ import mock
|
||||
|
||||
from zaqar.common import urls
|
||||
from zaqar.notification import notifier
|
||||
from zaqar.notification.tasks import webhook
|
||||
from zaqar import tests as testing
|
||||
|
||||
|
||||
@ -433,3 +434,16 @@ class NotifierTest(testing.TestBase):
|
||||
@ddt.data(False, True)
|
||||
def test_send_confirm_notification_with_email(self, is_unsub):
|
||||
self._send_confirm_notification_with_email(is_unsubscribed=is_unsub)
|
||||
|
||||
def test_webhook_backoff_function(self):
|
||||
expect = [10, 12, 14, 18, 22, 27, 33, 40, 49, 60]
|
||||
sec = webhook._Exponential_function(10, 60, 5)
|
||||
self.assertEqual(expect, sec)
|
||||
|
||||
expect = [20, 22, 25, 29, 33, 37, 42, 48, 54, 62, 70, 80]
|
||||
sec = webhook._Geometric_function(20, 80, 5)
|
||||
self.assertEqual(expect, sec)
|
||||
|
||||
expect = [30, 30, 32, 34, 37, 41, 46, 51, 57, 64, 72, 80, 90, 100]
|
||||
sec = webhook._Arithmetic_function(30, 100, 5)
|
||||
self.assertEqual(expect, sec)
|
||||
|
@ -21,8 +21,10 @@ from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
from zaqar.common import consts
|
||||
from zaqar.i18n import _
|
||||
|
||||
|
||||
MIN_MESSAGE_TTL = 60
|
||||
MIN_CLAIM_TTL = 60
|
||||
MIN_CLAIM_GRACE = 60
|
||||
@ -249,11 +251,11 @@ class Validator(object):
|
||||
if retry_value and not isinstance(retry_value, str):
|
||||
msg = _('retry_backoff_function must be a string.')
|
||||
raise ValidationFailed(msg)
|
||||
# TODO(wanghao): Now we only support linear function.
|
||||
# This will be removed after we support more functions.
|
||||
if retry_value and retry_value != 'linear':
|
||||
msg = _('retry_backoff_function only supports linear '
|
||||
'now.')
|
||||
# Now we support linear, arithmetic, exponential
|
||||
# and geometric retry backoff function.
|
||||
fun = {'linear', 'arithmetic', 'exponential', 'geometric'}
|
||||
if retry_value and retry_value not in fun:
|
||||
msg = _('invalid retry_backoff_function.')
|
||||
raise ValidationFailed(msg)
|
||||
elif key == 'ignore_subscription_override':
|
||||
if retry_value and not isinstance(retry_value, bool):
|
||||
@ -264,6 +266,16 @@ class Validator(object):
|
||||
if retry_value and not isinstance(retry_value, int):
|
||||
msg = _('Retry policy: %s must be a integer.') % key
|
||||
raise ValidationFailed(msg)
|
||||
min_delay = retry_policy.get('minimum_delay',
|
||||
consts.MINIMUM_DELAY)
|
||||
max_delay = retry_policy.get('maximum_delay',
|
||||
consts.MAXIMUM_DELAY)
|
||||
if max_delay < min_delay:
|
||||
msg = _('minimum_delay must less than maximum_delay.')
|
||||
raise ValidationFailed(msg)
|
||||
if ((max_delay - min_delay) < 2*consts.LINEAR_INTERVAL):
|
||||
msg = _('invalid minimum_delay and maximum_delay.')
|
||||
raise ValidationFailed(msg)
|
||||
|
||||
def queue_patching(self, request, changes):
|
||||
washed_changes = []
|
||||
|
Loading…
Reference in New Issue
Block a user