Explorar el Código

Merge pull request #14736 from overleaf/jpa-split-test-optimizations

[web] split test optimizations

GitOrigin-RevId: 97aa6c5aaad7f2dbb2f2fe9da33812857982996a
Jakob Ackermann hace 3 años
padre
commit
d19090e398
Se han modificado 1 ficheros con 53 adiciones y 10 borrados
  1. 53 10
      services/web/app/src/Features/SplitTests/SplitTestHandler.js

+ 53 - 10
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,