|
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | 15 | import socket |
16 | | -import time |
17 | 16 | from functools import wraps |
18 | 17 | from typing import Any, Callable, Dict, Optional |
19 | 18 | from urllib.parse import urljoin |
20 | 19 |
|
21 | | -import lightning_cloud |
22 | 20 | import requests |
23 | | -import urllib3 |
24 | | -from lightning_cloud.rest_client import create_swagger_client, GridRestClient |
| 21 | + |
| 22 | +# for backwards compatibility |
| 23 | +from lightning_cloud.rest_client import create_swagger_client, GridRestClient, LightningClient # noqa: F401 |
25 | 24 | from requests import Session |
26 | 25 | from requests.adapters import HTTPAdapter |
27 | 26 | from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout |
@@ -87,7 +86,6 @@ def _find_free_network_port_cloudspace(): |
87 | 86 |
|
88 | 87 | _CONNECTION_RETRY_TOTAL = 2880 |
89 | 88 | _CONNECTION_RETRY_BACKOFF_FACTOR = 0.5 |
90 | | -_DEFAULT_BACKOFF_MAX = 5 * 60 # seconds |
91 | 89 | _DEFAULT_REQUEST_TIMEOUT = 30 # seconds |
92 | 90 |
|
93 | 91 |
|
@@ -119,75 +117,6 @@ def _check_service_url_is_ready(url: str, timeout: float = 5, metadata="") -> bo |
119 | 117 | return False |
120 | 118 |
|
121 | 119 |
|
122 | | -def _get_next_backoff_time(num_retries: int, backoff_value: float = 0.5) -> float: |
123 | | - next_backoff_value = backoff_value * (2 ** (num_retries - 1)) |
124 | | - return min(_DEFAULT_BACKOFF_MAX, next_backoff_value) |
125 | | - |
126 | | - |
127 | | -def _retry_wrapper(self, func: Callable, max_tries: Optional[int] = None) -> Callable: |
128 | | - """Returns the function decorated by a wrapper that retries the call several times if a connection error occurs. |
129 | | -
|
130 | | - The retries follow an exponential backoff. |
131 | | -
|
132 | | - """ |
133 | | - |
134 | | - @wraps(func) |
135 | | - def wrapped(*args: Any, **kwargs: Any) -> Any: |
136 | | - consecutive_errors = 0 |
137 | | - |
138 | | - while True: |
139 | | - try: |
140 | | - return func(self, *args, **kwargs) |
141 | | - except (lightning_cloud.openapi.rest.ApiException, urllib3.exceptions.HTTPError) as ex: |
142 | | - # retry if the backend fails with all errors except 4xx but not 408 - (Request Timeout) |
143 | | - if ( |
144 | | - isinstance(ex, urllib3.exceptions.HTTPError) |
145 | | - or ex.status in (408, 409) |
146 | | - or not str(ex.status).startswith("4") |
147 | | - ): |
148 | | - consecutive_errors += 1 |
149 | | - backoff_time = _get_next_backoff_time(consecutive_errors) |
150 | | - |
151 | | - msg = ( |
152 | | - f"error: {str(ex)}" |
153 | | - if isinstance(ex, urllib3.exceptions.HTTPError) |
154 | | - else f"response: {ex.status}" |
155 | | - ) |
156 | | - logger.debug( |
157 | | - f"The {func.__name__} request failed to reach the server, {msg}." |
158 | | - f" Retrying after {backoff_time} seconds." |
159 | | - ) |
160 | | - |
161 | | - if max_tries is not None and consecutive_errors == max_tries: |
162 | | - raise Exception(f"The {func.__name__} request failed to reach the server, {msg}.") |
163 | | - |
164 | | - time.sleep(backoff_time) |
165 | | - else: |
166 | | - raise ex |
167 | | - |
168 | | - return wrapped |
169 | | - |
170 | | - |
171 | | -class LightningClient(GridRestClient): |
172 | | - """The LightningClient is a wrapper around the GridRestClient. |
173 | | -
|
174 | | - It wraps all methods to monitor connection exceptions and employs a retry strategy. |
175 | | -
|
176 | | - Args: |
177 | | - retry: Whether API calls should follow a retry mechanism with exponential backoff. |
178 | | - max_tries: Maximum number of attempts (or -1 to retry forever). |
179 | | -
|
180 | | - """ |
181 | | - |
182 | | - def __init__(self, retry: bool = True, max_tries: Optional[int] = None) -> None: |
183 | | - super().__init__(api_client=create_swagger_client()) |
184 | | - if retry: |
185 | | - for base_class in GridRestClient.__mro__: |
186 | | - for name, attribute in base_class.__dict__.items(): |
187 | | - if callable(attribute) and attribute.__name__ != "__init__": |
188 | | - setattr(self, name, _retry_wrapper(self, attribute, max_tries=max_tries)) |
189 | | - |
190 | | - |
191 | 120 | class CustomRetryAdapter(HTTPAdapter): |
192 | 121 | def __init__(self, *args: Any, **kwargs: Any): |
193 | 122 | self.timeout = kwargs.pop("timeout", _DEFAULT_REQUEST_TIMEOUT) |
|
0 commit comments