-
Notifications
You must be signed in to change notification settings - Fork 0
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: obfuscated precomputed assignments #164
feat: obfuscated precomputed assignments #164
Conversation
…o-getobfuscatedprecomputedflag
…o-getobfuscatedprecomputedflag
…o-getobfuscatedprecomputedflag
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.
Will take a thorough read tomorrow 👍
… mainly be used by clients
// Convenience method that combines setters we need to make assignments work | ||
public setSubjectAndPrecomputedFlagsRequestParameters( | ||
parameters: PrecomputedFlagsRequestParameters, | ||
) { | ||
this.setPrecomputedFlagsRequestParameters(parameters); | ||
this.setSubjectData(parameters.precompute.subjectKey, parameters.precompute.subjectAttributes); |
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.
This should not be public.
It also seems to be doing a bad thing — allows reinterpreting precomputed configuration from another subject. It also does not reset the poller, so it would continue polling with the previous subject key/attributes
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.
made not public.
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.
What is an alternative approach to setting these instance variables in the client if this method is no longer public?
Re: the concern of a mismatch arising between a previously running poller and newly provided request parameters + subject data: I'm thinking that adding a this.requestPoller?.stop();
method at the top of this method would prevent that from happening
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.
Re: the concern of a mismatch arising between a previously running poller and newly provided request parameters + subject data: I'm thinking that adding a this.requestPoller?.stop(); method at the top of this method would prevent that from happening
It will not, because stop()
does not interrupt an in-progress request, so what could happen is:
- Client is initialized with one subject.
- Poller sends a request.
- Client is re-initialized with another subject.
- Previous request completes, and assigns data from previous subject to the store.
What is an alternative approach to setting these instance variables in the client if this method is no longer public?
Constructor. We shall prefer creating a new instance of the client in initialization instead of resetting the existing one. Resetting is problematic as it makes it impossible to use multiple clients simultaneously, and some data may still linger and intermix as in poller example above. (e.g., this is backfiring in our e2e testing effort, and afaik it is problematic for evaluation reasons on eppo portal)
Alternatively, JS/TS don't have much protection against calling typescript's private
methods. If we really need to call a method, it's easy to disable typescript protection (although not recommended).
} | ||
|
||
// Convenience method that combines setters we need to make assignments work | ||
public setSubjectSaltAndPrecomputedFlagStore( |
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.
Shall not be public
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.
done
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.
If setSubjectAndPrecomputedFlagStore
is to be made private
and setPrecomputedFlagStore
and setSubjectData
are also private
-- can you provide a suggestion for how the client should set and persist this information in the offlinePrecomputedInit
method in js-client-sdk
?
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.
Would it be preferable to delete setSubjectAndPrecomputedFlagStore
completely and make both setPrecomputedFlagStore
and setSubjectData
public? In my mind, I thought of these private individual setters as implementation details and the public setter as a convenient grouping of everything you need to get offline init to work
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 would rather have less public methods as all public methods are exposed to our users and become part of our API.
precomputedFlagStore: IConfigurationStore<PrecomputedFlag>; | ||
isObfuscated?: boolean; |
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 would also expect EppoPrecomputedClient
to receive sdk key, subject key, attributes, assignment logger, optional initial configuration, etc in constructor
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've added the request params object to the init function as I don't want to work the PrecomputedFlagsRequestParameters
and EppoPrecomputedClientOptions
types in this PR. We will have a chance to iterate on the API before releasing to the general public and have a need to punt some of this work in the very short term.
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.
The request parameters could be included in EppoPrecomputedClientOptions
. This would be consistent with how the regular client's constructor uses configurationRequestParameters
js-sdk-common/src/client/eppo-client.ts
Lines 129 to 138 in f036edc
configurationRequestParameters?: FlagConfigurationRequestParameters; | |
isObfuscated?: boolean; | |
}) { | |
this.eventDispatcher = eventDispatcher; | |
this.flagConfigurationStore = flagConfigurationStore; | |
this.banditVariationConfigurationStore = banditVariationConfigurationStore; | |
this.banditModelConfigurationStore = banditModelConfigurationStore; | |
this.configurationRequestParameters = configurationRequestParameters; | |
this.isObfuscated = isObfuscated; | |
} |
I opted against this pattern because the js-client-sdk doesn't actually use this field in the constructor and explicitly calls a instance.setConfigurationRequestParameters(requestConfiguration);
, which is the same approach I'd taken with using setSubjectAndPrecomputedFlagsRequestParameters
. If you don't intend to use an argument in a constructor, why even have the argument in the first place?
…o-getobfuscatedprecomputedflag
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.
approved but version bump needed before merging
public setIsObfuscated(isObfuscated: boolean) { | ||
this.isObfuscated = isObfuscated; |
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.
before we added the ability to parse whether a configuration is obfuscated from the configuration, when users needed to bootstrap the SDK in offline mode they need to be able to set this. the canonical example is the JS browser client (which by default is in obfuscated mode) getting a configuration from a nodejs server (which is not obfuscated).
that being said, I know there are customers using this method. removing this public method will require a major version bump please.
overall this is a more elegant solution to receive the obfuscation status from the configuration - users will need to upgrade both their nodejs and js browser SDKs to make sure that metadata is included.
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.
that being said, I know there are customers using this method. removing this public method will require a major version bump please.
This is a method on a PrecomputedEppoClient, not the regular one
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.
@rasendubi What is the purpose of providing this obfuscated
boolean field in the response (https://github.com/Eppo-exp/eppo-multiplatform/blob/5aadb2ed0572a96fbb999066c0191e949abd20f5/eppo_core/src/precomputed.rs#L83) if we're designing the SDKs to always assume that is it true
?
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.
if we're designing the SDKs to always assume that is it true?
That may change in the future. The flag is here to facilitate that change
package.json
Outdated
@@ -1,6 +1,6 @@ | |||
{ | |||
"name": "@eppo/js-client-sdk-common", | |||
"version": "4.7.0", | |||
"version": "4.7.0-alpha.1", |
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.
"version": "4.7.0-alpha.1", | |
"version": "5.0.0-alpha.1", |
removing some public methods that are used by customers (see below)
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.
See my other comment — we are not removing used methods. All changes are for the new precomputed client, not the regular one
…o-getobfuscatedprecomputedflag
Co-authored-by: Oleksii Shmalko <[email protected]>
Co-authored-by: Oleksii Shmalko <[email protected]>
if (!this.precomputedFlagStore.isInitialized()) { | ||
logger.error( | ||
'[Eppo SDK] EppoPrecomputedClient requires an initialized precomputedFlagStore if requestParameters are not provided', | ||
); | ||
} | ||
if (!this.precomputedFlagStore.salt) { | ||
logger.error( | ||
'[Eppo SDK] EppoPrecomputedClient requires a precomputedFlagStore with a salt if requestParameters are not provided', | ||
); | ||
} |
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.
Printing instead of throwing errors if the precomputed flag store is misconfigured because the SDK shouldn't disrupt the user's code, but also these scenarios should never happen, and if they do, assignments will always return the user-provided default
labels: mergeable
Fixes: #issue
Motivation and Context
Description
Updates de-obfuscation code to use a salt in hashing the flag key for finding precomputed flags. We expect this to work for both the regular init-ed client and offline init, since both will be obfuscated.
How has this been tested?