Chris Masone | 6f10908 | 2012-07-18 14:21:38 -0700 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
Alex Miller | 320dac9 | 2013-01-09 21:18:13 -0800 | [diff] [blame] | 5 | import logging, random, time |
Chris Masone | 6f10908 | 2012-07-18 14:21:38 -0700 | [diff] [blame] | 6 | from autotest_lib.client.common_lib import error |
| 7 | from autotest_lib.frontend.afe.json_rpc import proxy |
| 8 | |
| 9 | |
Alex Miller | 320dac9 | 2013-01-09 21:18:13 -0800 | [diff] [blame] | 10 | def retry(ExceptionToCheck, timeout_min=1, delay_sec=3): |
Chris Masone | 6f10908 | 2012-07-18 14:21:38 -0700 | [diff] [blame] | 11 | """Retry calling the decorated function using a delay with jitter. |
| 12 | |
| 13 | Will raise RPC ValidationError exceptions from the decorated |
| 14 | function without retrying; a malformed RPC isn't going to |
| 15 | magically become good. |
| 16 | |
| 17 | original from: |
| 18 | http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ |
| 19 | |
| 20 | @param ExceptionToCheck: the exception to check. May be a tuple of |
| 21 | exceptions to check. |
| 22 | @param timeout_min: timeout in minutes until giving up. |
| 23 | @param delay_sec: pre-jittered delay between retries in seconds. Actual |
| 24 | delays will be centered around this value, ranging up to |
| 25 | 50% off this midpoint. |
| 26 | """ |
| 27 | def deco_retry(func): |
| 28 | random.seed() |
| 29 | def func_retry(*args, **kwargs): |
| 30 | deadline = time.time() + timeout_min * 60 # convert to seconds. |
| 31 | while time.time() < deadline: |
| 32 | try: |
Alex Miller | 320dac9 | 2013-01-09 21:18:13 -0800 | [diff] [blame] | 33 | return func(*args, **kwargs) |
| 34 | except error.CrosDynamicSuiteException, e: |
| 35 | raise e |
| 36 | except proxy.ValidationError, e: |
| 37 | raise e |
| 38 | except ExceptionToCheck, e: |
| 39 | # 'Jitter' the delay, up to 50% in either direction. |
| 40 | delay = random.uniform(.5 * delay_sec, 1.5 * delay_sec) |
| 41 | logging.warning("%s(%s), Retrying in %f seconds...", |
| 42 | e.__class__, e, delay) |
| 43 | time.sleep(delay) |
Chris Masone | 6f10908 | 2012-07-18 14:21:38 -0700 | [diff] [blame] | 44 | else: |
Alex Miller | 320dac9 | 2013-01-09 21:18:13 -0800 | [diff] [blame] | 45 | # On the last try, run func() and allow exceptions to escape. |
| 46 | return func(*args, **kwargs) |
| 47 | return |
Chris Masone | 6f10908 | 2012-07-18 14:21:38 -0700 | [diff] [blame] | 48 | return func_retry # true decorator |
| 49 | return deco_retry |