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

feat(decision): User profile service #163

Merged
merged 6 commits into from
Nov 5, 2019
Merged

Conversation

mikeproeng37
Copy link
Contributor

@mikeproeng37 mikeproeng37 commented Oct 31, 2019

Summary

  • Introduces UserProfileService interface
  • Introduces a PersistingExperimentService class that wraps over the ExperimentBucketerService and performs UPS lookup and save
  • Integration into the CompositeExperimentService will be done in a follow-up PR

Copy link
Contributor

@mjc1283 mjc1283 left a comment

Choose a reason for hiding this comment

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

I like this approach, let's do it. Just some questions about how it's exposed via options.

Comment on lines 41 to 45
func WithUserProfileService(userProfileService UserProfileService) PESOptionFunc {
return func(f *PersistingExperimentService) {
f.userProfileService = userProfileService
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this optional? Seems like it should be required.

@@ -34,14 +34,16 @@ type CompositeExperimentService struct {

// NewCompositeExperimentService creates a new instance of the CompositeExperimentService
func NewCompositeExperimentService() *CompositeExperimentService {
persistingExperimentService := NewPersistingExperimentService(NewExperimentBucketerService())
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we only apply the persisting wrapper when they use an option func and actually provide a user profile service? Otherwise, most of the time, this persisting experiment service is running, but not doing anything interesting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah you are right. Let me fix that.

// GetDecision returns the decision with the variation the user is bucketed into
func (p PersistingExperimentService) GetDecision(decisionContext ExperimentDecisionContext, userContext entities.UserContext) (ExperimentDecision, error) {
var experimentDecision ExperimentDecision
var err error
Copy link
Contributor

Choose a reason for hiding this comment

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

var experimentDecision ExperimentDecision
var err error -- these could be defined on the return level

@mikeproeng37 mikeproeng37 marked this pull request as ready for review November 4, 2019 22:43
@mikeproeng37 mikeproeng37 requested a review from a team as a code owner November 4, 2019 22:43
@mikeproeng37 mikeproeng37 changed the title feat(decision): User profile service integration. feat(decision): User profile service Nov 4, 2019
Copy link
Contributor

@mjc1283 mjc1283 left a comment

Choose a reason for hiding this comment

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

The implementation of PersistingExperimentService looks good. I just have a few questions about the UserProfileService interface.

Comment on lines 68 to 70
type UserDecisionKey struct {
ExperimentID string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why use a struct here, as opposed to just a string?

Copy link
Contributor Author

@mikeproeng37 mikeproeng37 Nov 5, 2019

Choose a reason for hiding this comment

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

Good catch. In our current user profile service implementation we use a combination of experimentID and the key variation_id (which I forgot to add in this PR). I don't remember why we decided to make that value a map instead of pointing from experimentID to variationID and I added the struct so we could maintain that relationship and maintain consistency with the other SDKs.


// UserProfileService is used to save and retrieve past bucketing decisions for users
type UserProfileService interface {
Lookup(string) (UserProfile, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need Lookup to return an error? The SDK internals don't have to care whether there was an error - that seems more like a concern of the implementation. Whoever implements this could track and handle errors on their end. Do they really need to route an error through the SDK logger like that?

From the SDK's perspective, it just needs a user profile for a given user id. We could require that a UserProfile is always returned, and say that when there is no saved decision for that user id (for whatever reason, error or not), then the implementation should return a UserProfile containing an empty ExperimentBucketMap.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point. I'll remove the errors since we don't actually care about them.

// UserProfileService is used to save and retrieve past bucketing decisions for users
type UserProfileService interface {
Lookup(string) (UserProfile, error)
Save(UserProfile) error
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment above applies to returning an error here. Why does the SDK care?

@mikeproeng37 mikeproeng37 merged commit 168b57c into master Nov 5, 2019
@mikeproeng37 mikeproeng37 deleted the mng/user-profile-service branch November 5, 2019 16:53
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.

4 participants