Merge pull request #10266 from overleaf/ab-split-test-no-serialization

[web] Skip mongoose object transformations for the split test cache

GitOrigin-RevId: 8fb5420f6f938c0ab7cfe1ca82c107c7ce3522ca
This commit is contained in:
Jakob Ackermann 2022-11-02 15:34:01 +00:00 committed by Copybot
parent 28c1cc2ea9
commit 10c6bd20ab
6 changed files with 57 additions and 46 deletions

View file

@ -1,5 +1,4 @@
const SplitTestManager = require('./SplitTestManager') const SplitTestManager = require('./SplitTestManager')
const { SplitTest } = require('../../models/SplitTest')
const { CacheLoader } = require('cache-flow') const { CacheLoader } = require('cache-flow')
class SplitTestCache extends CacheLoader { class SplitTestCache extends CacheLoader {
@ -10,18 +9,19 @@ class SplitTestCache extends CacheLoader {
} }
async load(name) { async load(name) {
return await SplitTestManager.getSplitTest({ const splitTest = await SplitTestManager.getSplitTest({
name, name,
archived: { $ne: true }, archived: { $ne: true },
}) })
return splitTest?.toObject()
} }
serialize(value) { serialize(value) {
return value ? value.toObject() : undefined return value
} }
deserialize(value) { deserialize(value) {
return new SplitTest(value) return value
} }
} }

View file

