diff options
author | Clayton Walker <cwalker@sofi.org> | 2022-03-02 11:34:05 -0700 |
---|---|---|
committer | Nejc Habjan <nejc.habjan@siemens.com> | 2022-04-04 23:30:14 +0200 |
commit | c3ef1b5c1eaf1348a18d753dbf7bda3c129e3262 (patch) | |
tree | feba26855cc7d26751415b96da1a17cdcbf09839 | |
parent | 3b49e4d61e6f360f1c787aa048edf584aec55278 (diff) | |
download | gitlab-c3ef1b5c1eaf1348a18d753dbf7bda3c129e3262.tar.gz |
fix: add 52x range to retry transient failures and tests
-rw-r--r-- | gitlab/client.py | 9 | ||||
-rw-r--r-- | tests/unit/test_gitlab_http_methods.py | 98 |
2 files changed, 103 insertions, 4 deletions
diff --git a/gitlab/client.py b/gitlab/client.py index 75765f7..c6e9b96 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -35,6 +35,8 @@ REDIRECT_MSG = ( "{source!r} to {target!r}" ) +RETRYABLE_TRANSIENT_ERROR_CODES = [500, 502, 503, 504] + list(range(520, 531)) + class Gitlab: """Represents a GitLab server connection. @@ -694,9 +696,9 @@ class Gitlab: ) except requests.ConnectionError: if retry_transient_errors and ( - max_retries == -1 or cur_retries < max_retries + max_retries == -1 or cur_retries < max_retries ): - wait_time = 2 ** cur_retries * 0.1 + wait_time = 2**cur_retries * 0.1 cur_retries += 1 time.sleep(wait_time) continue @@ -712,7 +714,8 @@ class Gitlab: "retry_transient_errors", self.retry_transient_errors ) if (429 == result.status_code and obey_rate_limit) or ( - result.status_code in [500, 502, 503, 504] and retry_transient_errors + result.status_code in RETRYABLE_TRANSIENT_ERROR_CODES + and retry_transient_errors ): # Response headers documentation: # https://docs.gitlab.com/ee/user/admin_area/settings/user_and_ip_rate_limits.html#response-headers diff --git a/tests/unit/test_gitlab_http_methods.py b/tests/unit/test_gitlab_http_methods.py index a65b53e..ed96215 100644 --- a/tests/unit/test_gitlab_http_methods.py +++ b/tests/unit/test_gitlab_http_methods.py @@ -3,6 +3,7 @@ import requests import responses from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectError +from gitlab.client import RETRYABLE_TRANSIENT_ERROR_CODES from tests.unit import helpers MATCH_EMPTY_QUERY_PARAMS = [responses.matchers.query_param_matcher({})] @@ -51,7 +52,7 @@ def test_http_request_404(gl): @responses.activate -@pytest.mark.parametrize("status_code", [500, 502, 503, 504]) +@pytest.mark.parametrize("status_code", RETRYABLE_TRANSIENT_ERROR_CODES) def test_http_request_with_only_failures(gl, status_code): url = "http://localhost/api/v4/projects" responses.add( @@ -98,6 +99,37 @@ def test_http_request_with_retry_on_method_for_transient_failures(gl): @responses.activate +def test_http_request_with_retry_on_method_for_transient_network_failures(gl): + call_count = 0 + calls_before_success = 3 + + url = "http://localhost/api/v4/projects" + + def request_callback(request): + nonlocal call_count + call_count += 1 + status_code = 200 + headers = {} + body = "[]" + + if call_count >= calls_before_success: + return (status_code, headers, body) + raise requests.ConnectionError("Connection aborted.") + + responses.add_callback( + method=responses.GET, + url=url, + callback=request_callback, + content_type="application/json", + ) + + http_r = gl.http_request("get", "/projects", retry_transient_errors=True) + + assert http_r.status_code == 200 + assert len(responses.calls) == calls_before_success + + +@responses.activate def test_http_request_with_retry_on_class_for_transient_failures(gl_retry): call_count = 0 calls_before_success = 3 @@ -127,6 +159,37 @@ def test_http_request_with_retry_on_class_for_transient_failures(gl_retry): @responses.activate +def test_http_request_with_retry_on_class_for_transient_network_failures(gl_retry): + call_count = 0 + calls_before_success = 3 + + url = "http://localhost/api/v4/projects" + + def request_callback(request: requests.models.PreparedRequest): + nonlocal call_count + call_count += 1 + status_code = 200 + headers = {} + body = "[]" + + if call_count >= calls_before_success: + return (status_code, headers, body) + raise requests.ConnectionError("Connection aborted.") + + responses.add_callback( + method=responses.GET, + url=url, + callback=request_callback, + content_type="application/json", + ) + + http_r = gl_retry.http_request("get", "/projects", retry_transient_errors=True) + + assert http_r.status_code == 200 + assert len(responses.calls) == calls_before_success + + +@responses.activate def test_http_request_with_retry_on_class_and_method_for_transient_failures(gl_retry): call_count = 0 calls_before_success = 3 @@ -155,6 +218,39 @@ def test_http_request_with_retry_on_class_and_method_for_transient_failures(gl_r assert len(responses.calls) == 1 +@responses.activate +def test_http_request_with_retry_on_class_and_method_for_transient_network_failures( + gl_retry, +): + call_count = 0 + calls_before_success = 3 + + url = "http://localhost/api/v4/projects" + + def request_callback(request): + nonlocal call_count + call_count += 1 + status_code = 200 + headers = {} + body = "[]" + + if call_count >= calls_before_success: + return (status_code, headers, body) + raise requests.ConnectionError("Connection aborted.") + + responses.add_callback( + method=responses.GET, + url=url, + callback=request_callback, + content_type="application/json", + ) + + with pytest.raises(requests.ConnectionError): + gl_retry.http_request("get", "/projects", retry_transient_errors=False) + + assert len(responses.calls) == 1 + + def create_redirect_response( *, response: requests.models.Response, http_method: str, api_path: str ) -> requests.models.Response: |