1818
1919# currently excluded from documentation - see docs/README.md
2020
21+ import math
2122import random
2223import time
2324from enum import Enum
2728 DEFAULT_INITIAL_RECONNECT_DELAY ,
2829 DEFAULT_POLL_INTERVAL
2930)
30- from ldclient .impl .util import log , validate_positive_finite
31+ from ldclient .impl .util import log
3132
3233# The delay ceiling of the normal regime for streaming, in seconds.
3334NORMAL_STREAMING_CEILING_DELAY = 30
5354_MAX_BACKOFF_EXPONENT = 30
5455
5556
57+ def _usable_delay (value : float , default : float , name : str , ceiling : float = math .inf ) -> float :
58+ """
59+ Returns the delay to use, clamped to the ceiling. A value that is
60+ not a positive, finite number of seconds is replaced by the default.
61+
62+ :param value: the configured number of seconds
63+ :param default: the value to use when ``value`` is not usable
64+ :param name: the option name, for the warning message
65+ :param ceiling: the longest delay allowed
66+ """
67+
68+ if value > 0 and math .isfinite (value ):
69+ return min (value , ceiling )
70+ log .warning ("%s must be a positive, finite number of seconds; using the default of %ss" % (name , default ))
71+ return default
72+
73+
5674class FailureKind (Enum ):
5775 """How a failure is classified, which decides how long the next wait is."""
5876
@@ -251,19 +269,12 @@ def for_streaming(initial_reconnect_delay: float) -> RetryState:
251269 """
252270 Builds the retry state for a streaming data source.
253271
254- Streaming's operating cadence is zero, so there is no delay during
255- healthy operation. Stream failures use either the normal or extended
256- initial delay to determine their backoff wait. A stream returns to
257- healthy operation after establishing a successful connection with no
258- failures during the ``STREAMING_RESET_INTERVAL``.
259-
260- ``Config`` validates the configured delay, so this guard only catches a
261- state built without it.
262-
263- The extended regime never starts below the configured delay.
272+ Streaming's cadence is zero, so a healthy stream never waits. An invalid
273+ delay value is replaced by the documented default; one longer than a
274+ ceiling raises that bound rather than being cut down to it.
264275 """
265- initial_reconnect_delay = validate_positive_finite (
266- initial_reconnect_delay , DEFAULT_INITIAL_RECONNECT_DELAY , 'initial_reconnect_delay' , log
276+ initial_reconnect_delay = _usable_delay (
277+ initial_reconnect_delay , DEFAULT_INITIAL_RECONNECT_DELAY , 'initial_reconnect_delay'
267278 )
268279 return RetryState (
269280 normal_initial_delay = initial_reconnect_delay ,
@@ -279,16 +290,12 @@ def for_polling(poll_interval: float) -> RetryState:
279290 """
280291 Builds the retry state for a polling data source.
281292
282- The poll interval is polling's operating cadence, so no wait is ever
283- shorter than it. In the normal regime the delay bounds are the poll
284- interval itself, which means a normal failure simply polls again on
285- schedule. Polling is healthy on any successful poll, and resets after two
286- in a row.
287-
288- ``Config`` validates and clamps the poll interval, so this guard only
289- catches a state built without it.
293+ The poll interval is polling's cadence and its normal ceiling, so a normal
294+ failure waits the interval rather than backing off past it. An invalid
295+ interval is replaced by the documented default. No wait is ever shorter
296+ than the interval, so the cadence wins over the extended ceiling.
290297 """
291- poll_interval = validate_positive_finite (poll_interval , DEFAULT_POLL_INTERVAL , 'poll_interval' , log )
298+ poll_interval = _usable_delay (poll_interval , DEFAULT_POLL_INTERVAL , 'poll_interval' )
292299 return RetryState (
293300 normal_initial_delay = poll_interval ,
294301 normal_ceiling_delay = poll_interval ,
0 commit comments