-
Notifications
You must be signed in to change notification settings - Fork 37
feat: odp segment manager #402
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
Changes from 1 commit
dea481c
8065996
4766c11
72057fb
39b0118
1b15581
6180223
92f8792
8cd49a0
f312b77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# Copyright 2022, Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from __future__ import annotations | ||
|
||
from typing import List, Optional | ||
|
||
from optimizely import logger as optimizely_logger | ||
from optimizely.helpers.enums import Errors | ||
from optimizely.helpers.enums import OptimizelySegmentOption | ||
from optimizely.odp.lru_cache import LRUCache | ||
from optimizely.odp.odp_config import OdpConfig | ||
from optimizely.odp.zaius_graphql_api_manager import ZaiusGraphQLApiManager | ||
|
||
|
||
class OdpSegmentManager: | ||
"""Schedules connections to ODP for audience segmentation and caches the results.""" | ||
|
||
IGNORE_CACHE = OptimizelySegmentOption.IGNORE_CACHE | ||
RESET_CACHE = OptimizelySegmentOption.RESET_CACHE | ||
|
||
def __init__(self, odp_config: Optional[OdpConfig], segments_cache: Optional[LRUCache[str, List[str]](1000, 1000)], | ||
zaius_manager: Optional[ZaiusGraphQLApiManager], | ||
logger: Optional[optimizely_logger.Logger] = None) -> None: | ||
|
||
self.odp_config = odp_config | ||
self.segments_cache = segments_cache | ||
self.zaius_manager = zaius_manager | ||
self.logger = logger or optimizely_logger.NoOpLogger() | ||
|
||
def fetch_qualified_segments(self, user_key: str, user_value: str, options: list[OptimizelySegmentOption]): | ||
Mat001 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not self.odp_config.odp_integrated(): | ||
self.logger.error(Errors.FETCH_SEGMENTS_FAILED.format('apiKey/apiHost not defined')) | ||
Mat001 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return None | ||
|
||
odp_api_key: Optional[str] = self.odp_config.get_api_key() | ||
Mat001 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
odp_api_host: Optional[str] = self.odp_config.get_api_host() | ||
odp_segments_to_check: Optional[list[str]] = self.odp_config.get_segments_to_check() | ||
|
||
if not odp_segments_to_check and not len(odp_segments_to_check): | ||
self.logger.debug('No segments are used in the project. Returning empty list.') | ||
return [] | ||
|
||
cache_key = self.make_cache_key(user_key, user_value) | ||
|
||
ignore_cache = self.IGNORE_CACHE if self.IGNORE_CACHE in options else None | ||
Mat001 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
reset_cache = self.RESET_CACHE if self.RESET_CACHE in options else None | ||
|
||
if reset_cache: | ||
self._reset() | ||
|
||
if not ignore_cache and not reset_cache: | ||
segments = self.segments_cache.lookup(cache_key) | ||
if segments: | ||
self.logger.debug('ODP cache hit. Returning segments from cache.') | ||
return segments | ||
|
||
self.logger.debug('ODP cache miss. Making a call to ODP server.') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not necessarily a cache miss... We might want to split this into two debug logs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrewleap-optimizely Not sure what you mean, can you clarify? If we don't hit the cache we miss it no? Rb segment manager has the same btw There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that the cache might not have been checked, because Yea I should change it in ruby and probably Java where I pulled it from 😀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like this: if not ignore_cache and not reset_cache:
segments = self.segments_cache.lookup(cache_key)
if segments:
self.logger.debug('ODP cache hit. Returning segments from cache.')
return segments
self.logger.debug('ODP cache miss.')
self.logger.debug('Making a call to ODP server.') Otherwise looking at the logs with a disabled cache may confuse the reader into thinking the cache is being used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, yeah, that makes total sense. good catch |
||
|
||
segments = self.zaius_manager.fetch_segments(odp_api_key, odp_api_host, user_key, user_value, | ||
odp_segments_to_check) | ||
|
||
if segments and not ignore_cache: | ||
self.segments_cache.save(cache_key, segments) | ||
|
||
return segments | ||
|
||
def _reset(self): | ||
self.segments_cache.reset() | ||
|
||
def make_cache_key(self, user_key: str, user_value: str) -> str: | ||
return user_key + '-$-' + user_value | ||
Mat001 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Copyright 2022, Optimizely | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http:#www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from __future__ import annotations | ||
from tests import base | ||
from optimizely.odp.odp_config import OdpConfig | ||
|
||
|
||
class OdpConfigTest(base.BaseTest): | ||
api_host = 'test-host' | ||
api_key = 'test-key' | ||
segments_to_check = ['test-segment'] | ||
|
||
def test_init_config(self): | ||
config = OdpConfig(self.api_key, self.api_host, self.segments_to_check) | ||
|
||
self.assertEqual(config.get_api_key(), self.api_key) | ||
self.assertEqual(config.get_api_host(), self.api_host) | ||
self.assertEqual(config.get_segments_to_check(), self.segments_to_check) | ||
|
||
def test_update_config(self): | ||
config = OdpConfig() | ||
updated = config.update(self.api_key, self.api_host, self.segments_to_check) | ||
|
||
self.assertStrictTrue(updated) | ||
self.assertEqual(config.get_api_key(), self.api_key) | ||
self.assertEqual(config.get_api_host(), self.api_host) | ||
self.assertEqual(config.get_segments_to_check(), self.segments_to_check) | ||
|
||
updated = config.update(self.api_key, self.api_host, self.segments_to_check) | ||
self.assertStrictFalse(updated) |
Uh oh!
There was an error while loading. Please reload this page.