Skip to content

Commit 06a84f7

Browse files
committed
update evaluator
1 parent 2c9c47e commit 06a84f7

File tree

3 files changed

+229
-25
lines changed

3 files changed

+229
-25
lines changed

splitio/client/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def __init__(self, factory, recorder, labels_enabled=True):
201201
:rtype: Client
202202
"""
203203
ClientBase.__init__(self, factory, recorder, labels_enabled)
204-
self._context_factory = EvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'))
204+
self._context_factory = EvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'), factory._get_storage('rule_based_segments'))
205205

206206
def destroy(self):
207207
"""
@@ -668,7 +668,7 @@ def __init__(self, factory, recorder, labels_enabled=True):
668668
:rtype: Client
669669
"""
670670
ClientBase.__init__(self, factory, recorder, labels_enabled)
671-
self._context_factory = AsyncEvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'))
671+
self._context_factory = AsyncEvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'), factory._get_storage('rule_based_segments'))
672672

673673
async def destroy(self):
674674
"""

splitio/engine/evaluator.py

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
from splitio.models.grammar.condition import ConditionType
77
from splitio.models.grammar.matchers.misc import DependencyMatcher
88
from splitio.models.grammar.matchers.keys import UserDefinedSegmentMatcher
9+
from splitio.models.grammar.matchers.rule_based_segment import RuleBasedSegmentMatcher
910
from splitio.optional.loaders import asyncio
1011

1112
CONTROL = 'control'
12-
EvaluationContext = namedtuple('EvaluationContext', ['flags', 'segment_memberships'])
13+
EvaluationContext = namedtuple('EvaluationContext', ['flags', 'segment_memberships', 'segment_rbs_memberships', 'segment_rbs_conditions'])
1314

1415
_LOGGER = logging.getLogger(__name__)
1516

@@ -98,9 +99,10 @@ def _treatment_for_flag(self, flag, key, bucketing, attributes, ctx):
9899

99100
class EvaluationDataFactory:
100101

101-
def __init__(self, split_storage, segment_storage):
102+
def __init__(self, split_storage, segment_storage, rbs_segment_storage):
102103
self._flag_storage = split_storage
103104
self._segment_storage = segment_storage
105+
self._rbs_segment_storage = rbs_segment_storage
104106

