Show More
@@ -39,7 +39,7 b' from rhodecode.lib.ext_json import json' | |||
|
39 | 39 | from rhodecode.lib.auth import ( |
|
40 | 40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, |
|
41 | 41 | NotAnonymous, CSRFRequired) |
|
42 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist | |
|
42 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int, aslist, retry | |
|
43 | 43 | from rhodecode.lib.vcs.backends.base import ( |
|
44 | 44 | EmptyCommit, UpdateFailureReason, unicode_to_reference) |
|
45 | 45 | from rhodecode.lib.vcs.exceptions import ( |
@@ -1353,9 +1353,13 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
1353 | 1353 | def _update_commits(self, c, pull_request): |
|
1354 | 1354 | _ = self.request.translate |
|
1355 | 1355 | |
|
1356 | @retry(exception=Exception, n_tries=3) | |
|
1357 | def commits_update(): | |
|
1358 | return PullRequestModel().update_commits( | |
|
1359 | pull_request, self._rhodecode_db_user) | |
|
1360 | ||
|
1356 | 1361 | with pull_request.set_state(PullRequest.STATE_UPDATING): |
|
1357 | resp = PullRequestModel().update_commits( | |
|
1358 | pull_request, self._rhodecode_db_user) | |
|
1362 | resp = commits_update() # retry x3 | |
|
1359 | 1363 | |
|
1360 | 1364 | if resp.executed: |
|
1361 | 1365 |
@@ -35,7 +35,7 b' import urllib' | |||
|
35 | 35 | import urlobject |
|
36 | 36 | import uuid |
|
37 | 37 | import getpass |
|
38 | from functools import update_wrapper, partial | |
|
38 | from functools import update_wrapper, partial, wraps | |
|
39 | 39 | |
|
40 | 40 | import pygments.lexers |
|
41 | 41 | import sqlalchemy |
@@ -1038,16 +1038,17 b' class CachedProperty(object):' | |||
|
1038 | 1038 | """ |
|
1039 | 1039 | Lazy Attributes. With option to invalidate the cache by running a method |
|
1040 | 1040 | |
|
1041 | class Foo(): | |
|
1041 | >>> class Foo(object): | |
|
1042 | ... | |
|
1043 | ... @CachedProperty | |
|
1044 | ... def heavy_func(self): | |
|
1045 | ... return 'super-calculation' | |
|
1046 | ... | |
|
1047 | ... foo = Foo() | |
|
1048 | ... foo.heavy_func() # first computation | |
|
1049 | ... foo.heavy_func() # fetch from cache | |
|
1050 | ... foo._invalidate_prop_cache('heavy_func') | |
|
1042 | 1051 | |
|
1043 | @CachedProperty | |
|
1044 | def heavy_func(): | |
|
1045 | return 'super-calculation' | |
|
1046 | ||
|
1047 | foo = Foo() | |
|
1048 | foo.heavy_func() # first computions | |
|
1049 | foo.heavy_func() # fetch from cache | |
|
1050 | foo._invalidate_prop_cache('heavy_func') | |
|
1051 | 1052 | # at this point calling foo.heavy_func() will be re-computed |
|
1052 | 1053 | """ |
|
1053 | 1054 | |
@@ -1072,3 +1073,76 b' class CachedProperty(object):' | |||
|
1072 | 1073 | |
|
1073 | 1074 | def _invalidate_prop_cache(self, inst, name): |
|
1074 | 1075 | inst.__dict__.pop(name, None) |
|
1076 | ||
|
1077 | ||
|
1078 | def retry(func=None, exception=Exception, n_tries=5, delay=5, backoff=1, logger=True): | |
|
1079 | """ | |
|
1080 | Retry decorator with exponential backoff. | |
|
1081 | ||
|
1082 | Parameters | |
|
1083 | ---------- | |
|
1084 | func : typing.Callable, optional | |
|
1085 | Callable on which the decorator is applied, by default None | |
|
1086 | exception : Exception or tuple of Exceptions, optional | |
|
1087 | Exception(s) that invoke retry, by default Exception | |
|
1088 | n_tries : int, optional | |
|
1089 | Number of tries before giving up, by default 5 | |
|
1090 | delay : int, optional | |
|
1091 | Initial delay between retries in seconds, by default 5 | |
|
1092 | backoff : int, optional | |
|
1093 | Backoff multiplier e.g. value of 2 will double the delay, by default 1 | |
|
1094 | logger : bool, optional | |
|
1095 | Option to log or print, by default False | |
|
1096 | ||
|
1097 | Returns | |
|
1098 | ------- | |
|
1099 | typing.Callable | |
|
1100 | Decorated callable that calls itself when exception(s) occur. | |
|
1101 | ||
|
1102 | Examples | |
|
1103 | -------- | |
|
1104 | >>> import random | |
|
1105 | >>> @retry(exception=Exception, n_tries=3) | |
|
1106 | ... def test_random(text): | |
|
1107 | ... x = random.random() | |
|
1108 | ... if x < 0.5: | |
|
1109 | ... raise Exception("Fail") | |
|
1110 | ... else: | |
|
1111 | ... print("Success: ", text) | |
|
1112 | >>> test_random("It works!") | |
|
1113 | """ | |
|
1114 | ||
|
1115 | if func is None: | |
|
1116 | return partial( | |
|
1117 | retry, | |
|
1118 | exception=exception, | |
|
1119 | n_tries=n_tries, | |
|
1120 | delay=delay, | |
|
1121 | backoff=backoff, | |
|
1122 | logger=logger, | |
|
1123 | ) | |
|
1124 | ||
|
1125 | @wraps(func) | |
|
1126 | def wrapper(*args, **kwargs): | |
|
1127 | _n_tries, n_delay = n_tries, delay | |
|
1128 | log = logging.getLogger('rhodecode.retry') | |
|
1129 | ||
|
1130 | while _n_tries > 1: | |
|
1131 | try: | |
|
1132 | return func(*args, **kwargs) | |
|
1133 | except exception as e: | |
|
1134 | e_details = repr(e) | |
|
1135 | msg = "Exception on calling func {func}: {e}, " \ | |
|
1136 | "Retrying in {n_delay} seconds..."\ | |
|
1137 | .format(func=func, e=e_details, n_delay=n_delay) | |
|
1138 | if logger: | |
|
1139 | log.warning(msg) | |
|
1140 | else: | |
|
1141 | print(msg) | |
|
1142 | time.sleep(n_delay) | |
|
1143 | _n_tries -= 1 | |
|
1144 | n_delay *= backoff | |
|
1145 | ||
|
1146 | return func(*args, **kwargs) | |
|
1147 | ||
|
1148 | return wrapper |
@@ -4062,7 +4062,7 b' class _SetState(object):' | |||
|
4062 | 4062 | return self |
|
4063 | 4063 | |
|
4064 | 4064 | def __exit__(self, exc_type, exc_val, exc_tb): |
|
4065 | if exc_val is not None: | |
|
4065 | if exc_val is not None or exc_type is not None: | |
|
4066 | 4066 | log.error(traceback.format_exc(exc_tb)) |
|
4067 | 4067 | return None |
|
4068 | 4068 |
General Comments 0
You need to be logged in to leave comments.
Login now