-
Notifications
You must be signed in to change notification settings - Fork 85
Description
How would the enhancement work?
Reduce the number of times that the OptimizelyConfig's instantiation iterates over the datafile's feature flag list.
Current State Summary
- The OptimizelyConfig instance iterates over each feature flag in a datafile when the constructor calls
OptimizelyConfig.getFeaturesMap - getDeliveryRules is called during each iteration, which in turn calls getVariableIdMap
getVariableIdMapiterates over each feature flag and each feature flag's variables
That is, each feature flag iteration in getFeaturesMap incurs another complete iteration of the feature flags when creating the variablesIdMap.
Due to the nested loop, creating an OptimizelyConfig instance with a hypothetical datafile containing 100 feature flags iterates over the entire feature flag list over 10,000 times.
👈 For reference, here's the complete logic flow
1. Constructor calls OptimizelyConfig.getFeaturesMap
| this.featuresMap = OptimizelyConfig.getFeaturesMap(configObj, featureIdVariablesMap, experimentsMapById); |
2. getFeaturesMap iterates over each feature flag
javascript-sdk/packages/optimizely-sdk/lib/core/optimizely_config/index.ts
Lines 399 to 405 in 2580ea0
| static getFeaturesMap( | |
| configObj: ProjectConfig, | |
| featureVariableIdMap: FeatureVariablesMap, | |
| experimentsMapById: OptimizelyExperimentsMap | |
| ): OptimizelyFeaturesMap { | |
| const featuresMap: OptimizelyFeaturesMap = {}; | |
| configObj.featureFlags.forEach((featureFlag) => { |
3. OptimizelyConfig.getDeliveryRules is called during each feature flag iteration
javascript-sdk/packages/optimizely-sdk/lib/core/optimizely_config/index.ts
Lines 425 to 427 in 2580ea0
| const rollout = configObj.rolloutIdMap[featureFlag.rolloutId]; | |
| if (rollout) { | |
| deliveryRules = OptimizelyConfig.getDeliveryRules( |
4. getDeliveryRules calls OptimizelyConfig.getVariableIdMap
| const variableIdMap = OptimizelyConfig.getVariableIdMap(configObj); |
5. getVariableIdMap iterates over all of the feature flags again & each of the feature flag's variables
javascript-sdk/packages/optimizely-sdk/lib/core/optimizely_config/index.ts
Lines 281 to 291 in 2580ea0
| static getVariableIdMap(configObj: ProjectConfig): { [id: string]: FeatureVariable } { | |
| let variablesIdMap: { [id: string]: FeatureVariable } = {}; | |
| variablesIdMap = (configObj.featureFlags || []).reduce((resultMap: { [id: string]: FeatureVariable }, feature) => { | |
| feature.variables.forEach((variable) => { | |
| resultMap[variable.id] = variable; | |
| }); | |
| return resultMap; | |
| }, {}); | |
| return variablesIdMap; | |
| } |
Proposed State
My familiarity with the optimizely-sdk's internals is limited. Based on this narrow understanding, I see a couple of ways to reduce the number of times that the OptimizelyConfig iterates over the feature flag list.
👈 One option - Create a single variablesIdMap in the constructor
The constructor iterates over the features flags to generate the featureIdVariablesMap. This reducer's callback could be extended to simultaneously create a single variablesIdMap. The variablesIdMap can then be passed to the getFeaturesMap call, considerably reduceing the variablesIdMap function's overhead.
javascript-sdk/packages/optimizely-sdk/lib/core/optimizely_config/index.ts
Lines 72 to 75 in 2580ea0
| const featureIdVariablesMap = (configObj.featureFlags || []).reduce((resultMap: FeatureVariablesMap, feature) => { | |
| resultMap[feature.id] = feature.variables; | |
| return resultMap; | |
| }, {}); |
👈 Another option - Use the featureVariableIdMap to pass a specific feature's variables directly to getVariableIdMap
As getDeliveryRules has both the featureVariableIdMap and featureId, the feature's variables can be passed directly to getVariableIdMap. This removes the need to iterate over the entire feature flag list.
javascript-sdk/packages/optimizely-sdk/lib/core/optimizely_config/index.ts
Lines 301 to 304 in 2580ea0
| static getDeliveryRules( | |
| configObj: ProjectConfig, | |
| featureVariableIdMap: FeatureVariablesMap, | |
| featureId: string, |
This enhancement might look something like,
static getDeliveryRules(
configObj: ProjectConfig,
featureVariableIdMap: FeatureVariablesMap,
featureId: string,
experiments: Experiment[]
): OptimizelyExperiment[] {
// Call `getVariableIdMap` with `featureVariableIdMap[featureId]`, an array of the feature's variables
const variableIdMap = OptimizelyConfig.getVariableIdMap(featureVariableIdMap[featureId]); static getVariableIdMap(featureVariables: FeatureVariable[]): { [id: string]: FeatureVariable } {
let variablesIdMap: { [id: string]: FeatureVariable } = {};
// Iterate over the `featureVariables` rather than the entire datafile's feature flag list
variablesIdMap = (featureVariables || []).reduce((resultMap: { [id: string]: FeatureVariable }, variable) => {
resultMap[variable.id] = variable;
return resultMap;
}, {});
return variablesIdMap;
}When would the enhancement be useful?
This is a useful performance enhancement for all @optimizely/javascript-sdk users, but particularly those with a large number of feature flags in their datafile.