-
Couldn't load subscription status.
- Fork 32
[FSSDK-11170] update: decision service methods for cmab #583
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
base: master
Are you sure you want to change the base?
[FSSDK-11170] update: decision service methods for cmab #583
Conversation
…e to support CMAB UUID handling
…and include CMAB UUIDs in responses
…ils for CMAB configuration
…ations over CMAB service decisions in DecisionService
…tly verify error state
…izely and DecisionService
…text and related fetcher classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look good to me. Added few comments.
| private String cmabUUID; | ||
|
|
||
| public DecisionResponse(@Nullable T result, @Nonnull DecisionReasons reasons) { | ||
| public DecisionResponse(@Nullable T result, @Nonnull DecisionReasons reasons, @Nonnull boolean error, @Nullable String cmabUUID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it impact the existing user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, existing users can still use this class as they were. In that case cmabUUID will be set null and error will be false. Class behaviour will be same.
core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/optimizelydecision/AsyncDecisionsFetcher.java
Outdated
Show resolved
Hide resolved
…ecision responses
…ent key retrieval
…eation in CMAB client
…hance decision handling
core-api/src/main/java/com/optimizely/ab/cmab/client/CmabClientHelper.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few changes in API definitions. Can you refactor them first?
I need more time to review the cmab logic.
| String flagKey = flagsWithoutForcedDecision.get(i).getKey(); | ||
|
|
||
| if (error) { | ||
| OptimizelyDecision optimizelyDecision = OptimizelyDecision.newErrorDecision(flagKey, user, DecisionMessage.CMAB_ERROR.reason(experimentKey)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we always report CMAB error for any decision errors? Is this safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand, we get error from decision service only when cmab fails. So this error flag is only true for cmab errors. @raju-opti can verify.
| @Nonnull List<String> keys, | ||
| @Nonnull List<OptimizelyDecideOption> options, | ||
| boolean ignoreDefaultOptions) { | ||
| Map<String, OptimizelyDecision> decisionMap = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have full separate copies of decideForKeys and decideForKeysSync
It won't be easy to maintain these identical copies with small changes.
Can we consider refactor decideForKeysSync to reuse decideForKeys (or the other way). We can pass "needAsync=True" param to skip async ops (including CMAB).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for decideSync and decideAllSync?
core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java
Outdated
Show resolved
Hide resolved
| @Nullable UserProfileTracker userProfileTracker, | ||
| @Nullable DecisionReasons reasons) { | ||
| @Nullable DecisionReasons reasons, | ||
| @Nonnull boolean useCmab) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we change this param useCmab to needAsync (or similar)?
I understand the purpose of this is differentiate sync vs async not specific for cmab.
We can keep more it more generatic for future extension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have three cases:
- Sync + useCmab (java default)
- Async + useCmab (for android)
- Sync + witoutCmab (for android)
Using needAsync will be confusing in that case I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useCmab is for differentiating flow in sync path only. Not related to async.
| if (decisionMeetAudience.getResult()) { | ||
| String bucketingId = getBucketingId(user.getUserId(), user.getAttributes()); | ||
| String cmabUUID = null; | ||
| if (useCmab && isCmabExperiment(experiment)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can consider to bring useCmab (needAsync flag) to decouple from cmab so can use to cover other async ops too.
core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java
Outdated
Show resolved
Hide resolved
core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java
Outdated
Show resolved
Hide resolved
… backward compatibility for mobile apps
… decisions in OptimizelyUserContext
…rContextTest for consistency
…od for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more suggestions at the top level.
| @Nonnull List<OptimizelyDecideOption> options, | ||
| boolean ignoreDefaultOptions) { | ||
| boolean ignoreDefaultOptions, | ||
| boolean useCmab) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still like to change this useCmab to more generic name to async. Let's discuss :)
| * @param options A list of options for decision-making. | ||
| * @return A decision result. | ||
| */ | ||
| public OptimizelyDecision decideSync(@Nonnull String key, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we better remove this and all "sync" functions from OptimizelyUserContext.
These are only for android-sdk, so must be hidden from java-sdk users.
Can we change "private final Optimizely optimizely;" in line 53 to "final Optimizely optimizely;" so android-sdk can directly access "optimizely.decideSync()"?
| * @param options A list of options for decision-making. | ||
| * @return All decision results mapped by flag keys. | ||
| */ | ||
| public Map<String, OptimizelyDecision> decideForKeysSync(@Nonnull List<String> keys, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same - we should remove this.
| * @param options A list of options for decision-making. | ||
| * @return All decision results mapped by flag keys. | ||
| */ | ||
| public Map<String, OptimizelyDecision> decideAllSync(@Nonnull List<OptimizelyDecideOption> options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same - we should remove this too.
| int DEFAULT_MAX_SIZE = 1000; | ||
| int DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 * 1000; | ||
| DefaultLRUCache<CmabCacheValue> cmabCache = new DefaultLRUCache<>(DEFAULT_MAX_SIZE, DEFAULT_CMAB_CACHE_TIMEOUT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| int DEFAULT_MAX_SIZE = 1000; | |
| int DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 * 1000; | |
| DefaultLRUCache<CmabCacheValue> cmabCache = new DefaultLRUCache<>(DEFAULT_MAX_SIZE, DEFAULT_CMAB_CACHE_TIMEOUT); | |
| int DEFAULT_CMAB_CACHE_TIMEOUT_IN_SECS = 30 * 60; | |
| `` | |
| I believe DefaultLRUCache accepts timeout in secs not in millisecs. Can we add a test case for DefaultLRUCache to validate this error? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand cmabCacheSize and cmabCacheTimeout not configurable for now?
| int DEFAULT_CMAB_CACHE_TIMEOUT = 30 * 60 * 1000; | ||
| DefaultLRUCache<CmabCacheValue> cmabCache = new DefaultLRUCache<>(DEFAULT_MAX_SIZE, DEFAULT_CMAB_CACHE_TIMEOUT); | ||
| CmabServiceOptions cmabServiceOptions = new CmabServiceOptions(logger, cmabCache, cmabClient); | ||
| DefaultCmabService defaultCmabService = new DefaultCmabService(cmabServiceOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cmabClient should not be an "option"?
We can consider moving it to DefaultCmabService(cmabClient, cmabServiceOptions).
Co-authored-by: Jae Kim <[email protected]>
| return this; | ||
| } | ||
|
|
||
| public Builder withCmabClient(CmabClient cmabClient) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FarhanAnjum-opti It looks like we need to build withCmabService(), so others like android-sdk can inject full-featured CmabService with client + cacheSize + cacheTTL.
We can provide Builder pattern to CmabService like OdpManager -
CmabServiceBuilder() .withCmabCacheSize() .withCmabCachTimeoutInSecs() .withClient() .build()
…larity and maintainability
Co-authored-by: Jae Kim <[email protected]>
Co-authored-by: Jae Kim <[email protected]>
…ckage-private and add copyright notice to DecisionPath
Summary
Decision Service methods to handle CMAB
Test plan
Added unit tests
Issues
FSSDK-11170