Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fb2723a

Browse files
authored
Merge pull request #555 from splitio/development
Release 10.2.0
2 parents ac54e59 + 6aae7f4 commit fb2723a

18 files changed

+1490
-221
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
10.2.0 (Jan 17, 2025)
2+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on SplitView type objects. Read more in our docs.
3+
14
10.1.0 (Aug 7, 2024)
25
- Added support for Kerberos authentication in Spnego and Proxy Kerberos server instances.
36

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2024 Split Software, Inc.
1+
Copyright © 2025 Split Software, Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

splitio/client/client.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from splitio.engine.evaluator import Evaluator, CONTROL, EvaluationDataFactory, AsyncEvaluationDataFactory
55
from splitio.engine.splitters import Splitter
6-
from splitio.models.impressions import Impression, Label
6+
from splitio.models.impressions import Impression, Label, ImpressionDecorated
77
from splitio.models.events import Event, EventWrapper
88
from splitio.models.telemetry import get_latency_bucket_index, MethodExceptionsAndLatencies
99
from splitio.client import input_validator
@@ -22,7 +22,8 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
2222
'impression': {
2323
'label': Label.EXCEPTION,
2424
'change_number': None,
25-
}
25+
},
26+
'impressions_disabled': False
2627
}
2728

2829
_NON_READY_EVAL_RESULT = {
@@ -31,7 +32,8 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
3132
'impression': {
3233
'label': Label.NOT_READY,
3334
'change_number': None
34-
}
35+
},
36+
'impressions_disabled': False
3537
}
3638

3739
def __init__(self, factory, recorder, labels_enabled=True):
@@ -116,14 +118,15 @@ def _validate_treatments_input(key, features, attributes, method):
116118

117119
def _build_impression(self, key, bucketing, feature, result):
118120
"""Build an impression based on evaluation data & it's result."""
119-
return Impression(
120-
matching_key=key,
121+
return ImpressionDecorated(
122+
Impression(matching_key=key,
121123
feature_name=feature,
122124
treatment=result['treatment'],
123125
label=result['impression']['label'] if self._labels_enabled else None,
124126
change_number=result['impression']['change_number'],
125127
bucketing_key=bucketing,
126-
time=utctime_ms())
128+
time=utctime_ms()),
129+
disabled=result['impressions_disabled'])
127130

128131
def _build_impressions(self, key, bucketing, results):
129132
"""Build an impression based on evaluation data & it's result."""
@@ -296,8 +299,8 @@ def _get_treatment(self, method, key, feature, attributes=None):
296299
result = self._FAILED_EVAL_RESULT
297300

298301
if result['impression']['label'] != Label.SPLIT_NOT_FOUND:
299-
impression = self._build_impression(key, bucketing, feature, result)
300-
self._record_stats([(impression, attributes)], start, method)
302+
impression_decorated = self._build_impression(key, bucketing, feature, result)
303+
self._record_stats([(impression_decorated, attributes)], start, method)
301304

302305
return result['treatment'], result['configurations']
303306

@@ -571,23 +574,23 @@ def _get_treatments(self, key, features, method, attributes=None):
571574
self._telemetry_evaluation_producer.record_exception(method)
572575
results = {n: self._FAILED_EVAL_RESULT for n in features}
573576

