-
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: create offlinePrecomputedInit #117
feat: create offlinePrecomputedInit #117
Conversation
package.json
Outdated
@@ -59,7 +59,7 @@ | |||
"webpack-cli": "^4.10.0" | |||
}, | |||
"dependencies": { | |||
"@eppo/js-client-sdk-common": "^4.5.4" | |||
"@eppo/js-client-sdk-common": "^4.6.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.
Depends on this to be published Eppo-exp/js-sdk-common#163
* Configuration for Eppo precomputed client initialization | ||
* @public | ||
*/ | ||
export interface IPrecomputedClientConfig extends IBaseRequestConfig { |
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.
Breaking change to the interface of precomputedInit
but assuming it is safe because we are currently the only users of this function
Marking as ready for review because I expect the tests to pass once the js common dependency is published |
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.
Pieces coming together 💪
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 haven't reviewed this yet but since it has a ✅ please remember to test this build by compiled the production package.
Makefile
Outdated
@@ -27,14 +27,15 @@ help: Makefile | |||
testDataDir := test/data/ | |||
tempDir := ${testDataDir}temp/ | |||
gitDataDir := ${tempDir}sdk-test-data/ | |||
branchName := main | |||
branchName := sameeran/ff-3687-obfuscated-precomputed-json |
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.
Temporarily using this branch for testing until Eppo-exp/sdk-test-data#89 gets an approval 🙏
src/index.spec.ts
Outdated
let precomputedConfigurationWire: string; | ||
|
||
beforeAll(() => { | ||
precomputedConfigurationWire = readMockPrecomputedResponse(MOCK_PRECOMPUTED_RESPONSE_FILE); |
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.
Currently, tests use the unobfuscated, unstringified ConfigurationWire
format https://github.com/Eppo-exp/sdk-test-data/blob/8ee55117b7c7f5d02b2997629af0f0fa93e4e97c/configuration-wire/precomputed-v1.json
In a follow up PR I will add support for parsing json strings and handling obfuscated fields
src/index.ts
Outdated
export interface IConfigurationWire { | ||
version: number; | ||
precomputed: { | ||
subjectKey: string; | ||
subjectAttributes: Record<string, AttributeType>; | ||
fetchedAt: string; | ||
response: { | ||
createdAt: string; | ||
format: FormatEnum; | ||
obfuscated: boolean; | ||
environment: Environment; | ||
flags: Record<string, PrecomputedFlag>; | ||
}; | ||
}; | ||
} |
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 replace with an exported interface from Eppo-exp/js-sdk-common#160
src/index.spec.ts
Outdated
@@ -1205,7 +1214,7 @@ describe('EppoClient config', () => { | |||
const retryManager = eventDispatcher['retryManager']; | |||
const batchProcessor = eventDispatcher['batchProcessor']; | |||
expect(eventDispatcher['deliveryIntervalMs']).toEqual(1); | |||
expect(batchProcessor['batchSize']).toEqual(5); | |||
expect(batchProcessor['batchSize']).toEqual(100); |
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.
cc @felipecsl I had to change this because 5 is below the minimum allowed batch size
const MIN_BATCH_SIZE = 100;
const MAX_BATCH_SIZE = 10_000;
export default class BatchEventProcessor {
private readonly batchSize: number;
constructor(private readonly eventQueue: NamedEventQueue<Event>, batchSize: number) {
// clamp batch size between min and max
this.batchSize = Math.max(MIN_BATCH_SIZE, Math.min(MAX_BATCH_SIZE, batchSize));
}
package.json
Outdated
@@ -1,6 +1,6 @@ | |||
{ | |||
"name": "@eppo/js-client-sdk", | |||
"version": "3.8.3", | |||
"version": "3.9.0-alpha.0", |
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 edge compute endpoint is published with updated hashing
- The corresponding md5 hasher in js-client-sdk-common is updated https://github.com/Eppo-exp/js-sdk-common/pull/164/files#r1887291803 (
.appendBinary
-->.append
) - And 4.7.0-alpha.1 version of js-client-sdk-common has been released and the dependency in this file has been updated to match (also see: https://github.com/Eppo-exp/js-sdk-common/pull/164/files#r1887650834)
src/index.ts
Outdated
contextAttributes ?? { numericAttributes: {}, categoricalAttributes: {} }, | ||
); | ||
|
||
EppoPrecomputedJSClient.instance.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.
Based on these PR comments
Eppo-exp/js-sdk-common#164 (comment)
Eppo-exp/js-sdk-common#164 (comment)
I'll see if I can make this method private/not use it here and have these set through the constructor
src/index.ts
Outdated
const configurationWire: IConfigurationWire = JSON.parse(config.precomputedConfigurationWire); | ||
if (!configurationWire.precomputed) { | ||
applicationLogger.error('Invalid precomputed configuration wire'); | ||
return EppoPrecomputedJSClient.instance; | ||
} | ||
const { subjectKey, subjectAttributes, response } = configurationWire.precomputed; | ||
const parsedResponse: IPrecomputedConfigurationResponse = JSON.parse(response); |
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.
Is there any reason all this parsing is done in client library and not common?
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.
It's due to the approach taken. The same EppoPrecomputedClient
is used for online (via precomputedInit
) vs offline mode (via offlinePrecomputedInit
)
The EppoPrecomputedClient
's constructor knows which mode we're using based on the provided initialization options for the client instance. Both precomputedFlagStore
and subject
need to be accessible for initializing in the online mode: the user may want to select a specific storage option (e.g. chrome.storage
) for their use case and subject
is required to know what to precompute. We're reusing these same inputs for offline mode, hence the parsing.
I could add an option to pass in the configuration wire string to be parsed by the common library, which would overwrite the contents of precomputedFlagStore
, but making subject
optional or to overwrite it seems like doing a lot in EppoPrecomputedClient
's constructor.
subjectAttributes: subjectAttributes ?? ({} as Attributes), | ||
}; | ||
|
||
EppoPrecomputedJSClient.instance = new EppoPrecomputedJSClient({ |
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.
major: before potentially overwriting previous instance, we should shut it down. (and warn of re-initialization)
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 I'm unclear about what "shut it down" means here. The client is offline so polling/making requests isn't at play. Also the two instance variables being set by the constructor (subject
and precomputedFlagStore
) are private.
I could make these public so I can clear them here. Or I can add a public method to EppoPrecomputedClient
in the common sdk to clear these. I can't do it in the constructor because that only has access to the new instance, not the one being replaced.
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.
My attempt at a private shutdown function
function shutdownEppoPrecomputedClient() {
if (EppoPrecomputedJSClient.instance) {
EppoPrecomputedJSClient.instance.stopPolling();
EppoPrecomputedJSClient.initialized = false;
applicationLogger.warn(`${loggerPrefix} Precomputed client is being re-initialized.`);
}
}
The configuration store is a private variable in EppoPrecomputedClient
so I can't clear it here, nor does it come with any helpers to clear its contents. Also the store we use at the moment is memory-only, so I don't know if persisting across sessions is possible, e.g. if a page refresh is the reason for re-initializing
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 I'm unclear about what "shut it down" means here.
I mostly meant stopping polling. Most SDKs have a method for stopping all background activities.
The client is offline so polling/making requests isn't at play.
The fact that we're initializing offline doesn't mean that the previous client was in offline mode too
labels: mergeable
Fixes: #issue
Motivation and Context
Description
Adds a method for
offlinePrecomputedInit
Depends on Eppo-exp/js-sdk-common#163 because we need a way to persist the subject key and attributes without relying on request parameters, since the offline init doesn't make requests
How has this been tested?