105107
def context_for(self, key, feature_names):
106108
"""
@@ -114,28 +116,50 @@ def context_for(self, key, feature_names):
114116
pending = set(feature_names)
115117
splits = {}
116118
pending_memberships = set()
119+
pending_rbs_memberships = set()
117120
while pending:
118121
fetched = self._flag_storage.fetch_many(list(pending))
119122
features = filter_missing(fetched)
120123
splits.update(features)
121124
pending = set()
122125
for feature in features.values():
123-
cf, cs = get_dependencies(feature)
126+
cf, cs, crbs = get_dependencies(feature)
124127
pending.update(filter(lambda f: f not in splits, cf))
125128
pending_memberships.update(cs)
126-
127-
return EvaluationContext(splits, {
128-
segment: self._segment_storage.segment_contains(segment, key)
129-
for segment in pending_memberships
130-
})
131-
129+
pending_rbs_memberships.update(crbs)
130+
131+
rbs_segment_memberships = {}
132+
rbs_segment_conditions = {}
133+
key_membership = False
134+
segment_memberhsip = False
135+
for rbs_segment in pending_rbs_memberships:
136+
key_membership = key in self._rbs_segment_storage.get(rbs_segment).excluded.get_excluded_keys()
137+
segment_memberhsip = False
138+
for segment_name in self._rbs_segment_storage.get(rbs_segment).excluded.get_excluded_segments():
139+
if self._segment_storage.segment_contains(segment_name, key):
140+
segment_memberhsip = True
141+
break
142+
143+
rbs_segment_memberships.update({rbs_segment: segment_memberhsip or key_membership})
144+
if not (segment_memberhsip or key_membership):
145+
rbs_segment_conditions.update({rbs_segment: [condition for condition in self._rbs_segment_storage.get(rbs_segment).conditions]})
146+
147+
return EvaluationContext(
148+
splits,
149+
{ segment: self._segment_storage.segment_contains(segment, key)
150+
for segment in pending_memberships
151+
},
152+
rbs_segment_memberships,
153+
rbs_segment_conditions
154+
)
132155

133156
class AsyncEvaluationDataFactory:
134157

135-
def __init__(self, split_storage, segment_storage):
158+
def __init__(self, split_storage, segment_storage, rbs_segment_storage):
136159
self._flag_storage = split_storage
137160
self._segment_storage = segment_storage
138-
161+
self._rbs_segment_storage = rbs_segment_storage
162+
139163
async def context_for(self, key, feature_names):
140164
"""
141165
Recursively iterate & fetch all data required to evaluate these flags.
@@ -148,23 +172,47 @@ async def context_for(self, key, feature_names):
148172
pending = set(feature_names)
149173
splits = {}
150174
pending_memberships = set()
175+
pending_rbs_memberships = set()
151176
while pending:
152177
fetched = await self._flag_storage.fetch_many(list(pending))
153178
features = filter_missing(fetched)
154179
splits.update(features)
155180
pending = set()
156181
for feature in features.values():
157-
cf, cs = get_dependencies(feature)
182+
cf, cs, crbs = get_dependencies(feature)
158183
pending.update(filter(lambda f: f not in splits, cf))
159184
pending_memberships.update(cs)
160-
185+
pending_rbs_memberships.update(crbs)
186+
161187
segment_names = list(pending_memberships)
162188
segment_memberships = await asyncio.gather(*[
163189
self._segment_storage.segment_contains(segment, key)
164190
for segment in segment_names
165191
])
166192

167-
return EvaluationContext(splits, dict(zip(segment_names, segment_memberships)))
193+
rbs_segment_memberships = {}
194+
rbs_segment_conditions = {}
195+
key_membership = False
196+
segment_memberhsip = False
197+
for rbs_segment in pending_rbs_memberships:
198+
rbs_segment_obj = await self._rbs_segment_storage.get(rbs_segment)
199+
key_membership = key in rbs_segment_obj.excluded.get_excluded_keys()
200+
segment_memberhsip = False
201+
for segment_name in rbs_segment_obj.excluded.get_excluded_segments():
202+
if await self._segment_storage.segment_contains(segment_name, key):
203+
segment_memberhsip = True
204+
break
205+
206+
rbs_segment_memberships.update({rbs_segment: segment_memberhsip or key_membership})
207+
if not (segment_memberhsip or key_membership):
208+
rbs_segment_conditions.update({rbs_segment: [condition for condition in rbs_segment_obj.conditions]})
209+
210+
return EvaluationContext(
211+
splits,
212+
dict(zip(segment_names, segment_memberships)),
213+
rbs_segment_memberships,
214+
rbs_segment_conditions
215+
)
168216

169217

170218
def get_dependencies(feature):
@@ -173,14 +221,17 @@ def get_dependencies(feature):
173221
"""
174222
feature_names = []
175223
segment_names = []
224+
rbs_segment_names = []
176225
for condition in feature.conditions:
177226
for matcher in condition.matchers:
227+
if isinstance(matcher,RuleBasedSegmentMatcher):
228+
rbs_segment_names.append(matcher._rbs_segment_name)
178229
if isinstance(matcher,UserDefinedSegmentMatcher):
179230
segment_names.append(matcher._segment_name)
180231
elif isinstance(matcher, DependencyMatcher):
181232
feature_names.append(matcher._split_name)
182233

183-
return feature_names, segment_names
234+
return feature_names, segment_names, rbs_segment_names
184235

185236
def filter_missing(features):
186237
return {k: v for (k, v) in features.items() if v is not None}

0 commit comments

Comments
 (0)