Skip to content
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

fix: decision service for group and multiple experiments. #322

Merged
merged 16 commits into from
May 4, 2021

Conversation

ozayr-zaviar
Copy link
Contributor

@ozayr-zaviar ozayr-zaviar commented Apr 23, 2021

Summary

  • get_variation_for_feature code updated to fix group and multiple experiment issues.

Fixes:

  • Currently only first experiment is picked through feature.experimentIds[0]. Which is fixed by using loop.
  • Group experiments are evaluated first then individual experiments are evaluated instead of evaluating first occurring experiment in feature like in other SDKs which is fixed by removing condition for group experiments.

Test plan

  • New test cases for group experiments should pass in FSC

Issues

  • Need to fix test cases for get_variation_for_feature after current changes in Python.

@ozayr-zaviar ozayr-zaviar removed their assignment Apr 23, 2021
@ozayr-zaviar ozayr-zaviar changed the title refactor: decision service for group and multiple experiments. fix: decision service for group and multiple experiments. Apr 23, 2021
Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a comment fix suggested

decide_reasons += reasons
if experiment and experiment.id in feature.experimentIds:

# Next check if the feature is being experimented on
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "Next"

@msohailhussain
Copy link
Contributor

Remove the code in project_config.py where
feature.groupId is set since it's no more of use after changing the logic.

@coveralls
Copy link

coveralls commented Apr 27, 2021

Coverage Status

Coverage decreased (-0.04%) to 95.838% when pulling f0d9f60 on uzair/decision-service-for-group-fixes into 46651fa on master.

Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced on unit test changes, doesn't make sense to mock the value and expect the same value. Either we should mock Bucketer or remove these unit tests.

"test_user",
None,
False
)

def test_get_variation_for_feature__returns_none_for_user_not_in_group(self):
""" Test that get_variation_for_feature returns None for
user not in group and the feature is not part of a rollout. """
user not in group and the feature is not part of a rollout. """
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent like before.

@@ -1277,7 +1277,6 @@ def test_get_variation_for_feature__returns_variation_if_user_not_in_experiment_
side_effect=[[False, []], [True, []]],
) as mock_audience_check, self.mock_decision_logger as mock_decision_service_logging, mock.patch(
"optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't remove existing line.

)
mock_decision_service_logging.error.assert_called_once_with(
enums.Errors.INVALID_GROUP_ID.format("_get_variation_for_feature")
variation_received, _ = self.decision_service.get_variation_for_feature(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add logging.

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those test cases updated all turn out to be same - "returns rollout decisions when feature tests cannot find variations".
Can we fix them (and add more tests) to test

  • group exclusion in a feature
  • multiple non-grouped tests in a feature

) as mock_get_experiment_in_group, mock.patch(
"optimizely.decision_service.DecisionService.get_variation"
) as mock_decision:
"optimizely.decision_service.DecisionService.get_variation",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock does not give a test for "group". It tests the cases for feature-tests not bucketed.
Can we mock bucket to control group-exclusion cases?

Comment on lines -1323 to 1324
"optimizely.decision_service.DecisionService.get_experiment_in_group",
return_value=(self.project_config.get_experiment_from_key("group_exp_1"), []),
) as mock_get_experiment_in_group, mock.patch(
"optimizely.decision_service.DecisionService.get_variation",
return_value=(expected_variation, []),
) as mock_decision:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock does not give a test for "group". It tests the cases for feature-tests not bucketed.
Can we mock bucket to control group-exclusion cases?

Comment on lines 1391 to 1392
feature = self.project_config.get_feature_from_key("test_feature_in_group")
feature.groupId = "aabbccdd"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not use this feature "groupId" any more. This test is not valid any more.

Comment on lines 1429 to 1412
"optimizely.decision_service.DecisionService.get_experiment_in_group",
return_value=[self.project_config.get_experiment_from_key("group_exp_2"), []],
"optimizely.decision_service.DecisionService.get_variation",
return_value=[None, []],
) as mock_decision:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same test as above.

@msohailhussain msohailhussain marked this pull request as ready for review April 30, 2021 22:05
@msohailhussain msohailhussain requested a review from a team as a code owner April 30, 2021 22:05
tests/base.py Outdated
'forcedVariations': {},
'trafficAllocation': [
{'entityId': '', 'endOfRange': 2500},
{'entityId': '', 'endOfRange': 5000},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we generate datafile like this?

'forcedVariations': {},
'trafficAllocation': [
{'entityId': '222239', 'endOfRange': 2500},
{'entityId': '', 'endOfRange': 5000},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to discuss offline.

@@ -501,7 +501,7 @@ def test_init__with_v4_datafile(self):
'211111',
{'number_of_projects': entities.Variable('131', 'number_of_projects', 'integer', '10')},
),
'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}, '19228'),
'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 19228 removed.

def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_not_associated_with_feature(
self,
):
""" Test that if a user is in the mutex group but the experiment is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to revise description, doesn't make sense. let's talk offline.

