Skip to content

Decider #6

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

Merged
merged 23 commits into from
Apr 18, 2022
Merged

Decider #6

merged 23 commits into from
Apr 18, 2022

Conversation

mrlevitas
Copy link
Contributor

Summary

This pr creates these classes:

  • DeciderContextFactory (for baseplate setup)
  • DeciderContext (contains all fields used for bucketing, targeting, & exposure, e.g. "user_id", "country_code", etc)
  • Decider (contains new API: get_variant())

as well as the baseplate setup convenience function:

  • decider_client_from_config()

Todo:

  • DeciderClient (convenience class for baseplate setup)
  • implement get_variant_without_expose(), expose() on Decider

Expose logic has been implemented but commented out because Rust Decider lib needs to return a vector of events containing all of these fields:

“experiment_id:experiment_name:experiment_version:variant_name:bucket_val:start_ts:stop_ts:owner:event_type"

for the exposure event (currently, the SDK is not json.parse()ing the experiment cfg json and deferring that to Rust Decider lib, so Rust has to return these fields).

Testing

Unit tests for:

  • get_variant() happy path
  • get_variant() returns None if the experiment's name is not in the config
  • get_variant() returns None if the json config has an experiment's id as a str instead of an int
  • get_variant() returns None if missing "experiment" key in json config

Also some basic unit test coverage of:

  • decider_client_from_config() setup
  • that DeciderContextFactory.make_object_for_context() correctly populates DeciderContext ivar in Decider instance

Missing tests/todos:

  • DeciderClient
  • get_variant_without_expose()
  • expose()

@mrlevitas mrlevitas requested review from mattknox and wuyiqicc April 8, 2022 17:30
@mrlevitas
Copy link
Contributor Author

mrlevitas commented Apr 8, 2022

CI will fail until we actually publish decider-py package to pypi, not just test.pypi.org.
@mattknox should we do this today so we can get tests running here?

edit: package published to pypi as reddit-decider and CI works.

@mrlevitas
Copy link
Contributor Author

mrlevitas commented Apr 8, 2022

you can run pytest locally by installing decider-py package from test.pypi.org:
pip install -i https://test.pypi.org/simple/ decider-py==0.0.3

edit: pip install -r requirements.txt works

and then running the new test via:
python -m pytest tests/decider_tests.py
or make test for the full test suite.

Copy link

@mattknox mattknox left a comment

Choose a reason for hiding this comment

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

nice! You should probably have a BP.py expert like Andrew Boyle or similar look at this too.

"app_name": self._app_name,
"build_number": self._build_number,
"loid": self._loid,
"cookie_created_timestamp": self._cookie_created_timestamp,
Copy link

Choose a reason for hiding this comment

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

no event fields? That seems fine, if this is to be passed into decider

Copy link
Contributor Author

@mrlevitas mrlevitas Apr 8, 2022

Choose a reason for hiding this comment

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

user_event fields currently get passed under inputs keyword in event_logger.log() so I separated them out into get_user_event_fields() function here which I combine with additional exposure_kwargs passed into the fns ,in the commented out expose code, like so:

inputs = self._decider_context.get_user_event_fields()
inputs.update(exposure_kwargs or {})

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually, I should combine self._decider_context.to_dict() into inputs as well to make some of the other fields available, such as bucketing_value, to the ExperimentsLogger, since it seems everything in input will get dumped to the final v2 event.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed get_user_event_fields() since self._decider_context.to_dict() contains the 3 fields that event_fields() contains

def test_none_returned_on_variant_call_with_bad_id(self):
config = {
"test": {
"id": "1",
Copy link
Contributor

Choose a reason for hiding this comment

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

What will be the case to cause the id to be passed as str?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just some general error handling, nothing particular--I took this test case from existing tests

Copy link
Contributor

Choose a reason for hiding this comment

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

I see.
It is a non blocker but I think if ZK config is somehow passing str id, maybe we should not silently return None.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's definitely a serious issue if that happens but I don't think we should raise an exception..at least it seems that the mentality of the experiments.py sdk is to log to warning instead of raising exceptions, which I do here (rust lib provides specific failure message to std out as well):

err: SerdeError(
    Error("invalid type: string \"1\", expected u32", line: 1, column: 19),
)
WARNING  reddit_decider:__init__.py:139 Rust decider did not initialize.

Copy link
Contributor Author

@mrlevitas mrlevitas Apr 8, 2022

Choose a reason for hiding this comment

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

maybe Rust decider did not initialize. should be error level log, instead of warning though?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the x = rust_decider.init() return value have a way to access the error string via x.err() (in this pr) so that we can control the error message in the sdk and log it to logger.error() without having rust lib print to std out and pollute logs.

Comment on lines +247 to +267
user_id=user_event_fields.get("user_id"),
logged_in=user_event_fields.get("logged_in"),
Copy link
Contributor Author

@mrlevitas mrlevitas Apr 8, 2022

Choose a reason for hiding this comment

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

getting user_id & logged_in from ec.user.event_fields() instead off of edge_context.user directly to match current functionality.

@pacejackson pacejackson requested review from MelissaCole and bradengroom and removed request for pacejackson April 8, 2022 20:55
@mrlevitas
Copy link
Contributor Author

nice! You should probably have a BP.py expert like Andrew Boyle or similar look at this too.

Hi @pacejackson!
@mattknox suggested I get run this change by your since it's intended to be pulled into BP.py
More details from the design doc here.

Copy link

@SiddharthManoj SiddharthManoj left a comment

Choose a reason for hiding this comment

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

Looks great Roman! I noticed some functions don't have return types - do we wanna just always add return types moving forward? (e.g. to_dict())

mrlevitas added a commit that referenced this pull request Apr 12, 2022
* use rust decider crate tag

* update readme & leave comment
@wuyiqicc wuyiqicc merged commit 8aaaed6 into develop Apr 18, 2022
@wuyiqicc wuyiqicc deleted the dynamic_config branch April 18, 2022 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

6 participants