Skip to content

Commit 97b1f14

Browse files
Abhi591rohitesh-wingify
authored andcommitted
feat: getFlag Batching
1 parent 65b0806 commit 97b1f14

File tree

8 files changed

+175
-84
lines changed

8 files changed

+175
-84
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.15.0] - 2025-11-20
8+
9+
### Added
10+
11+
- Added batch event processing to optimize network calls during GetFlag operations. Multiple impression events are now collected and sent in a single batch request
12+
713
## [1.14.0] - 2025-11-17
814

915
### Added

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def run(self):
121121

122122
setup(
123123
name="vwo-fme-python-sdk",
124-
version="1.14.0",
124+
version="1.15.0",
125125
description="VWO Feature Management and Experimentation SDK for Python",
126126
long_description=long_description,
127127
long_description_content_type="text/markdown",

vwo/api/get_flag_api.py

Lines changed: 98 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
)
3434
from ..utils.rule_evaluation_util import evaluate_rule
3535
from ..utils.decision_util import evaluate_traffic_and_get_variation
36-
from ..utils.impression_util import create_and_send_impression_for_variation_shown
36+
from ..utils.impression_util import (
37+
send_impression_for_variation_shown_batch,
38+
send_impression_for_variation_shown,
39+
)
3740
from ..models.user.get_flag import GetFlag
3841
from ..services.storage_service import StorageService
3942
from ..decorators.storage_decorator import StorageDecorator
@@ -42,6 +45,9 @@
4245
from ..packages.logger.enums.log_level_enum import LogLevelEnum
4346
from ..constants.Constants import Constants
4447
from ..utils.debugger_service_util import send_debug_event_to_vwo
48+
from ..utils.network_util import get_track_user_payload_data
49+
from ..enums.event_enum import EventEnum
50+
from ..services.settings_manager import SettingsManager
4551

4652

