Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
1. [#714](https://github.com/influxdata/influxdb-client-python/pull/714): Implement `WriteApi.flush()` to force pending batch writes without disposing the API (mirrors C#/Java batch writer).
1. [#715](https://github.com/influxdata/influxdb-client-python/pull/715): Accept numeric zero for `RangeThreshold.min`/`max` and greater/lesser `value` (identity checks vs `None`, not truthiness); cover create-check serialization.
1. [#720](https://github.com/influxdata/influxdb-client-python/pull/720): Prevent the `api_client` class from raising exceptions while the Python interpreter clears the client state during shutdown.
1. [#725](https://github.com/influxdata/influxdb-client-python/pull/725): Start the `WritesRetry` `max_retry_time` at a request's first failure, so a strategy passed as `retries` to `InfluxDBClient` keeps retrying after `max_retry_time` has passed since it was created.

## 1.50.0 [2026-01-23]

Expand Down
12 changes: 10 additions & 2 deletions influxdb_client/client/write/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ def __init__(self, jitter_interval=0, max_retry_delay=125, exponential_base=2, m
self.max_retry_delay = max_retry_delay
self.max_retry_time = max_retry_time
self.exponential_base = exponential_base
self.retry_timeout = datetime.now() + timedelta(seconds=max_retry_time)
# Deadline for retrying the current request. It is set when a request
# first fails, not here: one instance can serve many requests, e.g. when
# passed as ``retries`` to ``InfluxDBClient``.
self.retry_timeout = None
self.retry_callback = retry_callback

def new(self, **kw):
Expand Down Expand Up @@ -118,10 +121,15 @@ def get_retry_after(self, response):

def increment(self, method=None, url=None, response=None, error=None, _pool=None, _stacktrace=None):
"""Return a new Retry object with incremented retry counters."""
if self.retry_timeout < datetime.now():
retry_timeout = self.retry_timeout
if retry_timeout is None:
# First failure of a request: its retry time starts now.
retry_timeout = datetime.now() + timedelta(seconds=self.max_retry_time)
if retry_timeout <= datetime.now():
raise MaxRetryError(_pool, url, error or ResponseError("max_retry_time exceeded"))

new_retry = super().increment(method, url, response, error, _pool, _stacktrace)
new_retry.retry_timeout = retry_timeout

if response is not None:
parsed_error = InfluxDBError(response=response)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_WritesRetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,33 @@ def test_backoff_max_time(self):

self.assertEqual("max_retry_time exceeded", exception.reason.args[0])

def test_max_retry_time_starts_at_first_failure(self):
# A strategy passed as ``retries`` to InfluxDBClient lives as long as the client.
retry = NonRandomMinWritesRetry(max_retry_time=1)
time.sleep(1.5)

# The first failure of a request made after max_retry_time is still retried.
retry = retry.increment()
self.assertEqual(retry.is_exhausted(), False)
self.assertEqual(retry.total, 4)

def test_max_retry_time_is_per_request(self):
base = NonRandomMinWritesRetry(max_retry_time=1)

first_request = base.increment()
time.sleep(1.5)
with self.assertRaises(MaxRetryError) as cm:
first_request.increment()
self.assertEqual("max_retry_time exceeded", cm.exception.reason.args[0])

# Another request made with the same strategy gets its own max_retry_time.
second_request = base.increment()
self.assertEqual(second_request.total, 4)

def test_max_retry_time_zero_disables_retry(self):
with self.assertRaises(MaxRetryError):
WritesRetry(max_retry_time=0).increment()

def test_backoff_start_range(self):
retry = NonRandomMinWritesRetry(total=5, retry_interval=1, exponential_base=2,
max_retry_delay=550)
Expand Down