diff --git a/services/web/app/src/Features/SplitTests/SplitTestHandler.js b/services/web/app/src/Features/SplitTests/SplitTestHandler.js index 91181a261f..a20895ebab 100644 --- a/services/web/app/src/Features/SplitTests/SplitTestHandler.js +++ b/services/web/app/src/Features/SplitTests/SplitTestHandler.js @@ -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,