Skip to content

Commit

Permalink
feat: added logging message for retries (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
bednar authored Oct 14, 2020
1 parent f8d1407 commit be6fe25
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## 1.12.0 [unreleased]

### Features
1. [#161](https://github.com/influxdata/influxdb-client-python/pull/161): Added logging message for retries

## 1.11.0 [2020-10-02]

### Features
Expand Down
37 changes: 37 additions & 0 deletions influxdb_client/client/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Exceptions utils for InfluxDB."""

import logging

from urllib3 import HTTPResponse

logger = logging.getLogger(__name__)


class InfluxDBError(Exception):
"""Raised when a server error occurs."""

def __init__(self, response: HTTPResponse):
"""Initialize the InfluxDBError handler."""
self.response = response
self.message = self._get_message(response)
self.retry_after = response.getheader('Retry-After')
super().__init__(self.message)

def _get_message(self, response):
# Body
if response.data:
import json
try:
return json.loads(response.data)["message"]
except Exception as e:
logging.debug(f"Cannot parse error response to JSON: {response.data}, {e}")
return response.data

# Header
for header_key in ["X-Platform-Error-Code", "X-Influx-Error", "X-InfluxDb-Error"]:
header_value = response.getheader(header_key)
if header_value is not None:
return header_value

# Http Status
return response.reason
24 changes: 24 additions & 0 deletions influxdb_client/client/write/retry.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
"""Implementation for Retry strategy during HTTP requests."""

import logging
from itertools import takewhile
from random import random

from urllib3 import Retry

from influxdb_client.client.exceptions import InfluxDBError

logger = logging.getLogger(__name__)


class WritesRetry(Retry):
"""
Expand Down Expand Up @@ -63,5 +68,24 @@ def get_retry_after(self, response):
retry_after += self._jitter_delay()
return retry_after

def increment(self, method=None, url=None, response=None, error=None, _pool=None, _stacktrace=None):
"""Return a new Retry object with incremented retry counters."""
new_retry = super().increment(method, url, response, error, _pool, _stacktrace)

if response is not None:
parsed_error = InfluxDBError(response=response)
elif error is not None:
parsed_error = error
else:
parsed_error = f"Failed request to: {url}"

message = f"The retriable error occurred during request. Reason: '{parsed_error}'."
if isinstance(parsed_error, InfluxDBError):
message += f" Retry in {parsed_error.retry_after}s."

logger.warning(message)

return new_retry

def _jitter_delay(self):
return self.jitter_interval * random()
48 changes: 48 additions & 0 deletions tests/test_InfluxDBError.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import unittest

from urllib3 import HTTPResponse

from influxdb_client.client.exceptions import InfluxDBError


class TestInfluxDBError(unittest.TestCase):
def test_response(self):
response = HTTPResponse()
self.assertEqual(response, InfluxDBError(response=response).response)

def test_message(self):

response = HTTPResponse()
response.headers.add('X-Platform-Error-Code', 'too many requests 1')
self.assertEqual("too many requests 1", str(InfluxDBError(response=response)))

response = HTTPResponse()
response.headers.add('X-Influx-Error', 'too many requests 2')
self.assertEqual("too many requests 2", str(InfluxDBError(response=response)))

response = HTTPResponse()
response.headers.add('X-InfluxDb-Error', 'too many requests 3')
self.assertEqual("too many requests 3", str(InfluxDBError(response=response)))

response = HTTPResponse(body='{"code":"too many requests","message":"org 04014de4ed590000 has exceeded limited_write plan limit"}')
response.headers.add('X-InfluxDb-Error', 'error 3')
self.assertEqual("org 04014de4ed590000 has exceeded limited_write plan limit", str(InfluxDBError(response=response)))

response = HTTPResponse(body='org 04014de4ed590000 has exceeded limited_write plan limit')
response.headers.add('X-InfluxDb-Error', 'error 3')
self.assertEqual("org 04014de4ed590000 has exceeded limited_write plan limit", str(InfluxDBError(response=response)))

response = HTTPResponse(reason='too many requests 4')
self.assertEqual("too many requests 4", str(InfluxDBError(response=response)))

def test_message_get_retry_after(self):
response = HTTPResponse(reason="too many requests")
response.headers.add('Retry-After', '63')

influx_db_error = InfluxDBError(response=response)
self.assertEqual("too many requests", str(influx_db_error))
self.assertEqual("63", influx_db_error.retry_after)

influx_db_error = InfluxDBError(response=HTTPResponse(reason="too many requests"))
self.assertEqual("too many requests", str(influx_db_error))
self.assertEqual(None, influx_db_error.retry_after)
21 changes: 21 additions & 0 deletions tests/test_WritesRetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,24 @@ def test_is_retry_respect_method(self):
retry = WritesRetry(method_whitelist=["POST"])

self.assertFalse(retry.is_retry("GET", 429, False))

def test_logging(self):
response = HTTPResponse(
body='{"code":"too many requests","message":"org 04014de4ed590000 has exceeded limited_write plan limit"}')
response.headers.add('Retry-After', '63')

with self.assertLogs('influxdb_client.client.write.retry', level='WARNING') as cm:
WritesRetry(total=5, backoff_factor=1, max_retry_delay=15) \
.increment(response=response) \
.increment(error=Exception("too many requests")) \
.increment(url='http://localhost:9999')

self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
"Reason: 'org 04014de4ed590000 has exceeded limited_write plan limit'. Retry in 63s.",
cm.output[0])
self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
"Reason: 'too many requests'.",
cm.output[1])
self.assertEqual("WARNING:influxdb_client.client.write.retry:The retriable error occurred during request. "
"Reason: 'Failed request to: http://localhost:9999'.",
cm.output[2])

0 comments on commit be6fe25

Please sign in to comment.