|
|
@@ -17,6 +17,7 @@ const Settings = require('@overleaf/settings')
|
|
|
const DEFAULT_VARIANT = 'default'
|
|
|
const ALPHA_PHASE = 'alpha'
|
|
|
const BETA_PHASE = 'beta'
|
|
|
+const CACHE_TOMBSTONE_SPLIT_TEST_NOT_ACTIVE_FOR_USER = null
|
|
|
const DEFAULT_ASSIGNMENT = {
|
|
|
variant: DEFAULT_VARIANT,
|
|
|
analytics: {
|
|
|
@@ -224,6 +225,10 @@ async function _getAssignment(
|
|
|
splitTest.name,
|
|
|
currentVersion
|
|
|
)
|
|
|
+ if (cachedVariant === CACHE_TOMBSTONE_SPLIT_TEST_NOT_ACTIVE_FOR_USER) {
|
|
|
+ Metrics.inc('split_test_get_assignment_source', 1, { status: 'cache' })
|
|
|
+ return DEFAULT_ASSIGNMENT
|
|
|
+ }
|
|
|
if (cachedVariant) {
|
|
|
Metrics.inc('split_test_get_assignment_source', 1, { status: 'cache' })
|
|
|
return _makeAssignment(splitTest, cachedVariant, currentVersion)
|
|
|
@@ -238,9 +243,18 @@ async function _getAssignment(
|
|
|
Metrics.inc('split_test_get_assignment_source', 1, { status: 'none' })
|
|
|
}
|
|
|
|
|
|
- user = user || (userId && (await _getUser(userId)))
|
|
|
+ user = user || (userId && (await _getUser(userId, splitTestName)))
|
|
|
const { activeForUser, selectedVariantName, phase, versionNumber } =
|
|
|
await _getAssignmentMetadata(analyticsId, user, splitTest)
|
|
|
+ if (session) {
|
|
|
+ _setVariantInSession({
|
|
|
+ session,
|
|
|
+ splitTestName,
|
|
|
+ currentVersion,
|
|
|
+ selectedVariantName,
|
|
|
+ activeForUser,
|
|
|
+ })
|
|
|
+ }
|
|
|
if (activeForUser) {
|
|
|
const assignmentConfig = {
|
|
|
user,
|
|
|
@@ -329,7 +343,7 @@ async function _updateVariantAssignment({
|
|
|
}
|
|
|
// if the user is logged in
|
|
|
if (userId) {
|
|
|
- user = user || (await _getUser(userId))
|
|
|
+ user = user || (await _getUser(userId, splitTestName))
|
|
|
if (user) {
|
|
|
const assignedSplitTests = user.splitTests || []
|
|
|
const assignmentLog = assignedSplitTests[splitTestName] || []
|
|
|
@@ -387,23 +401,52 @@ function _makeAssignment(splitTest, variant, currentVersion) {
|
|
|
function _getCachedVariantFromSession(session, splitTestName, currentVersion) {
|
|
|
if (!session.cachedSplitTestAssignments) {
|
|
|
session.cachedSplitTestAssignments = {}
|
|
|
- return
|
|
|
}
|
|
|
const cacheKey = `${splitTestName}-${currentVersion.versionNumber}`
|
|
|
- if (currentVersion.active) {
|
|
|
- return session.cachedSplitTestAssignments[cacheKey]
|
|
|
+ return session.cachedSplitTestAssignments[cacheKey]
|
|
|
+}
|
|
|
+
|
|
|
+function _setVariantInSession({
|
|
|
+ session,
|
|
|
+ splitTestName,
|
|
|
+ currentVersion,
|
|
|
+ selectedVariantName,
|
|
|
+ activeForUser,
|
|
|
+}) {
|
|
|
+ if (!session.cachedSplitTestAssignments) {
|
|
|
+ session.cachedSplitTestAssignments = {}
|
|
|
+ }
|
|
|
+
|
|
|
+ // clean up previous entries from this split test
|
|
|
+ for (const cacheKey of Object.keys(session.cachedSplitTestAssignments)) {
|
|
|
+ // drop '-versionNumber'
|
|
|
+ const name = cacheKey.split('-').slice(0, -1).join('-')
|
|
|
+ if (name === splitTestName) {
|
|
|
+ delete session.cachedSplitTestAssignments[cacheKey]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const cacheKey = `${splitTestName}-${currentVersion.versionNumber}`
|
|
|
+ if (activeForUser) {
|
|
|
+ session.cachedSplitTestAssignments[cacheKey] = selectedVariantName
|
|
|
} else {
|
|
|
- delete session.cachedSplitTestAssignments[cacheKey]
|
|
|
+ session.cachedSplitTestAssignments[cacheKey] =
|
|
|
+ CACHE_TOMBSTONE_SPLIT_TEST_NOT_ACTIVE_FOR_USER
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-async function _getUser(id) {
|
|
|
- const user = await UserGetter.promises.getUser(id, {
|
|
|
+async function _getUser(id, splitTestName) {
|
|
|
+ const projection = {
|
|
|
analyticsId: 1,
|
|
|
- splitTests: 1,
|
|
|
alphaProgram: 1,
|
|
|
betaProgram: 1,
|
|
|
- })
|
|
|
+ }
|
|
|
+ if (splitTestName) {
|
|
|
+ projection[`splitTests.${splitTestName}`] = 1
|
|
|
+ } else {
|
|
|
+ projection.splitTests = 1
|
|
|
+ }
|
|
|
+ const user = await UserGetter.promises.getUser(id, projection)
|
|
|
Metrics.histogram(
|
|
|
'split_test_get_user_from_mongo_size',
|
|
|
JSON.stringify(user).length,
|