4753
class GetFlagApi:
@@ -87,6 +93,7 @@ def get(
8793
stored_data = StorageDecorator().get_feature_from_storage(
8894
feature_key, context, storage_service
8995
)
96+
batchPayload = []
9097

9198
if stored_data and stored_data.get("experimentVariationId"):
9299
if "experimentKey" in stored_data:
@@ -153,24 +160,36 @@ def get(
153160
rollout_rules_to_evaluate: List[CampaignModel] = []
154161

155162
for rule in roll_out_rules:
156-
pre_segmentation_result, whitelisted_object, updated_decision = (
157-
evaluate_rule(
158-
settings,
159-
feature,
160-
rule,
161-
context,
162-
self._evaluated_feature_map,
163-
{},
164-
storage_service,
165-
decision,
166-
)
163+
(
164+
pre_segmentation_result,
165+
whitelisted_object,
166+
updated_decision,
167+
payload,
168+
) = evaluate_rule(
169+
settings,
170+
feature,
171+
rule,
172+
context,
173+
self._evaluated_feature_map,
174+
{},
175+
storage_service,
176+
decision,
167177
)
168-
169178
decision.update(updated_decision)
170179

171180
if pre_segmentation_result:
172181
rollout_rules_to_evaluate.append(rule)
173182

183+
if (
184+
SettingsManager.get_instance().is_gateway_service_provided
185+
and payload is not None
186+
and len(payload) > 0
187+
):
188+
send_impression_for_variation_shown(payload, context)
189+
else:
190+
if payload is not None and len(payload) > 0:
191+
batchPayload.append(payload)
192+
174193
self._evaluated_feature_map[feature_key] = {
175194
"rolloutId": rule.get_id(),
176195
"rolloutKey": rule.get_key(),
@@ -194,13 +213,23 @@ def get(
194213
self._update_integrations_decision_object(
195214
rollout_rules_to_evaluate[0], variation, decision
196215
)
197-
create_and_send_impression_for_variation_shown(
216+
217+
payload = get_track_user_payload_data(
198218
settings,
219+
EventEnum.VWO_VARIATION_SHOWN.value,
199220
rollout_rules_to_evaluate[0].get_id(),
200221
variation.get_id(),
201222
context,
202-
feature_key,
203223
)
224+
if (
225+
SettingsManager.get_instance().is_gateway_service_provided
226+
and payload is not None
227+
and len(payload) > 0
228+
):
229+
send_impression_for_variation_shown(payload, context)
230+
else:
231+
if payload is not None and len(payload) > 0:
232+
batchPayload.append(payload)
204233

205234
if not roll_out_rules:
206235
LogManager.get_instance().debug(
@@ -215,17 +244,20 @@ def get(
215244
meg_group_winner_campaigns = {}
216245

217246
for rule in experiment_rules:
218-
pre_segmentation_result, whitelisted_object, updated_decision = (
219-
evaluate_rule(
220-
settings,
221-
feature,
222-
rule,
223-
context,
224-
self._evaluated_feature_map,
225-
meg_group_winner_campaigns,
226-
storage_service,
227-
decision,
228-
)
247+
(
248+
pre_segmentation_result,
249+
whitelisted_object,
250+
updated_decision,
251+
payload,
252+
) = evaluate_rule(
253+
settings,
254+
feature,
255+
rule,
256+
context,
257+
self._evaluated_feature_map,
258+
meg_group_winner_campaigns,
259+
storage_service,
260+
decision,
229261
)
230262

231263
decision.update(updated_decision)
@@ -234,6 +266,16 @@ def get(
234266
if whitelisted_object is None:
235267
experiment_rules_to_evaluate.append(rule)
236268
else:
269+
if (
270+
SettingsManager.get_instance().is_gateway_service_provided
271+
and payload is not None
272+
and len(payload) > 0
273+
):
274+
send_impression_for_variation_shown(payload, context)
275+
else:
276+
if payload is not None and len(payload) > 0:
277+
batchPayload.append(payload)
278+
237279
self._get_flag_response.set_is_enabled(True)
238280
self._get_flag_response.set_variables(
239281
whitelisted_object["variation"].get_variables()
@@ -263,13 +305,23 @@ def get(
263305
self._update_integrations_decision_object(
264306
experiment_rules_to_evaluate[0], variation, decision
265307
)
266-
create_and_send_impression_for_variation_shown(
308+
309+
payload = get_track_user_payload_data(
267310
settings,
311+
EventEnum.VWO_VARIATION_SHOWN.value,
268312
experiment_rules_to_evaluate[0].get_id(),
269313
variation.get_id(),
270314
context,
271-
feature_key,
272315
)
316+
if (
317+
SettingsManager.get_instance().is_gateway_service_provided
318+
and payload is not None
319+
and len(payload) > 0
320+
):
321+
send_impression_for_variation_shown(payload, context)
322+
else:
323+
if payload is not None and len(payload) > 0:
324+
batchPayload.append(payload)
273325

274326
if self._get_flag_response.is_enabled():
275327
StorageDecorator().set_data_in_storage(
@@ -310,14 +362,26 @@ def get(
310362
)
311363
)
312364

313-
create_and_send_impression_for_variation_shown(
365+
payload = get_track_user_payload_data(
314366
settings,
367+
EventEnum.VWO_VARIATION_SHOWN.value,
315368
feature.get_impact_campaign().get_campaign_id(),
316-
(
317-
2 if self._get_flag_response.is_enabled() else 1
318-
), # 2 is for Variation (flag enabled), 1 is for Control (flag disabled)
319-
context,
320-
feature_key,
369+
(2 if self._get_flag_response.is_enabled() else 1),
370+
context
371+
)
372+
if (
373+
SettingsManager.get_instance().is_gateway_service_provided
374+
and payload is not None
375+
and len(payload) > 0
376+
):
377+
send_impression_for_variation_shown(payload, context)
378+
else:
379+
if payload is not None and len(payload) > 0:
380+
batchPayload.append(payload)
381+
382+
if not SettingsManager.get_instance().is_gateway_service_provided:
383+
send_impression_for_variation_shown_batch(
384+
batchPayload, settings.get_account_id(), settings.get_sdk_key()
321385
)
322386

323387
return self._get_flag_response

vwo/constants/Constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Constants:
1717
# TODO: read from setup.py
1818
sdk_meta = {
1919
"name": "vwo-fme-python-sdk",
20-
"version": "1.14.0"
20+
"version": "1.15.0"
2121
}
2222

2323
# Constants

vwo/utils/campaign_util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def set_variation_allocation(campaign: CampaignModel) -> None:
5353
LogManager.get_instance().info(
5454
info_messages.get("VARIATION_RANGE_ALLOCATION").format(
5555
variationKey=variation.get_name(),
56-
campaignKey=campaign.get_rule_key(),
56+
campaignKey=campaign.get_key(),
5757
variationWeight=variation.get_weight(),
5858
startRange=variation.get_start_range_variation(),
5959
endRange=variation.get_end_range_variation(),
@@ -452,7 +452,7 @@ def _handle_rollout_campaign(campaign: CampaignModel) -> None:
452452
LogManager.get_instance().info(
453453
info_messages.get("VARIATION_RANGE_ALLOCATION").format(
454454
variationKey=variation.get_name(),
455-
campaignKey=campaign.get_rule_key(),
455+
campaignKey=campaign.get_key(),
456456
variationWeight=variation.get_weight(),
457457
startRange=1,
458458
endRange=end_range,

vwo/utils/impression_util.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,31 @@
1313
# limitations under the License.
1414

1515

16-
from ..models.settings.settings_model import SettingsModel
1716
from ..models.user.context_model import ContextModel
1817
from ..utils.network_util import (
1918
get_events_base_properties,
20-
get_track_user_payload_data,
2119
send_post_api_request,
20+
send_post_batch_request,
2221
)
2322
from ..enums.event_enum import EventEnum
24-
from ..utils.campaign_util import (
25-
get_campaign_key_from_campaign_id,
26-
get_variation_name_from_campaign_id_and_variation_id,
27-
get_campaign_type_from_campaign_id,
28-
)
29-
from ..constants.Constants import Constants
23+
from ..packages.network_layer.manager.network_manager import NetworkManager
3024

3125

3226
# The function that creates and sends an impression for a variation shown event
33-
def create_and_send_impression_for_variation_shown(
34-
settings: SettingsModel, campaign_id: int, variation_id: int, context: ContextModel, feature_key: str
35-
):
27+
def send_impression_for_variation_shown(payload: dict, context: ContextModel):
28+
"""
29+
Sends an impression for a variation shown event.
30+
:param payload: The payload containing the event data
31+
:param context: The context of the user
32+
"""
3633
from ..vwo_client import VWOClient
34+
3735
# Get base properties for the event
3836
properties = get_events_base_properties(
3937
EventEnum.VWO_VARIATION_SHOWN.value,
4038
visitor_user_agent=context.get_user_agent(),
4139
ip_address=context.get_ip_address(),
4240
)
43-
# Construct payload data for tracking the user
44-
payload = get_track_user_payload_data(
45-
settings,
46-
EventEnum.VWO_VARIATION_SHOWN.value,
47-
campaign_id,
48-
variation_id,
49-
context,
50-
)
51-
52-
campaign_key_with_feaure_key = get_campaign_key_from_campaign_id(settings, campaign_id)
53-
variation_name = get_variation_name_from_campaign_id_and_variation_id(settings, campaign_id, variation_id)
54-
campaign_type = get_campaign_type_from_campaign_id(settings, campaign_id)
55-
campaign_key = None
56-
if feature_key == campaign_key_with_feaure_key:
57-
campaign_key = Constants.IMPACT_ANALYSIS
58-
else:
59-
feature_key_with_underscore = feature_key + "_"
60-
campaign_key = campaign_key_with_feaure_key.split(feature_key_with_underscore)[1]
6141

6242
vwo_instance = VWOClient.get_instance()
6343

@@ -67,9 +47,37 @@ def create_and_send_impression_for_variation_shown(
6747
vwo_instance.batch_event_queue.enqueue(payload)
6848
else:
6949
# Send the event immediately if batch events are not enabled
70-
send_post_api_request(properties, payload, context.get_id(), feature_info={
71-
"campaign_key": campaign_key,
72-
"variation_name": variation_name,
73-
"campaign_type": campaign_type,
74-
"feature_key": feature_key,
75-
})
50+
send_post_api_request(properties, payload, context.get_id())
51+
52+
def send_impression_for_variation_shown_batch(
53+
batch_payload: dict, account_id: int, sdk_key: str
54+
):
55+
"""
56+
Sends an impression for a variation shown event in batch.
57+
:param batch_payload: The batch payload containing all events from getFlag for a single user
58+
:param account_id: The account ID
59+
:param sdk_key: The SDK key
60+
"""
61+
from ..vwo_client import VWOClient
62+
63+
vwo_instance = VWOClient.get_instance()
64+
if vwo_instance.batch_event_queue is not None:
65+
# batch_payload - contains all events from getFlag for a single user
66+
# add each event to the batch queue
67+
for payload in batch_payload:
68+
vwo_instance.batch_event_queue.enqueue(payload)
69+
else:
70+
# Send the batch events immediately if batch events are not enabled
71+
network_instance = NetworkManager.get_instance()
72+
73+
# Create a method to send the request
74+
def send_batch_request():
75+
send_post_batch_request(batch_payload, account_id, sdk_key)
76+
77+
# check if threading is enabled
78+
if network_instance.should_use_threading:
79+
# execute the request in a background thread
80+
network_instance.execute_in_background(send_batch_request)
81+
else:
82+
# execute the request immediately
83+
send_batch_request()

0 commit comments

Comments
 (0)