##// END OF EJS Templates
pull-requests: added retry mechanism for updating pull requests.
super-admin -
r4696:7a5e2fc4 stable
parent child Browse files
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