@@ -41,9 +41,11 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
4141# under the License.
4242from __future__ import annotations
4343
44+ import asyncio
45+
4446from json import JSONDecodeError
4547from os import environ
46- from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict
48+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional
4749
4850from httpx import AsyncClient, ConnectTimeout, NetworkError, Response
4951
@@ -53,6 +55,7 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
5355 API_VERSION_HEADER,
5456 RID_KEY_HEADER,
5557 SUPPORTED_CDI_VERSIONS,
58+ RATE_LIMIT_STATUS_CODE,
5659)
5760from .normalised_url_path import NormalisedURLPath
5861
@@ -250,6 +253,7 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
250253 method: str,
251254 http_function: Callable[[str], Awaitable[Response]],
252255 no_of_tries: int,
256+ retry_info_map: Optional[Dict[str, int]] = None,
253257 ) -> Any:
254258 if no_of_tries == 0:
255259 raise_general_exception("No SuperTokens core available to query")
@@ -266,6 +270,14 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
266270 Querier.__last_tried_index %= len(self.__hosts)
267271 url = current_host + path.get_as_string_dangerous()
268272
273+ max_retries = 5
274+
275+ if retry_info_map is None:
276+ retry_info_map = {}
277+
278+ if retry_info_map.get(url) is None:
279+ retry_info_map[url] = max_retries
280+
269281 ProcessState.get_instance().add_state(
270282 AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
271283 )
@@ -275,6 +287,20 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
275287 ):
276288 Querier.__hosts_alive_for_testing.add(current_host)
277289
290+ if response.status_code == RATE_LIMIT_STATUS_CODE:
291+ retries_left = retry_info_map[url]
292+
293+ if retries_left > 0:
294+ retry_info_map[url] = retries_left - 1
295+
296+ attempts_made = max_retries - retries_left
297+ delay = (10 + attempts_made * 250) / 1000
298+
299+ await asyncio.sleep(delay)
300+ return await self.__send_request_helper(
301+ path, method, http_function, no_of_tries, retry_info_map
302+ )
303+
278304 if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore
279305 raise_general_exception(
280306 "SuperTokens core threw an error for a "
@@ -292,9 +318,9 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
292318 except JSONDecodeError:
293319 return response.text
294320
295- except (ConnectionError, NetworkError, ConnectTimeout):
321+ except (ConnectionError, NetworkError, ConnectTimeout) as _ :
296322 return await self.__send_request_helper(
297- path, method, http_function, no_of_tries - 1
323+ path, method, http_function, no_of_tries - 1, retry_info_map
298324 )
299325 except Exception as e:
300326 raise_general_exception(e)</ code > </ pre >
@@ -503,6 +529,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
503529 method: str,
504530 http_function: Callable[[str], Awaitable[Response]],
505531 no_of_tries: int,
532+ retry_info_map: Optional[Dict[str, int]] = None,
506533 ) -> Any:
507534 if no_of_tries == 0:
508535 raise_general_exception("No SuperTokens core available to query")
@@ -519,6 +546,14 @@ <h2 class="section-title" id="header-classes">Classes</h2>
519546 Querier.__last_tried_index %= len(self.__hosts)
520547 url = current_host + path.get_as_string_dangerous()
521548
549+ max_retries = 5
550+
551+ if retry_info_map is None:
552+ retry_info_map = {}
553+
554+ if retry_info_map.get(url) is None:
555+ retry_info_map[url] = max_retries
556+
522557 ProcessState.get_instance().add_state(
523558 AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
524559 )
@@ -528,6 +563,20 @@ <h2 class="section-title" id="header-classes">Classes</h2>
528563 ):
529564 Querier.__hosts_alive_for_testing.add(current_host)
530565
566+ if response.status_code == RATE_LIMIT_STATUS_CODE:
567+ retries_left = retry_info_map[url]
568+
569+ if retries_left > 0:
570+ retry_info_map[url] = retries_left - 1
571+
572+ attempts_made = max_retries - retries_left
573+ delay = (10 + attempts_made * 250) / 1000
574+
575+ await asyncio.sleep(delay)
576+ return await self.__send_request_helper(
577+ path, method, http_function, no_of_tries, retry_info_map
578+ )
579+
531580 if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore
532581 raise_general_exception(
533582 "SuperTokens core threw an error for a "
@@ -545,9 +594,9 @@ <h2 class="section-title" id="header-classes">Classes</h2>
545594 except JSONDecodeError:
546595 return response.text
547596
548- except (ConnectionError, NetworkError, ConnectTimeout):
597+ except (ConnectionError, NetworkError, ConnectTimeout) as _ :
549598 return await self.__send_request_helper(
550- path, method, http_function, no_of_tries - 1
599+ path, method, http_function, no_of_tries - 1, retry_info_map
551600 )
552601 except Exception as e:
553602 raise_general_exception(e)</ code > </ pre >
0 commit comments