Skip to content

Commit be6fe25

Browse files
authored
feat: added logging message for retries (#161)
1 parent f8d1407 commit be6fe25

File tree

5 files changed

+133
-0
lines changed

5 files changed

+133
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 1.12.0 [unreleased]
22

3+
### Features
4+
1. [#161](https://github.com/influxdata/influxdb-client-python/pull/161): Added logging message for retries
5+
36
## 1.11.0 [2020-10-02]
47

58
### Features

influxdb_client/client/exceptions.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Exceptions utils for InfluxDB."""
2+
3+
import logging
4+
5+
from urllib3 import HTTPResponse
6+
7+
logger = logging.getLogger(__name__)
8+
9+
10+
class InfluxDBError(Exception):
11+
"""Raised when a server error occurs."""
12+
13+
def __init__(self, response: HTTPResponse):
14+
"""Initialize the InfluxDBError handler."""
15+
self.response = response
16+
self.message = self._get_message(response)
17+
self.retry_after = response.getheader('Retry-After')
18+
super().__init__(self.message)
19+
20+
def _get_message(self, response):
21+
# Body
22+
if response.data:
23+
import json
24+
try:
25+
return json.loads(response.data)["message"]
26+
except Exception as e:
27+
logging.debug(f"Cannot parse error response to JSON: {response.data}, {e}")
28+
return response.data
29+
30+
# Header
31+
for header_key in ["X-Platform-Error-Code", "X-Influx-Error", "X-InfluxDb-Error"]:
32+
header_value = response.getheader(header_key)
33+
if header_value is not None:
34+
return header_value
35+
36+
# Http Status
37+
return response.reason

influxdb_client/client/write/retry.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
"""Implementation for Retry strategy during HTTP requests."""
22

3+
import logging
34
from itertools import takewhile
45
from random import random
56

67
from urllib3 import Retry
78

9+
from influxdb_client.client.exceptions import InfluxDBError
10+
11+
logger = logging.getLogger(__name__)
12+
813

914
class WritesRetry(Retry):
1015
"""
@@ -63,5 +68,24 @@ def get_retry_after(self, response):
6368
retry_after += self._jitter_delay()
6469
return retry_after
6570

71+
def increment(self, method=None, url=None, response=None, error=None, _pool=None, _stacktrace=None):
72+
"""Return a new Retry object with incremented retry counters."""
73+
new_retry = super().increment(method, url, response, error, _pool, _stacktrace)
74+
75+
if response is not None:
76+
parsed_error = InfluxDBError(response=response)
77+
elif error is not None:
78+
parsed_error = error
79+
else:
80+
parsed_error = f"Failed request to: {url}"
81+
82+
message = f"The retriable error occurred during request. Reason: '{parsed_error}'."
83+
if isinstance(parsed_error, InfluxDBError):
84+
message += f" Retry in {parsed_error.retry_after}s."
85+
86+
logger.warning(message)
87+
88+
return new_retry
89+
6690
def _jitter_delay(self):
6791
return self.jitter_interval * random()

tests/test_InfluxDBError.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import unittest
2+
3+
from urllib3 import HTTPResponse
4+
5+
from influxdb_client.client.exceptions import InfluxDBError
6+
7+
8+
class TestInfluxDBError(unittest.TestCase):
9+
def test_response(self):
10+
response = HTTPResponse()
11+
self.assertEqual(response, InfluxDBError(response=response).response)
12+
13+
def test_message(self):
14+
15+
response = HTTPResponse()
16+
response.headers.add('X-Platform-Error-Code', 'too many requests 1')
17+
self.assertEqual("too many requests 1", str(InfluxDBError(response=response)))
18+
19+
response = HTTPResponse()
20+
response.headers.add('X-Influx-Error', 'too many requests 2')
21+
self.assertEqual("too many requests 2", str(InfluxDBError(response=response)))
22+
23+
response = HTTPResponse()
24+
response.headers.add('X-InfluxDb-Error', 'too many requests 3')
25+
self.assertEqual("too many requests 3", str(InfluxDBError(response=response)))
26+
27+
response = HTTPResponse(body='{"code":"too many requests","message":"org 04014de4ed590000 has exceeded limited_write plan limit"}')
28+
response.headers.add('X-InfluxDb-Error', 'error 3')
29+
self.assertEqual("org 04014de4ed590000 has exceeded limited_write plan limit", str(InfluxDBError(response=response)))
30+
31+
response = HTTPResponse(body='org 04014de4ed590000 has exceeded limited_write plan limit')
32+
response.headers.add('X-InfluxDb-Error', 'error 3')
33+
self.assertEqual("org 04014de4ed590000 has exceeded limited_write plan limit", str(InfluxDBError(response=response)))
34+
35+
response = HTTPResponse(reason='too many requests 4')
36+
self.assertEqual("too many requests 4", str(InfluxDBError(response=response)))
37+
38+
def test_message_get_retry_after(self):
39+
response = HTTPResponse(reason="too many requests")
40+
response.headers.add('Retry-After', '63')
41+
42+
influx_db_error = InfluxDBError(response=response)
43+
self.assertEqual("too many requests", str(influx_db_error))
44+
self.assertEqual("63", influx_db_error.retry_after)
45+
46+
influx_db_error = InfluxDBError(response=HTTPResponse(reason="too many requests"))
47+
self.assertEqual("too many requests", str(influx_db_error))
48+
self.assertEqual(None, influx_db_error.retry_after)

tests/test_WritesRetry.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,24 @@ def test_is_retry_respect_method(self):
138138
retry = WritesRetry(method_whitelist=["POST"])
139139

140140
self.assertFalse(retry.is_retry("GET", 429, False))
141+
142+
def test_logging(self):
143+
response = HTTPResponse(
144+
body='{"code":"too many requests","message":"org 04014de4ed590000 has exceeded limited_write plan limit"}')
145+
response.headers.add('Retry-After', '63')
146+
147+
with self.assertLogs('influxdb_client.client.write.retry', level='WARNING') as cm:
148+
WritesRetry(total=5, backoff_factor=1, max_retry_delay=15) \
149+
.increment(response=response) \
150+
.increment(error=Exception("too many requests")) \
151+
.increment(url='http://localhost:9999')
152+
153+
self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
154+
"Reason: 'org 04014de4ed590000 has exceeded limited_write plan limit'. Retry in 63s.",
155+
cm.output[0])
156+
self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
157+
"Reason: 'too many requests'.",
158+
cm.output[1])
159+
self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
160+
"Reason: 'Failed request to: http://localhost:9999'.",
161+
cm.output[2])

0 commit comments

Comments
 (0)