Răsfoiți Sursa

Merge pull request #14836 from overleaf/ab-split-test-cache-refactoring

[web] Fetch all active split test into cache at once

GitOrigin-RevId: b477b88bf281349433af2cf692a0e9ea5b036588
Alexandre Bourdin 2 ani în urmă
părinte
comite
b8a5eca1d0

+ 6 - 9
services/web/app/src/Features/SplitTests/SplitTestCache.js

@@ -9,15 +9,12 @@ class SplitTestCache extends CacheLoader {
     })
   }
 
-  async load(name) {
-    Metrics.inc('split_test_get_split_test_from_mongo', 1, {
-      path: name,
-    })
-    const splitTest = await SplitTestManager.getSplitTest({
-      name,
-      archived: { $ne: true },
-    })
-    return splitTest?.toObject()
+  async load() {
+    Metrics.inc('split_test_get_split_test_from_mongo', 1, {})
+    const splitTests = await SplitTestManager.getRuntimeTests()
+    return new Map(
+      splitTests.map(splitTest => [splitTest.name, splitTest.toObject()])
+    )
   }
 
   serialize(value) {

+ 14 - 4
services/web/app/src/Features/SplitTests/SplitTestHandler.js

@@ -225,7 +225,7 @@ async function sessionMaintenance(req, user) {
  * @private
  */
 async function _getVariantNames(splitTestName) {
-  const splitTest = await SplitTestCache.get(splitTestName)
+  const splitTest = await _getSplitTest(splitTestName)
   const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
   if (currentVersion?.active) {
     return currentVersion.variants.map(v => v.name).concat([DEFAULT_VARIANT])
@@ -242,7 +242,7 @@ async function _getAssignment(
     return DEFAULT_ASSIGNMENT
   }
 
-  const splitTest = await SplitTestCache.get(splitTestName)
+  const splitTest = await _getSplitTest(splitTestName)
   const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
   if (!currentVersion?.active) {
     return DEFAULT_ASSIGNMENT
@@ -493,9 +493,14 @@ async function _getUser(id, splitTestName) {
 }
 
 async function _loadSplitTestInfoInLocals(locals, splitTestName) {
-  const splitTest = await SplitTestCache.get(splitTestName)
+  const splitTest = await _getSplitTest(splitTestName)
   if (splitTest) {
-    const phase = SplitTestUtils.getCurrentVersion(splitTest).phase
+    const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
+    if (!currentVersion.active) {
+      return
+    }
+
+    const phase = currentVersion.phase
     LocalsHelper.setSplitTestInfo(locals, splitTestName, {
       phase,
       badgeInfo: splitTest.badgeInfo?.[phase],
@@ -538,6 +543,11 @@ function _collectSessionStats(session) {
   }
 }
 
+async function _getSplitTest(name) {
+  const splitTests = await SplitTestCache.get('')
+  return splitTests?.get(name)
+}
+
 module.exports = {
   getPercentile,
   getAssignment: callbackify(getAssignment),

+ 11 - 0
services/web/app/src/Features/SplitTests/SplitTestManager.js

@@ -57,6 +57,16 @@ async function getSplitTests({ name, phase, type, active, archived }) {
   }
 }
 
+async function getRuntimeTests() {
+  try {
+    return await SplitTest.find({
+      archived: { $ne: true },
+    }).exec()
+  } catch (error) {
+    throw OError.tag(error, 'Failed to get active split tests list')
+  }
+}
+
 async function getSplitTest(query) {
   try {
     return await SplitTest.findOne(query)
@@ -439,6 +449,7 @@ function _mergeFlags(incomingTests, baseTests) {
 module.exports = {
   getSplitTest,
   getSplitTests,
+  getRuntimeTests,
   createSplitTest,
   updateSplitTestConfig,
   updateSplitTestInfo,

+ 29 - 20
services/web/test/unit/src/SplitTests/SplitTestHandlerTests.js

@@ -22,6 +22,10 @@ describe('SplitTestHandler', function () {
         versionNumber: 2,
       }),
     ]
+    this.cachedSplitTests = new Map()
+    for (const splitTest of this.splitTests) {
+      this.cachedSplitTests.set(splitTest.name, splitTest)
+    }
 
     this.UserGetter = {
       promises: {
@@ -36,11 +40,9 @@ describe('SplitTestHandler', function () {
     }
 
     this.SplitTestCache = {
-      get: sinon.stub().resolves(null),
-    }
-    for (const splitTest of this.splitTests) {
-      this.SplitTestCache.get.withArgs(splitTest.name).resolves(splitTest)
+      get: sinon.stub().resolves({}),
     }
+    this.SplitTestCache.get.resolves(this.cachedSplitTests)
     this.Settings = {
       moduleImportSequence: [],
       overleaf: {},
@@ -203,22 +205,29 @@ describe('SplitTestHandler', function () {
       this.AnalyticsManager.getIdsFromSession.returns({
         userId: 'abc123abc123',
       })
-      this.SplitTestCache.get.returns({
-        name: 'my-test-name',
-        versions: [
-          {
-            versionNumber: 0,
-            active: true,
-            variants: [
-              {
-                name: '100-percent-variant',
-                rolloutPercent: 100,
-                rolloutStripes: [{ start: 0, end: 100 }],
-              },
-            ],
-          },
-        ],
-      })
+      this.SplitTestCache.get.resolves(
+        new Map([
+          [
+            'my-test-name',
+            {
+              name: 'my-test-name',
+              versions: [
+                {
+                  versionNumber: 0,
+                  active: true,
+                  variants: [
+                    {
+                      name: '100-percent-variant',
+                      rolloutPercent: 100,
+                      rolloutStripes: [{ start: 0, end: 100 }],
+                    },
+                  ],
+                },
+              ],
+            },
+          ],
+        ])
+      )
 
       const assignment = await this.SplitTestHandler.promises.getAssignment(
         this.req,