Skip to content

Commit 04cba8b

Browse files
committed
Retry 50X responses and return last exception of retries exceed.
1 parent 5c1e520 commit 04cba8b

File tree

2 files changed

+19
-15
lines changed

2 files changed

+19
-15
lines changed

typesense/api_call.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import time
44

55
import requests
6-
from .exceptions import (ObjectAlreadyExists,
6+
from .exceptions import (HTTPStatus0Error, ObjectAlreadyExists,
77
ObjectNotFound, ObjectUnprocessable,
88
RequestMalformed, RequestUnauthorized,
99
ServerError, ServiceUnavailable, TypesenseClientError)
@@ -17,10 +17,9 @@ def __init__(self, config):
1717
self.nodes = copy.deepcopy(self.config.nodes)
1818
self.node_index = 0
1919

20-
@staticmethod
21-
def check_failed_node(node, healthcheck_interval):
20+
def check_failed_node(self, node):
2221
current_epoch_ts = int(time.time())
23-
return ((current_epoch_ts - node.last_access_ts) > healthcheck_interval)
22+
return (current_epoch_ts - node.last_access_ts) > self.config.healthcheck_interval_seconds
2423

2524
# Returns a healthy host from the pool in a round-robin fashion.
2625
# Might return an unhealthy host periodically to check for recovery.
@@ -30,19 +29,19 @@ def get_node(self):
3029
i += 1
3130
node = self.nodes[self.node_index]
3231
self.node_index = (self.node_index + 1) % len(self.nodes)
33-
healthcheck_interval = self.config.healthcheck_interval_seconds
3432

35-
if node.healthy or ApiCall.check_failed_node(node, healthcheck_interval):
33+
if node.healthy or self.check_failed_node(node):
3634
return node
3735

3836
# None of the nodes are marked healthy, but some of them could have become healthy since last health check.
3937
# So we will just return the next node.
40-
self.node_index = (self.node_index + 1) % len(self.nodes)
4138
return self.nodes[self.node_index]
4239

4340
@staticmethod
4441
def get_exception(http_code):
45-
if http_code == 400:
42+
if http_code == 0:
43+
return HTTPStatus0Error
44+
elif http_code == 400:
4645
return RequestMalformed
4746
elif http_code == 401:
4847
return RequestUnauthorized
@@ -62,6 +61,8 @@ def get_exception(http_code):
6261
# Makes the actual http request, along with retries
6362
def make_request(self, fn, endpoint, as_json, **kwargs):
6463
num_tries = 0
64+
last_exception = None
65+
6566
while num_tries < (self.config.num_retries + 1):
6667
num_tries += 1
6768
node = self.get_node()
@@ -86,19 +87,18 @@ def make_request(self, fn, endpoint, as_json, **kwargs):
8687
# We should raise a custom exception if status code is not 200 or 201
8788
if r.status_code not in [200, 201]:
8889
error_message = r.json().get('message', 'API error.')
89-
# print('error_message: ' + error_message)
90+
# Raised exception will be caught and retried only if it's a 50X
9091
raise ApiCall.get_exception(r.status_code)(r.status_code, error_message)
9192

9293
return r.json() if as_json else r.text
9394
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.HTTPError,
94-
requests.exceptions.RequestException, requests.exceptions.SSLError):
95+
requests.exceptions.RequestException, requests.exceptions.SSLError,
96+
HTTPStatus0Error, ServerError, ServiceUnavailable) as e:
9597
# Catch the exception and retry
96-
pass
97-
98-
# print('Failed, retrying after sleep: ' + node.port)
99-
time.sleep(self.config.retry_interval_seconds)
98+
last_exception = e
99+
time.sleep(self.config.retry_interval_seconds)
100100

101-
raise TypesenseClientError('Retries exceeded.')
101+
raise last_exception
102102

103103
def get(self, endpoint, params=None, as_json=True):
104104
params = params or {}

typesense/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@ class ServerError(TypesenseClientError):
3737

3838
class ServiceUnavailable(TypesenseClientError):
3939
pass
40+
41+
42+
class HTTPStatus0Error(TypesenseClientError):
43+
pass

0 commit comments

Comments
 (0)