574-
imp_attrs = [
577+
imp_decorated_attrs = [
575578
(i, attributes) for i in self._build_impressions(key, bucketing, results)
576-
if i.label != Label.SPLIT_NOT_FOUND
579+
if i.Impression.label != Label.SPLIT_NOT_FOUND
577580
]
578-
self._record_stats(imp_attrs, start, method)
581+
self._record_stats(imp_decorated_attrs, start, method)
579582

580583
return {
581584
feature: (results[feature]['treatment'], results[feature]['configurations'])
582585
for feature in results
583586
}
584587

585-
def _record_stats(self, impressions, start, operation):
588+
def _record_stats(self, impressions_decorated, start, operation):
586589
"""
587590
Record impressions.
588591
589-
:param impressions: Generated impressions
590-
:type impressions: list[tuple[splitio.models.impression.Impression, dict]]
592+
:param impressions_decorated: Generated impressions
593+
:type impressions_decorated: list[tuple[splitio.models.impression.ImpressionDecorated, dict]]
591594
592595
:param start: timestamp when get_treatment or get_treatments was called
593596
:type start: int
@@ -596,7 +599,7 @@ def _record_stats(self, impressions, start, operation):
596599
:type operation: str
597600
"""
598601
end = get_current_epoch_time_ms()
599-
self._recorder.record_treatment_stats(impressions, get_latency_bucket_index(end - start),
602+
self._recorder.record_treatment_stats(impressions_decorated, get_latency_bucket_index(end - start),
600603
operation, 'get_' + operation.value)
601604

602605
def track(self, key, traffic_type, event_type, value=None, properties=None):
@@ -763,8 +766,8 @@ async def _get_treatment(self, method, key, feature, attributes=None):
763766
result = self._FAILED_EVAL_RESULT
764767

765768
if result['impression']['label'] != Label.SPLIT_NOT_FOUND:
766-
impression = self._build_impression(key, bucketing, feature, result)
767-
await self._record_stats([(impression, attributes)], start, method)
769+
impression_decorated = self._build_impression(key, bucketing, feature, result)
770+
await self._record_stats([(impression_decorated, attributes)], start, method)
768771
return result['treatment'], result['configurations']
769772

770773
async def get_treatments(self, key, feature_flag_names, attributes=None):
@@ -960,23 +963,23 @@ async def _get_treatments(self, key, features, method, attributes=None):
960963
await self._telemetry_evaluation_producer.record_exception(method)
961964
results = {n: self._FAILED_EVAL_RESULT for n in features}
962965

963-
imp_attrs = [
966+
imp_decorated_attrs = [
964967
(i, attributes) for i in self._build_impressions(key, bucketing, results)
965-
if i.label != Label.SPLIT_NOT_FOUND
968+
if i.Impression.label != Label.SPLIT_NOT_FOUND
966969
]
967-
await self._record_stats(imp_attrs, start, method)
970+
await self._record_stats(imp_decorated_attrs, start, method)
968971

969972
return {
970973
feature: (res['treatment'], res['configurations'])
971974
for feature, res in results.items()
972975
}
973976

974-
async def _record_stats(self, impressions, start, operation):
977+
async def _record_stats(self, impressions_decorated, start, operation):
975978
"""
976979
Record impressions for async calls
977980
978-
:param impressions: Generated impressions
979-
:type impressions: list[tuple[splitio.models.impression.Impression, dict]]
981+
:param impressions_decorated: Generated impressions decorated
982+
:type impressions_decorated: list[tuple[splitio.models.impression.Impression, dict]]
980983
981984
:param start: timestamp when get_treatment or get_treatments was called
982985
:type start: int
@@ -985,7 +988,7 @@ async def _record_stats(self, impressions, start, operation):
985988
:type operation: str
986989
"""
987990
end = get_current_epoch_time_ms()
988-
await self._recorder.record_treatment_stats(impressions, get_latency_bucket_index(end - start),
991+
await self._recorder.record_treatment_stats(impressions_decorated, get_latency_bucket_index(end - start),
989992
operation, 'get_' + operation.value)
990993

991994
async def track(self, key, traffic_type, event_type, value=None, properties=None):

splitio/client/factory.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from splitio.client.listener import ImpressionListenerWrapper, ImpressionListenerWrapperAsync
1414
from splitio.engine.impressions.impressions import Manager as ImpressionsManager
1515
from splitio.engine.impressions import set_classes, set_classes_async
16-
from splitio.engine.impressions.strategies import StrategyDebugMode
16+
from splitio.engine.impressions.strategies import StrategyDebugMode, StrategyNoneMode
1717
from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageConsumer, \
1818
TelemetryStorageProducerAsync, TelemetryStorageConsumerAsync
1919
from splitio.engine.impressions.manager import Counter as ImpressionsCounter
@@ -553,10 +553,10 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
553553
unique_keys_tracker = UniqueKeysTracker(_UNIQUE_KEYS_CACHE_SIZE)
554554
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
555555
clear_filter_task, impressions_count_sync, impressions_count_task, \
556-
imp_strategy = set_classes('MEMORY', cfg['impressionsMode'], apis, imp_counter, unique_keys_tracker)
556+
imp_strategy, none_strategy = set_classes('MEMORY', cfg['impressionsMode'], apis, imp_counter, unique_keys_tracker)
557557

558558
imp_manager = ImpressionsManager(
559-
imp_strategy, telemetry_runtime_producer)
559+
imp_strategy, none_strategy, telemetry_runtime_producer)
560560

561561
synchronizers = SplitSynchronizers(
562562
SplitSynchronizer(apis['splits'], storages['splits']),
@@ -681,10 +681,10 @@ async def _build_in_memory_factory_async(api_key, cfg, sdk_url=None, events_url=
681681
unique_keys_tracker = UniqueKeysTrackerAsync(_UNIQUE_KEYS_CACHE_SIZE)
682682
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
683683
clear_filter_task, impressions_count_sync, impressions_count_task, \
684-
imp_strategy = set_classes_async('MEMORY', cfg['impressionsMode'], apis, imp_counter, unique_keys_tracker)
684+
imp_strategy, none_strategy = set_classes_async('MEMORY', cfg['impressionsMode'], apis, imp_counter, unique_keys_tracker)
685685

686686
imp_manager = ImpressionsManager(
687-
imp_strategy, telemetry_runtime_producer)
687+
imp_strategy, none_strategy, telemetry_runtime_producer)
688688

689689
synchronizers = SplitSynchronizers(
690690
SplitSynchronizerAsync(apis['splits'], storages['splits']),
@@ -775,10 +775,10 @@ def _build_redis_factory(api_key, cfg):
775775
unique_keys_tracker = UniqueKeysTracker(_UNIQUE_KEYS_CACHE_SIZE)
776776
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
777777
clear_filter_task, impressions_count_sync, impressions_count_task, \
778-
imp_strategy = set_classes('REDIS', cfg['impressionsMode'], redis_adapter, imp_counter, unique_keys_tracker)
778+
imp_strategy, none_strategy = set_classes('REDIS', cfg['impressionsMode'], redis_adapter, imp_counter, unique_keys_tracker)
779779

780780
imp_manager = ImpressionsManager(
781-
imp_strategy,
781+
imp_strategy, none_strategy,
782782
telemetry_runtime_producer)
783783

784784
synchronizers = SplitSynchronizers(None, None, None, None,
@@ -858,10 +858,10 @@ async def _build_redis_factory_async(api_key, cfg):
858858
unique_keys_tracker = UniqueKeysTrackerAsync(_UNIQUE_KEYS_CACHE_SIZE)
859859
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
860860
clear_filter_task, impressions_count_sync, impressions_count_task, \
861-
imp_strategy = set_classes_async('REDIS', cfg['impressionsMode'], redis_adapter, imp_counter, unique_keys_tracker)
861+
imp_strategy, none_strategy = set_classes_async('REDIS', cfg['impressionsMode'], redis_adapter, imp_counter, unique_keys_tracker)
862862

863863
imp_manager = ImpressionsManager(
864-
imp_strategy,
864+
imp_strategy, none_strategy,
865865
telemetry_runtime_producer)
866866

867867
synchronizers = SplitSynchronizers(None, None, None, None,
@@ -936,10 +936,10 @@ def _build_pluggable_factory(api_key, cfg):
936936
unique_keys_tracker = UniqueKeysTracker(_UNIQUE_KEYS_CACHE_SIZE)
937937
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
938938
clear_filter_task, impressions_count_sync, impressions_count_task, \
939-
imp_strategy = set_classes('PLUGGABLE', cfg['impressionsMode'], pluggable_adapter, imp_counter, unique_keys_tracker, storage_prefix)
939+
imp_strategy, none_strategy = set_classes('PLUGGABLE', cfg['impressionsMode'], pluggable_adapter, imp_counter, unique_keys_tracker, storage_prefix)
940940

941941
imp_manager = ImpressionsManager(
942-
imp_strategy,
942+
imp_strategy, none_strategy,
943943
telemetry_runtime_producer)
944944

945945
synchronizers = SplitSynchronizers(None, None, None, None,
@@ -1017,10 +1017,10 @@ async def _build_pluggable_factory_async(api_key, cfg):
10171017
unique_keys_tracker = UniqueKeysTrackerAsync(_UNIQUE_KEYS_CACHE_SIZE)
10181018
unique_keys_synchronizer, clear_filter_sync, unique_keys_task, \
10191019
clear_filter_task, impressions_count_sync, impressions_count_task, \
1020-
imp_strategy = set_classes_async('PLUGGABLE', cfg['impressionsMode'], pluggable_adapter, imp_counter, unique_keys_tracker, storage_prefix)
1020+
imp_strategy, none_strategy = set_classes_async('PLUGGABLE', cfg['impressionsMode'], pluggable_adapter, imp_counter, unique_keys_tracker, storage_prefix)
10211021

10221022
imp_manager = ImpressionsManager(
1023-
imp_strategy,
1023+
imp_strategy, none_strategy,
10241024
telemetry_runtime_producer)
10251025

10261026
synchronizers = SplitSynchronizers(None, None, None, None,
@@ -1123,7 +1123,7 @@ def _build_localhost_factory(cfg):
11231123
manager.start()
11241124

11251125
recorder = StandardRecorder(
1126-
ImpressionsManager(StrategyDebugMode(), telemetry_runtime_producer),
1126+
ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer),
11271127
storages['events'],
11281128
storages['impressions'],
11291129
telemetry_evaluation_producer,
@@ -1192,7 +1192,7 @@ async def _build_localhost_factory_async(cfg):
11921192
await manager.start()
11931193

11941194
recorder = StandardRecorderAsync(
1195-
ImpressionsManager(StrategyDebugMode(), telemetry_runtime_producer),
1195+
ImpressionsManager(StrategyDebugMode(), StrategyNoneMode(), telemetry_runtime_producer),
11961196
storages['events'],
11971197
storages['impressions'],
11981198
telemetry_evaluation_producer,

splitio/engine/evaluator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ def eval_with_context(self, key, bucketing, feature_name, attrs, ctx):
6767
'impression': {
6868
'label': label,
6969
'change_number': _change_number
70-
}
70+
},
71+
'impressions_disabled': feature.impressions_disabled if feature else None
7172
}
7273

7374
def _treatment_for_flag(self, flag, key, bucketing, attributes, ctx):

splitio/engine/impressions/__init__.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,24 @@ def set_classes(storage_mode, impressions_mode, api_adapter, imp_counter, unique
5353
api_impressions_adapter = api_adapter['impressions']
5454
sender_adapter = InMemorySenderAdapter(api_telemetry_adapter)
5555

56+
none_strategy = StrategyNoneMode()
57+
unique_keys_synchronizer = UniqueKeysSynchronizer(sender_adapter, unique_keys_tracker)
58+
unique_keys_task = UniqueKeysSyncTask(unique_keys_synchronizer.send_all)
59+
clear_filter_sync = ClearFilterSynchronizer(unique_keys_tracker)
60+
impressions_count_sync = ImpressionsCountSynchronizer(api_impressions_adapter, imp_counter)
61+
impressions_count_task = ImpressionsCountSyncTask(impressions_count_sync.synchronize_counters)
62+
clear_filter_task = ClearFilterSyncTask(clear_filter_sync.clear_all)
63+
unique_keys_tracker.set_queue_full_hook(unique_keys_task.flush)
64+
5665
if impressions_mode == ImpressionsMode.NONE:
5766
imp_strategy = StrategyNoneMode()
58-
unique_keys_synchronizer = UniqueKeysSynchronizer(sender_adapter, unique_keys_tracker)
59-
unique_keys_task = UniqueKeysSyncTask(unique_keys_synchronizer.send_all)
60-
clear_filter_sync = ClearFilterSynchronizer(unique_keys_tracker)
61-
impressions_count_sync = ImpressionsCountSynchronizer(api_impressions_adapter, imp_counter)
62-
impressions_count_task = ImpressionsCountSyncTask(impressions_count_sync.synchronize_counters)
63-
clear_filter_task = ClearFilterSyncTask(clear_filter_sync.clear_all)
64-
unique_keys_tracker.set_queue_full_hook(unique_keys_task.flush)
6567
elif impressions_mode == ImpressionsMode.DEBUG:
6668
imp_strategy = StrategyDebugMode()
6769
else:
6870
imp_strategy = StrategyOptimizedMode()
69-
impressions_count_sync = ImpressionsCountSynchronizer(api_impressions_adapter, imp_counter)
70-
impressions_count_task = ImpressionsCountSyncTask(impressions_count_sync.synchronize_counters)
7171

7272
return unique_keys_synchronizer, clear_filter_sync, unique_keys_task, clear_filter_task, \
73-
impressions_count_sync, impressions_count_task, imp_strategy
73+
impressions_count_sync, impressions_count_task, imp_strategy, none_strategy
7474

7575
def set_classes_async(storage_mode, impressions_mode, api_adapter, imp_counter, unique_keys_tracker, prefix=None):
7676
"""
@@ -118,21 +118,21 @@ def set_classes_async(storage_mode, impressions_mode, api_adapter, imp_counter,
118118
api_impressions_adapter = api_adapter['impressions']
119119
sender_adapter = InMemorySenderAdapterAsync(api_telemetry_adapter)
120120

121+
none_strategy = StrategyNoneMode()
122+
unique_keys_synchronizer = UniqueKeysSynchronizerAsync(sender_adapter, unique_keys_tracker)
123+
unique_keys_task = UniqueKeysSyncTaskAsync(unique_keys_synchronizer.send_all)
124+
clear_filter_sync = ClearFilterSynchronizerAsync(unique_keys_tracker)
125+
impressions_count_sync = ImpressionsCountSynchronizerAsync(api_impressions_adapter, imp_counter)
126+
impressions_count_task = ImpressionsCountSyncTaskAsync(impressions_count_sync.synchronize_counters)
127+
clear_filter_task = ClearFilterSyncTaskAsync(clear_filter_sync.clear_all)
128+
unique_keys_tracker.set_queue_full_hook(unique_keys_task.flush)
129+
121130
if impressions_mode == ImpressionsMode.NONE:
122131
imp_strategy = StrategyNoneMode()
123-
unique_keys_synchronizer = UniqueKeysSynchronizerAsync(sender_adapter, unique_keys_tracker)
124-
unique_keys_task = UniqueKeysSyncTaskAsync(unique_keys_synchronizer.send_all)
125-
clear_filter_sync = ClearFilterSynchronizerAsync(unique_keys_tracker)
126-
impressions_count_sync = ImpressionsCountSynchronizerAsync(api_impressions_adapter, imp_counter)
127-
impressions_count_task = ImpressionsCountSyncTaskAsync(impressions_count_sync.synchronize_counters)
128-
clear_filter_task = ClearFilterSyncTaskAsync(clear_filter_sync.clear_all)
129-
unique_keys_tracker.set_queue_full_hook(unique_keys_task.flush)
130132
elif impressions_mode == ImpressionsMode.DEBUG:
131133
imp_strategy = StrategyDebugMode()
132134
else:
133135
imp_strategy = StrategyOptimizedMode()
134-
impressions_count_sync = ImpressionsCountSynchronizerAsync(api_impressions_adapter, imp_counter)
135-
impressions_count_task = ImpressionsCountSyncTaskAsync(impressions_count_sync.synchronize_counters)
136136

137137
return unique_keys_synchronizer, clear_filter_sync, unique_keys_task, clear_filter_task, \
138-
impressions_count_sync, impressions_count_task, imp_strategy
138+
impressions_count_sync, impressions_count_task, imp_strategy, none_strategy

0 commit comments

Comments
 (0)