Show More
@@ -39,7 +39,7 b' from rhodecode.lib.ext_json import json' | |||||
39 | from rhodecode.lib.auth import ( |
|
39 | from rhodecode.lib.auth import ( | |
40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, |
|
40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, | |
41 | NotAnonymous, CSRFRequired) |
|
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 | from rhodecode.lib.vcs.backends.base import ( |
|
43 | from rhodecode.lib.vcs.backends.base import ( | |
44 | EmptyCommit, UpdateFailureReason, unicode_to_reference) |
|
44 | EmptyCommit, UpdateFailureReason, unicode_to_reference) | |
45 | from rhodecode.lib.vcs.exceptions import ( |
|
45 | from rhodecode.lib.vcs.exceptions import ( | |
@@ -1353,9 +1353,13 b' class RepoPullRequestsView(RepoAppView, ' | |||||
1353 | def _update_commits(self, c, pull_request): |
|
1353 | def _update_commits(self, c, pull_request): | |
1354 | _ = self.request.translate |
|
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 | with pull_request.set_state(PullRequest.STATE_UPDATING): |
|
1361 | with pull_request.set_state(PullRequest.STATE_UPDATING): | |
1357 | resp = PullRequestModel().update_commits( |
|
1362 | resp = commits_update() # retry x3 | |
1358 | pull_request, self._rhodecode_db_user) |
|
|||
1359 |
|
1363 | |||
1360 | if resp.executed: |
|
1364 | if resp.executed: | |
1361 |
|
1365 |
@@ -35,7 +35,7 b' import urllib' | |||||
35 | import urlobject |
|
35 | import urlobject | |
36 | import uuid |
|
36 | import uuid | |
37 | import getpass |
|
37 | import getpass | |
38 | from functools import update_wrapper, partial |
|
38 | from functools import update_wrapper, partial, wraps | |
39 |
|
39 | |||
40 | import pygments.lexers |
|
40 | import pygments.lexers | |
41 | import sqlalchemy |
|
41 | import sqlalchemy | |
@@ -1038,16 +1038,17 b' class CachedProperty(object):' | |||||
1038 | """ |
|
1038 | """ | |
1039 | Lazy Attributes. With option to invalidate the cache by running a method |
|
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 | # at this point calling foo.heavy_func() will be re-computed |
|
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 | def _invalidate_prop_cache(self, inst, name): |
|
1074 | def _invalidate_prop_cache(self, inst, name): | |
1074 | inst.__dict__.pop(name, None) |
|
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 | return self |
|
4062 | return self | |
4063 |
|
4063 | |||
4064 | def __exit__(self, exc_type, exc_val, exc_tb): |
|
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 | log.error(traceback.format_exc(exc_tb)) |
|
4066 | log.error(traceback.format_exc(exc_tb)) | |
4067 | return None |
|
4067 | return None | |
4068 |
|
4068 |
General Comments 0
You need to be logged in to leave comments.
Login now