Skip to content

Commit a6c8d9c

Browse files
committed
ec2_utils: reduce complexity of retry function
1 parent 04642ec commit a6c8d9c

File tree

1 file changed

+26
-28
lines changed

1 file changed

+26
-28
lines changed

nixops_aws/ec2_utils.py

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
from boto.exception import BotoServerError
1313
from botocore.exceptions import ClientError
1414
from boto.pyami.config import Config
15-
import botocore
16-
from typing import Tuple, TYPE_CHECKING
15+
from typing import Tuple, TYPE_CHECKING, Iterable, Any, Optional
1716

1817
if TYPE_CHECKING:
1918
import mypy_boto3_rds
@@ -138,12 +137,17 @@ def get_access_key_id():
138137
return os.environ.get("EC2_ACCESS_KEY") or os.environ.get("AWS_ACCESS_KEY_ID")
139138

140139

141-
def retry(f, error_codes=[], logger=None):
140+
def retry(
141+
f, error_codes: Optional[Iterable[Any]] = None, logger=None, num_retries: int = 7
142+
):
142143
"""
143144
Retry function f up to 7 times. If error_codes argument is empty list, retry on all EC2 response errors,
144145
otherwise, only on the specified error codes.
145146
"""
146147

148+
if error_codes is None:
149+
error_codes = []
150+
147151
def handle_exception(e):
148152
if hasattr(e, "error_code"):
149153
err_code = e.error_code
@@ -152,8 +156,12 @@ def handle_exception(e):
152156
err_code = e.response["Error"]["Code"]
153157
err_msg = e.response["Error"]["Message"]
154158

155-
if i == num_retries or (error_codes != [] and err_code not in error_codes):
156-
raise e
159+
if err_code == "RequestLimitExceeded":
160+
return False
161+
162+
if error_codes and err_code not in error_codes:
163+
return True
164+
157165
if logger is not None:
158166
logger.log(
159167
"got (possibly transient) EC2 error code '{0}': {1}. retrying...".format(
@@ -162,44 +170,34 @@ def handle_exception(e):
162170
)
163171

164172
def handle_boto3_exception(e):
165-
if i == num_retries:
166-
raise e
167-
elif (
168-
error_codes != []
169-
and getattr(e, "response", {}).get("code") not in error_codes
170-
):
171-
raise e
172-
elif logger is not None:
173+
if error_codes and getattr(e, "response", {}).get("code") not in error_codes:
174+
return True
175+
176+
if logger is not None:
173177
if hasattr(e, "response"):
174178
logger.log(
175179
"got (possibly transient) EC2 error '{}', retrying...".format(
176180
str(e.response["Error"])
177181
)
178182
)
179183

184+
def should_abort(e):
185+
if isinstance(e, (SQSError, EC2ResponseError, BotoServerError)):
186+
return handle_exception(e)
187+
elif isinstance(e, ClientError):
188+
return handle_boto3_exception(e)
189+
180190
i = 0
181-
num_retries = 7
182191
while i <= num_retries:
183192
i += 1
184193
next_sleep = 5 + random.random() * (2 ** i)
185194

186195
try:
187196
return f()
188-
except EC2ResponseError as e:
189-
handle_exception(e)
190-
except SQSError as e:
191-
handle_exception(e)
192-
except ClientError as e:
193-
handle_boto3_exception(e)
194-
except BotoServerError as e:
195-
if e.error_code == "RequestLimitExceeded":
196-
num_retries += 1
197-
else:
198-
handle_exception(e)
199-
except botocore.exceptions.ClientError as e:
200-
handle_exception(e)
201197
except Exception as e:
202-
raise e
198+
if num_retries == i or should_abort(e):
199+
raise e
200+
num_retries += 1
203201

204202
time.sleep(next_sleep)
205203

0 commit comments

Comments
 (0)