"test_user",
None,
False
self.project_config, self.project_config.get_experiment_from_id("32222"), "test_user", None, False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we are asserting get_experiment_from_id.

'audienceIds': ['11160'],
'layerId': '211183',
'variations': [
{'key': 'var_1', 'id': '38901'},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

append experiment key name with the key.

'audienceIds': ['11160'],
'layerId': '211184',
'variations': [
{'key': 'var_1', 'id': '38905'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

'audienceIds': ['11160'],
'layerId': '211185',
'variations': [
{'key': 'var_1', 'id': '38906'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above.

Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@msohailhussain msohailhussain requested a review from jaeopt May 3, 2021 19:14
Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests look great.
I suggest a few small changes and add one more test.

)

mock_config_logging.debug.assert_called_with('Assigned bucket 2400 to user with bucketing ID "test_user".')
mock_generate_bucket_value.assert_called_with('test_user42222')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mock_generate_bucket_value.assert_called_with('test_user42222')
mock_generate_bucket_value.assert_called_with('test_user19229')

group bucketing is more interesting here

variation_received,
)
mock_config_logging.debug.assert_called_with('Assigned bucket 4000 to user with bucketing ID "test_user".')
mock_generate_bucket_value.assert_called_with('test_user42223')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

variation_received,
)
mock_config_logging.debug.assert_called_with('Assigned bucket 6500 to user with bucketing ID "test_user".')
mock_generate_bucket_value.assert_called_with('test_user42224')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

self,
):
""" Test that if a user is in the mutex group and the user bucket value should be equal to 2500
or greater than 5000."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or greater than 5000."""
or less than 5000."""

self,
):
""" Test that if a user is in the mutex group and the user bucket value should be equal to 5000
or greater than 7500."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or greater than 7500."""
or less than 7500."""

variation_received,
)

mock_generate_bucket_value.assert_called_with('test_user211147')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mock_generate_bucket_value.assert_called_with('test_user211147')
mock_generate_bucket_value.assert_called_with('test_user19229')

self,
):
""" Test that if a user is in the non-mutex group and the user bucket value should be equal to 2500
or greater than 5000."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or greater than 5000."""
or less than 5000."""

self,
):
""" Test that if a user is in the non-mutex group and the user bucket value should be equal to 5000
or greater than 7500."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or greater than 7500."""
or less than 7500."""

)

mock_generate_bucket_value.assert_called_with('test_user211147')
mock_config_logging.debug.assert_called_with('Assigned bucket 8000 to user with bucketing ID "test_user".')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests look great.
One missing test is that in multiple experiments, one of the test can be set to missing target by audience (instead of traffic allocation). We do not need to repeat multiple traffic allocation tests. One of them can be changed to use audience-mismatch.

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jaeopt jaeopt merged commit 4470ca9 into master May 4, 2021
@jaeopt jaeopt deleted the uzair/decision-service-for-group-fixes branch May 4, 2021 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants