Skip to content

Commit

Permalink
Merge pull request #4543 from EdgeApp/jon/fix/experiment-config-init
Browse files Browse the repository at this point in the history
Jon/fix/experiment-config-init
  • Loading branch information
Jon-edge authored Oct 25, 2023
2 parents f0cfad4 + ad8fdb9 commit 83cbf92
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 18 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- changed: Change FlipInput styling to make the edit functionality more obvious
- changed: Enable max spend for Filecoin
- changed: Move asset-specific settings into their own settings page
- changed: Experiment config probability distribution support percentage based values
- fixed: Write updated experiment configs to disk
- removed: Moonpay sell via ACH
- removed: Banxa buy via Pix

Expand Down
49 changes: 31 additions & 18 deletions src/experimentConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,45 @@ const DEFAULT_EXPERIMENT_CONFIG: ExperimentConfig = {

const experimentConfigDisklet = makeReactNativeDisklet()

// The probability (0-1) of a feature config being set to the first value(s):
// the configuration that differs from the default feature configuration.
// The probability of an experiment config feature being set for a given key
const experimentDistribution = {
swipeLastUsp: [0.5],
createAccountType: [0.5],
legacyLanding: [0],
createAccountText: [0.33, 0.33],
signupCaptcha: [0.5]
swipeLastUsp: [50, 50],
createAccountType: [50, 50],
legacyLanding: [100],
createAccountText: [33.33, 33.33, 33.33],
signupCaptcha: [50, 50]
}

/**
* Generate a random index value according to the experiment distribution to
* determine which variant gets used.
*/
const generateExperimentConfigVal = <T>(key: keyof typeof experimentDistribution, configVals: T[]): T => {
const variantProbability = experimentDistribution[key]
const variantNominations = experimentDistribution[key]

if (variantProbability.length !== configVals.length - 1) {
if (variantNominations.length !== configVals.length) {
console.error(`Misconfigured experimentDistribution for: '${key}'`)
} else {
// Distribute the probability of each config value
const variantDenomination = variantNominations.reduce((sum, probability) => sum + probability, 0)
if (variantDenomination === 0) {
throw new Error(`Config values for '${key}' do not add up to 100%`)
} else if (variantDenomination > 101 || variantDenomination < 99) {
console.warn(`Config values for '${key}' do not add up to 100% +/- 1%`)
}
const distributedProbabilities = variantNominations.map(variantNomination => variantNomination / variantDenomination)

// Generate a random number between 0 and 1
const random = Math.random()

// Check which index the random number falls into and return the configVal:
let lowerBound = 0
for (let i = 0; i < variantProbability.length; i++) {
if (random >= lowerBound && random < variantProbability[i]) return configVals[i]
lowerBound += variantProbability[i]
let upperBound = distributedProbabilities[0]
for (let i = 0; i < distributedProbabilities.length; i++) {
if (random >= lowerBound && random < upperBound) return configVals[i]

lowerBound = upperBound
upperBound += distributedProbabilities[i]
}
}

Expand Down Expand Up @@ -81,17 +92,19 @@ const asExperimentConfig: Cleaner<ExperimentConfig> = asObject({
* This config value is available through the module's getter functions.
*/
const experimentConfigPromise: Promise<ExperimentConfig> = (async (): Promise<ExperimentConfig> => {
let currentConfig: ExperimentConfig
try {
const experimentConfigJson = await experimentConfigDisklet.getText(LOCAL_EXPERIMENT_CONFIG)
return asExperimentConfig(JSON.parse(experimentConfigJson))
currentConfig = asExperimentConfig(JSON.parse(experimentConfigJson))
} catch (err) {
console.debug('Experiment config not found/out of date. Regenerating...')
console.log('Experiment config not found/out of date. Regenerating...')
// Not found or incompatible. Re-generate with random values according to
// the defined distribution.
const generatedExperimentConfig = asExperimentConfig({})
await experimentConfigDisklet.setText(LOCAL_EXPERIMENT_CONFIG, JSON.stringify(generatedExperimentConfig))
return generatedExperimentConfig
currentConfig = asExperimentConfig({})
}

await experimentConfigDisklet.setText(LOCAL_EXPERIMENT_CONFIG, JSON.stringify(currentConfig))
return currentConfig
})()

/**
Expand All @@ -106,7 +119,7 @@ export const getExperimentConfig = async (): Promise<ExperimentConfig> => {
if (isMaestro()) return DEFAULT_EXPERIMENT_CONFIG // Test with forced defaults
else if (ENV.EXPERIMENT_CONFIG_OVERRIDE != null && Object.keys(ENV.EXPERIMENT_CONFIG_OVERRIDE).length > 0) {
try {
console.debug('exp cfg override')
console.log('ENV.EXPERIMENT_CONFIG_OVERRIDE set')
return asExperimentConfig(ENV.EXPERIMENT_CONFIG_OVERRIDE)
} catch (err) {
console.error('Error applying ENV.EXPERIMENT_CONFIG_OVERRIDE: ', String(err))
Expand Down

0 comments on commit 83cbf92

Please sign in to comment.