@ -10,6 +10,7 @@ const { SplitTest } = require('../../models/SplitTest')
const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache') const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache')
const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper') const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper')
const Features = require('../../infrastructure/Features') const Features = require('../../infrastructure/Features')
const SplitTestUtils = require('./SplitTestUtils')
const DEFAULT_VARIANT = 'default' const DEFAULT_VARIANT = 'default'
const ALPHA_PHASE = 'alpha' const ALPHA_PHASE = 'alpha'
@ -191,7 +192,7 @@ async function getActiveAssignmentsForUser(userId) {
*/ */
async function _getVariantNames(splitTestName) { async function _getVariantNames(splitTestName) {
const splitTest = await SplitTestCache.get(splitTestName) const splitTest = await SplitTestCache.get(splitTestName)
const currentVersion = splitTest?.getCurrentVersion() const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
if (currentVersion?.active) { if (currentVersion?.active) {
return currentVersion.variants.map(v => v.name).concat([DEFAULT_VARIANT]) return currentVersion.variants.map(v => v.name).concat([DEFAULT_VARIANT])
} else { } else {
@ -208,7 +209,7 @@ async function _getAssignment(
} }
const splitTest = await SplitTestCache.get(splitTestName) const splitTest = await SplitTestCache.get(splitTestName)
const currentVersion = splitTest?.getCurrentVersion() const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
if (!currentVersion?.active) { if (!currentVersion?.active) {
return DEFAULT_ASSIGNMENT return DEFAULT_ASSIGNMENT
} }
@ -251,7 +252,7 @@ async function _getAssignment(
} }
async function _getAssignmentMetadata(analyticsId, user, splitTest) { async function _getAssignmentMetadata(analyticsId, user, splitTest) {
const currentVersion = splitTest.getCurrentVersion() const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
const phase = currentVersion.phase const phase = currentVersion.phase
if ( if (
(phase === ALPHA_PHASE && !user?.alphaProgram) || (phase === ALPHA_PHASE && !user?.alphaProgram) ||
@ -398,10 +399,10 @@ async function _getUser(id) {
async function _loadSplitTestInfoInLocals(locals, splitTestName) { async function _loadSplitTestInfoInLocals(locals, splitTestName) {
const splitTest = await SplitTestCache.get(splitTestName) const splitTest = await SplitTestCache.get(splitTestName)
if (splitTest) { if (splitTest) {
const phase = splitTest.getCurrentVersion().phase const phase = SplitTestUtils.getCurrentVersion(splitTest).phase
LocalsHelper.setSplitTestInfo(locals, splitTestName, { LocalsHelper.setSplitTestInfo(locals, splitTestName, {
phase, phase,
badgeInfo: splitTest.toObject().badgeInfo?.[phase], badgeInfo: splitTest.badgeInfo?.[phase],
}) })
} }
} }

View file

@ -1,4 +1,5 @@
const { SplitTest } = require('../../models/SplitTest') const { SplitTest } = require('../../models/SplitTest')
const SplitTestUtils = require('./SplitTestUtils')
const OError = require('@overleaf/o-error') const OError = require('@overleaf/o-error')
const _ = require('lodash') const _ = require('lodash')
@ -89,7 +90,7 @@ async function updateSplitTestConfig(name, configuration) {
if (splitTest.archived) { if (splitTest.archived) {
throw new OError('Cannot update an archived split test', { name }) throw new OError('Cannot update an archived split test', { name })
} }
const lastVersion = splitTest.getCurrentVersion().toObject() const lastVersion = SplitTestUtils.getCurrentVersion(splitTest).toObject()
if (configuration.phase !== lastVersion.phase) { if (configuration.phase !== lastVersion.phase) {
throw new OError( throw new OError(
`Cannot update with different phase - use /switch-to-next-phase endpoint instead` `Cannot update with different phase - use /switch-to-next-phase endpoint instead`
@ -145,7 +146,7 @@ async function switchToNextPhase(name) {
name, name,
}) })
} }
const lastVersionCopy = splitTest.getCurrentVersion().toObject() const lastVersionCopy = SplitTestUtils.getCurrentVersion(splitTest).toObject()
lastVersionCopy.versionNumber++ lastVersionCopy.versionNumber++
if (lastVersionCopy.phase === ALPHA_PHASE) { if (lastVersionCopy.phase === ALPHA_PHASE) {
lastVersionCopy.phase = BETA_PHASE lastVersionCopy.phase = BETA_PHASE
@ -189,13 +190,13 @@ async function revertToPreviousVersion(name, versionNumber) {
`Cannot revert split test with ID '${name}' to previous version: split test must have at least 2 versions` `Cannot revert split test with ID '${name}' to previous version: split test must have at least 2 versions`
) )
} }
const previousVersion = splitTest.getVersion(versionNumber) const previousVersion = SplitTestUtils.getVersion(splitTest, versionNumber)
if (!previousVersion) { if (!previousVersion) {
throw new OError( throw new OError(
`Cannot revert split test with ID '${name}' to version number ${versionNumber}: version not found` `Cannot revert split test with ID '${name}' to version number ${versionNumber}: version not found`
) )
} }
const lastVersion = splitTest.getCurrentVersion() const lastVersion = SplitTestUtils.getCurrentVersion(splitTest)
if ( if (
lastVersion.phase === RELEASE_PHASE && lastVersion.phase === RELEASE_PHASE &&
previousVersion.phase !== RELEASE_PHASE previousVersion.phase !== RELEASE_PHASE
@ -217,7 +218,8 @@ async function archive(name) {
throw new OError(`Split test with ID '${name}' is already archived`) throw new OError(`Split test with ID '${name}' is already archived`)
} }
splitTest.archived = true splitTest.archived = true
const previousVersionCopy = splitTest.getCurrentVersion().toObject() const previousVersionCopy =
SplitTestUtils.getCurrentVersion(splitTest).toObject()
previousVersionCopy.versionNumber += 1 previousVersionCopy.versionNumber += 1
previousVersionCopy.active = false previousVersionCopy.active = false
splitTest.versions.push(previousVersionCopy) splitTest.versions.push(previousVersionCopy)

View file

@ -0,0 +1,20 @@
const _ = require('lodash')
function getCurrentVersion(splitTest) {
if (splitTest?.versions?.length > 0) {
return _.maxBy(splitTest.versions, 'versionNumber')
} else {
return undefined
}
}
function getVersion(splitTest, versionNumber) {
return _.find(splitTest.versions || [], {
versionNumber,
})
}
module.exports = {
getCurrentVersion,
getVersion,
}

View file

@ -1,6 +1,5 @@
const mongoose = require('../infrastructure/Mongoose') const mongoose = require('../infrastructure/Mongoose')
const { Schema } = mongoose const { Schema } = mongoose
const _ = require('lodash')
const MIN_NAME_LENGTH = 3 const MIN_NAME_LENGTH = 3
const MAX_NAME_LENGTH = 200 const MAX_NAME_LENGTH = 200
@ -147,20 +146,6 @@ const SplitTestSchema = new Schema({
}, },
}) })
SplitTestSchema.methods.getCurrentVersion = function () {
if (this.versions && this.versions.length > 0) {
return _.maxBy(this.versions, 'versionNumber')
} else {
return undefined
}
}
SplitTestSchema.methods.getVersion = function (versionNumber) {
return _.find(this.versions || [], {
versionNumber,
})
}
module.exports = { module.exports = {
SplitTest: mongoose.model('SplitTest', SplitTestSchema), SplitTest: mongoose.model('SplitTest', SplitTestSchema),
SplitTestSchema, SplitTestSchema,

View file

@ -176,28 +176,31 @@ describe('SplitTestHandler', function () {
}) })
}) })
function makeSplitTest(name, opts = {}) { function makeSplitTest(
const { name,
{
active = true, active = true,
analyticsEnabled = active, analyticsEnabled = active,
phase = 'release', phase = 'release',
versionNumber = 1, versionNumber = 1,
} = opts } = {}
) {
return { return {
name, name,
getCurrentVersion: sinon.stub().returns({ versions: [
active, {
analyticsEnabled, active,
phase, analyticsEnabled,
versionNumber, phase,
variants: [ versionNumber,
{ variants: [
name: 'variant-1', {
rolloutPercent: 100, name: 'variant-1',
rolloutStripes: [{ start: 0, end: 100 }], rolloutPercent: 100,
}, rolloutStripes: [{ start: 0, end: 100 }],
], },
}), ],
},
],
} }
} }