Skip to content

Instantly share code, notes, and snippets.

@jordangarcia
Created October 23, 2018 18:55
Show Gist options
  • Save jordangarcia/e8afafde75f8efa4081a3e7b137d444f to your computer and use it in GitHub Desktop.
Save jordangarcia/e8afafde75f8efa4081a3e7b137d444f to your computer and use it in GitHub Desktop.

NodeJS Sticky Bucketing

The problem as it exists today is that in order to do sticky bucketing the userProfileService implements two async methods lookup(userId) and save(userProfile). However the implementation of lookup is handled synchronously and thus won’t allow any asynchronous lookups when doing any sort of bucketing decision.

There are many good reasons to want to keep the javascript-sdk synchronous. By making all of the activate calls async we will create a large API breaking change that will force all customers to use an async approach to our SDK, this is bad.

Proposed Solution

Create separate async versions of all bucketing and tracking functions that rely on userProfileService.lookup and suffix them with WithUserProfileService

  • activate -> activateWithUserProfileService
  • getVariation -> getVariationWithUserProfileService
  • Etc…

These functions will accept a callback as the last argument and will call the their synchronous counterparts under the hood.

activateWithUserProfileService psuedo-code

var userProfileService = {
	lookup(userId, cb) {
   	someAsyncLookup(userId).then(userProfile => {
			cb(null, userProfile)
		}
	},
	save(userProfile) {
		someSaveCall(userProfile)
	}
}

var clientInstance = optimizelySDK.createInstance({
	datafile,
	userProfileService,
})

client.activateWithUserProfileService(
	'checkout_test',
	'userId',
	{},
	function(err, variationKey) {
		console.log('got variationKey', variationKey)
  }
)

experimentBucketMap as user attribute

The easiest way to implement this change is to allow a special key experiment_bucket_map to be passed into attributes if any of the bucketing functions see this, they will use this as the source of truth for sticky bucketing. This is how the activateWithUserProfileService function works internally.

Touch Points

Where does sticky bucketing (userProfile service) need to come into play with the SDK

Bucketing

activate getVariation isFeatureEnabled getEnabledFeatures getFeatureVariableBoolean getFeatureVariablDouble getFeatureVariableInteger getFeatureVariableString

Tracking

track

Other Solutions

  1. Make activate call asynchronous if there is a userProfile in place
  2. Allow activate to be passed a fourth argument that is the experimentBucketMap
  3. Allow the passing of a special attributes experimentBucketMap

Reference

ExperimentBucketMap

{
  <expId>: {
		variation_id: <varId>
	}
}

#work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment