From 84e0055cbf31604d02a242acae28fc2f3a21b0cc Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Wed, 10 Jul 2024 11:37:53 -0700 Subject: [PATCH 1/4] feat: [default polling to 10 seconds from 5 minutes; add configurable value] (FF-2686) --- README.md | 2 +- eppo_client/client.py | 7 ++++--- eppo_client/config.py | 1 - eppo_client/constants.py | 6 ++---- eppo_client/version.py | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6d46eeb..13e6103 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ The `init` function accepts the following optional configuration arguments. | ------ | ----- | ----- | ----- | | **`assignment_logger`** | [AssignmentLogger](https://github.com/Eppo-exp/python-sdk/blob/ebc1a0b781769fe9d2e2be6fc81779eb8685a6c7/eppo_client/assignment_logger.py#L6-L10) | A callback that sends each assignment to your data warehouse. Required only for experiment analysis. See [example](#assignment-logger) below. | `None` | | **`is_graceful_mode`** | bool | When true, gracefully handles all exceptions within the assignment function and returns the default value. | `True` | - +| **`poll_interval_seconds`** | int | The interval in seconds at which the SDK polls for configuration updates. | `10` | ## Assignment logger diff --git a/eppo_client/client.py b/eppo_client/client.py index 4ae6f1f..9ea341a 100644 --- a/eppo_client/client.py +++ b/eppo_client/client.py @@ -14,7 +14,7 @@ from eppo_client.configuration_requestor import ( ExperimentConfigurationRequestor, ) -from eppo_client.constants import POLL_INTERVAL_MILLIS, POLL_JITTER_MILLIS +from eppo_client.constants import POLL_JITTER_SECONDS, POLL_INTERVAL_SECONDS from eppo_client.models import VariationType from eppo_client.poller import Poller from eppo_client.sharders import MD5Sharder @@ -33,13 +33,14 @@ def __init__( config_requestor: ExperimentConfigurationRequestor, assignment_logger: AssignmentLogger, is_graceful_mode: bool = True, + poll_interval_seconds: int = POLL_INTERVAL_SECONDS, ): self.__config_requestor = config_requestor self.__assignment_logger = assignment_logger self.__is_graceful_mode = is_graceful_mode self.__poller = Poller( - interval_millis=POLL_INTERVAL_MILLIS, - jitter_millis=POLL_JITTER_MILLIS, + interval_millis=poll_interval_seconds * 1000, + jitter_millis=POLL_JITTER_SECONDS * 1000, callback=config_requestor.fetch_and_store_configurations, ) self.__poller.start() diff --git a/eppo_client/config.py b/eppo_client/config.py index d9d7d88..492f728 100644 --- a/eppo_client/config.py +++ b/eppo_client/config.py @@ -1,6 +1,5 @@ from eppo_client.assignment_logger import AssignmentLogger from eppo_client.base_model import SdkBaseModel - from eppo_client.validation import validate_not_blank diff --git a/eppo_client/constants.py b/eppo_client/constants.py index a13e8c8..97d49c9 100644 --- a/eppo_client/constants.py +++ b/eppo_client/constants.py @@ -1,5 +1,3 @@ # poller -SECOND_MILLIS = 1000 -MINUTE_MILLIS = 60 * SECOND_MILLIS -POLL_JITTER_MILLIS = 30 * SECOND_MILLIS -POLL_INTERVAL_MILLIS = 5 * MINUTE_MILLIS +POLL_JITTER_SECONDS = 2 +POLL_INTERVAL_SECONDS = 10 diff --git a/eppo_client/version.py b/eppo_client/version.py index 903a158..dcbfb52 100644 --- a/eppo_client/version.py +++ b/eppo_client/version.py @@ -1 +1 @@ -__version__ = "3.4.0" +__version__ = "3.5.0" From addc822e857c1f432e36d0634e10d5d062202795 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Mon, 15 Jul 2024 11:25:05 -0700 Subject: [PATCH 2/4] leave default at 5 min; add comment for breaking change in future --- README.md | 2 +- eppo_client/constants.py | 5 ++++- eppo_client/version.py | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 13e6103..c90effa 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ The `init` function accepts the following optional configuration arguments. | ------ | ----- | ----- | ----- | | **`assignment_logger`** | [AssignmentLogger](https://github.com/Eppo-exp/python-sdk/blob/ebc1a0b781769fe9d2e2be6fc81779eb8685a6c7/eppo_client/assignment_logger.py#L6-L10) | A callback that sends each assignment to your data warehouse. Required only for experiment analysis. See [example](#assignment-logger) below. | `None` | | **`is_graceful_mode`** | bool | When true, gracefully handles all exceptions within the assignment function and returns the default value. | `True` | -| **`poll_interval_seconds`** | int | The interval in seconds at which the SDK polls for configuration updates. | `10` | +| **`poll_interval_seconds`** | int | The interval in seconds at which the SDK polls for configuration updates. | `300` | ## Assignment logger diff --git a/eppo_client/constants.py b/eppo_client/constants.py index 97d49c9..0cacc25 100644 --- a/eppo_client/constants.py +++ b/eppo_client/constants.py @@ -1,3 +1,6 @@ # poller POLL_JITTER_SECONDS = 2 -POLL_INTERVAL_SECONDS = 10 +# We accidently shipped Python with a 5 minute poll interval. +# Customers can set the poll interval to 30 seconds to match the behavior of the other server SDKs. +# Please change this to 30 seconds when ready to bump to 4.0. +POLL_INTERVAL_SECONDS = 5 * 60 # 5 minutes diff --git a/eppo_client/version.py b/eppo_client/version.py index dcbfb52..91b5628 100644 --- a/eppo_client/version.py +++ b/eppo_client/version.py @@ -1 +1,4 @@ +# Note to developers: When ready to bump to 4.0, please change +# the `POLL_INTERVAL_SECONDS` constant in `eppo_client/constants.py` +# to 30 seconds to match the behavior of the other server SDKs. __version__ = "3.5.0" From ba9916009ac4052b008a7c25308d26f4bb3583c4 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Mon, 15 Jul 2024 12:13:42 -0700 Subject: [PATCH 3/4] add config for jitter --- README.md | 1 + eppo_client/__init__.py | 2 ++ eppo_client/client.py | 7 ++++--- eppo_client/config.py | 4 +++- eppo_client/constants.py | 4 ++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c90effa..c795c41 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ The `init` function accepts the following optional configuration arguments. | **`assignment_logger`** | [AssignmentLogger](https://github.com/Eppo-exp/python-sdk/blob/ebc1a0b781769fe9d2e2be6fc81779eb8685a6c7/eppo_client/assignment_logger.py#L6-L10) | A callback that sends each assignment to your data warehouse. Required only for experiment analysis. See [example](#assignment-logger) below. | `None` | | **`is_graceful_mode`** | bool | When true, gracefully handles all exceptions within the assignment function and returns the default value. | `True` | | **`poll_interval_seconds`** | int | The interval in seconds at which the SDK polls for configuration updates. | `300` | +| **`poll_jitter_seconds`** | int | The jitter in seconds to add to the poll interval. | `30` | ## Assignment logger diff --git a/eppo_client/__init__.py b/eppo_client/__init__.py index 91ee517..427ed12 100644 --- a/eppo_client/__init__.py +++ b/eppo_client/__init__.py @@ -48,6 +48,8 @@ def init(config: Config) -> EppoClient: __client = EppoClient( config_requestor=config_requestor, assignment_logger=assignment_logger, + poll_interval_seconds=config.poll_interval_seconds, + poll_jitter_seconds=config.poll_jitter_seconds, is_graceful_mode=is_graceful_mode, ) return __client diff --git a/eppo_client/client.py b/eppo_client/client.py index 9ea341a..0ce156a 100644 --- a/eppo_client/client.py +++ b/eppo_client/client.py @@ -14,7 +14,6 @@ from eppo_client.configuration_requestor import ( ExperimentConfigurationRequestor, ) -from eppo_client.constants import POLL_JITTER_SECONDS, POLL_INTERVAL_SECONDS from eppo_client.models import VariationType from eppo_client.poller import Poller from eppo_client.sharders import MD5Sharder @@ -22,6 +21,7 @@ from eppo_client.validation import validate_not_blank from eppo_client.eval import FlagEvaluation, Evaluator, none_result from eppo_client.version import __version__ +from eppo_client.constants import POLL_INTERVAL_SECONDS_DEFAULT, POLL_JITTER_SECONDS_DEFAULT logger = logging.getLogger(__name__) @@ -33,14 +33,15 @@ def __init__( config_requestor: ExperimentConfigurationRequestor, assignment_logger: AssignmentLogger, is_graceful_mode: bool = True, - poll_interval_seconds: int = POLL_INTERVAL_SECONDS, + poll_interval_seconds: int = POLL_INTERVAL_SECONDS_DEFAULT, + poll_jitter_seconds: int = POLL_JITTER_SECONDS_DEFAULT, ): self.__config_requestor = config_requestor self.__assignment_logger = assignment_logger self.__is_graceful_mode = is_graceful_mode self.__poller = Poller( interval_millis=poll_interval_seconds * 1000, - jitter_millis=POLL_JITTER_SECONDS * 1000, + jitter_millis=poll_jitter_seconds * 1000, callback=config_requestor.fetch_and_store_configurations, ) self.__poller.start() diff --git a/eppo_client/config.py b/eppo_client/config.py index 492f728..1b6684b 100644 --- a/eppo_client/config.py +++ b/eppo_client/config.py @@ -1,13 +1,15 @@ from eppo_client.assignment_logger import AssignmentLogger from eppo_client.base_model import SdkBaseModel from eppo_client.validation import validate_not_blank - +from eppo_client.constants import POLL_INTERVAL_SECONDS_DEFAULT, POLL_JITTER_SECONDS_DEFAULT class Config(SdkBaseModel): api_key: str base_url: str = "https://fscdn.eppo.cloud/api" assignment_logger: AssignmentLogger is_graceful_mode: bool = True + poll_interval_seconds: int = POLL_INTERVAL_SECONDS_DEFAULT + poll_jitter_seconds: int = POLL_JITTER_SECONDS_DEFAULT def _validate(self): validate_not_blank("api_key", self.api_key) diff --git a/eppo_client/constants.py b/eppo_client/constants.py index 0cacc25..e0f7f3a 100644 --- a/eppo_client/constants.py +++ b/eppo_client/constants.py @@ -1,6 +1,6 @@ # poller -POLL_JITTER_SECONDS = 2 # We accidently shipped Python with a 5 minute poll interval. # Customers can set the poll interval to 30 seconds to match the behavior of the other server SDKs. # Please change this to 30 seconds when ready to bump to 4.0. -POLL_INTERVAL_SECONDS = 5 * 60 # 5 minutes +POLL_JITTER_SECONDS_DEFAULT = 30 # 30 seconds +POLL_INTERVAL_SECONDS_DEFAULT = 5 * 60 # 5 minutes From 591b21dcb5afc4e5bc7545b062f221ec3e9bad9e Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Mon, 15 Jul 2024 12:17:34 -0700 Subject: [PATCH 4/4] tox --- eppo_client/client.py | 5 ++++- eppo_client/config.py | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/eppo_client/client.py b/eppo_client/client.py index 0ce156a..c1cde48 100644 --- a/eppo_client/client.py +++ b/eppo_client/client.py @@ -21,7 +21,10 @@ from eppo_client.validation import validate_not_blank from eppo_client.eval import FlagEvaluation, Evaluator, none_result from eppo_client.version import __version__ -from eppo_client.constants import POLL_INTERVAL_SECONDS_DEFAULT, POLL_JITTER_SECONDS_DEFAULT +from eppo_client.constants import ( + POLL_INTERVAL_SECONDS_DEFAULT, + POLL_JITTER_SECONDS_DEFAULT, +) logger = logging.getLogger(__name__) diff --git a/eppo_client/config.py b/eppo_client/config.py index 1b6684b..62e8e7d 100644 --- a/eppo_client/config.py +++ b/eppo_client/config.py @@ -1,7 +1,11 @@ from eppo_client.assignment_logger import AssignmentLogger from eppo_client.base_model import SdkBaseModel from eppo_client.validation import validate_not_blank -from eppo_client.constants import POLL_INTERVAL_SECONDS_DEFAULT, POLL_JITTER_SECONDS_DEFAULT +from eppo_client.constants import ( + POLL_INTERVAL_SECONDS_DEFAULT, + POLL_JITTER_SECONDS_DEFAULT, +) + class Config(SdkBaseModel): api_key: str