Skip to content

feat: added logging message for retries #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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])