@@ -41,9 +41,11 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
41
41
# under the License.
42
42
from __future__ import annotations
43
43
44
+ import asyncio
45
+
44
46
from json import JSONDecodeError
45
47
from os import environ
46
- from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict
48
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional
47
49
48
50
from httpx import AsyncClient, ConnectTimeout, NetworkError, Response
49
51
@@ -53,6 +55,7 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
53
55
API_VERSION_HEADER,
54
56
RID_KEY_HEADER,
55
57
SUPPORTED_CDI_VERSIONS,
58
+ RATE_LIMIT_STATUS_CODE,
56
59
)
57
60
from .normalised_url_path import NormalisedURLPath
58
61
@@ -250,6 +253,7 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
250
253
method: str,
251
254
http_function: Callable[[str], Awaitable[Response]],
252
255
no_of_tries: int,
256
+ retry_info_map: Optional[Dict[str, int]] = None,
253
257
) -> Any:
254
258
if no_of_tries == 0:
255
259
raise_general_exception("No SuperTokens core available to query")
@@ -266,6 +270,14 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
266
270
Querier.__last_tried_index %= len(self.__hosts)
267
271
url = current_host + path.get_as_string_dangerous()
268
272
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
+
269
281
ProcessState.get_instance().add_state(
270
282
AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
271
283
)
@@ -275,6 +287,20 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
275
287
):
276
288
Querier.__hosts_alive_for_testing.add(current_host)
277
289
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
+
278
304
if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore
279
305
raise_general_exception(
280
306
"SuperTokens core threw an error for a "
@@ -292,9 +318,9 @@ <h1 class="title">Module <code>supertokens_python.querier</code></h1>
292
318
except JSONDecodeError:
293
319
return response.text
294
320
295
- except (ConnectionError, NetworkError, ConnectTimeout):
321
+ except (ConnectionError, NetworkError, ConnectTimeout) as _ :
296
322
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
298
324
)
299
325
except Exception as e:
300
326
raise_general_exception(e)</ code > </ pre >
@@ -503,6 +529,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
503
529
method: str,
504
530
http_function: Callable[[str], Awaitable[Response]],
505
531
no_of_tries: int,
532
+ retry_info_map: Optional[Dict[str, int]] = None,
506
533
) -> Any:
507
534
if no_of_tries == 0:
508
535
raise_general_exception("No SuperTokens core available to query")
@@ -519,6 +546,14 @@ <h2 class="section-title" id="header-classes">Classes</h2>
519
546
Querier.__last_tried_index %= len(self.__hosts)
520
547
url = current_host + path.get_as_string_dangerous()
521
548
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
+
522
557
ProcessState.get_instance().add_state(
523
558
AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
524
559
)
@@ -528,6 +563,20 @@ <h2 class="section-title" id="header-classes">Classes</h2>
528
563
):
529
564
Querier.__hosts_alive_for_testing.add(current_host)
530
565
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
+
531
580
if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore
532
581
raise_general_exception(
533
582
"SuperTokens core threw an error for a "
@@ -545,9 +594,9 @@ <h2 class="section-title" id="header-classes">Classes</h2>
545
594
except JSONDecodeError:
546
595
return response.text
547
596
548
- except (ConnectionError, NetworkError, ConnectTimeout):
597
+ except (ConnectionError, NetworkError, ConnectTimeout) as _ :
549
598
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
551
600
)
552
601
except Exception as e:
553
602
raise_general_exception(e)</ code > </ pre >
0 commit comments