Merge pull request #2439 from overleaf/ta-refresh-features-script

Add Script To Refresh All Users' Features

GitOrigin-RevId: 3ec107d170af7d65b7fe5bf90598e18d738bfad5
This commit is contained in:
Timothée Alby 2019-12-05 19:27:58 +05:30 committed by Copybot
parent 3de7e69a8d
commit 5783f99fbd
2 changed files with 167 additions and 3 deletions

View file

@ -31,7 +31,16 @@ const FeaturesUpdater = {
if (callback == null) { if (callback == null) {
callback = function(error, features, featuresChanged) {} callback = function(error, features, featuresChanged) {}
} }
FeaturesUpdater._computeFeatures(user_id, (error, features) => {
if (error) {
return callback(error)
}
logger.log({ user_id, features }, 'updating user features')
UserFeaturesUpdater.updateFeatures(user_id, features, callback)
})
},
_computeFeatures(user_id, callback) {
const jobs = { const jobs = {
individualFeatures(cb) { individualFeatures(cb) {
return FeaturesUpdater._getIndividualFeatures(user_id, cb) return FeaturesUpdater._getIndividualFeatures(user_id, cb)
@ -99,9 +108,7 @@ const FeaturesUpdater = {
FeaturesUpdater._mergeFeatures, FeaturesUpdater._mergeFeatures,
Settings.defaultFeatures Settings.defaultFeatures
) )
callback(null, features)
logger.log({ user_id, features }, 'updating user features')
return UserFeaturesUpdater.updateFeatures(user_id, features, callback)
}) })
}, },

View file

@ -0,0 +1,157 @@
const { db } = require('../app/src/infrastructure/mongojs')
const minimist = require('minimist')
const _ = require('lodash')
const async = require('async')
const FeaturesUpdater = require('../app/src/Features/Subscription/FeaturesUpdater')
const UserFeaturesUpdater = require('../app/src/Features/Subscription/UserFeaturesUpdater')
const getMismatchReasons = (currentFeatures, expectedFeatures) => {
if (_.isEqual(currentFeatures, expectedFeatures)) {
return null
}
let mismatchReasons = {}
Object.keys(currentFeatures)
.sort()
.forEach(key => {
if (expectedFeatures[key] !== currentFeatures[key]) {
mismatchReasons[key] = currentFeatures[key]
}
})
return mismatchReasons
}
const normalizeMismatchReasons = mismatchReasons => {
if (mismatchReasons.collaborators > 1 && mismatchReasons.collaborators < 10) {
mismatchReasons.collaborators = 10
}
if (mismatchReasons.collaborators > 10) {
mismatchReasons.collaborators = -1
}
if (mismatchReasons.compileTimeout) {
mismatchReasons.compileTimeout = 240
}
return mismatchReasons
}
const recordMismatch = (user, mismatchReasons) => {
mismatchReasons = normalizeMismatchReasons(mismatchReasons)
const mismatchReasonsString = JSON.stringify(mismatchReasons)
if (allMismatchReasons[mismatchReasonsString]) {
allMismatchReasons[mismatchReasonsString] += 1
} else {
allMismatchReasons[mismatchReasonsString] = 1
}
mismatchUsersCount += 1
if (user.lastLoggedIn) {
let daysSinceLastLoggedIn =
(new Date() - user.lastLoggedIn) / 1000 / 3600 / 24
allDaysSinceLastLoggedIn.push(daysSinceLastLoggedIn)
}
}
const checkAndUpdateUser = (user, callback) =>
FeaturesUpdater._computeFeatures(user._id, (error, freshFeatures) => {
if (error) {
return callback(error)
}
let mismatchReasons = getMismatchReasons(user.features, freshFeatures)
if (!mismatchReasons) {
// features are matching; nothing else to do
return callback()
}
recordMismatch(user, mismatchReasons)
if (!COMMIT) {
// not saving features; nothing else to do
return callback()
}
UserFeaturesUpdater.updateFeatures(user._id, freshFeatures, callback)
})
const updateUsers = (users, callback) =>
async.eachLimit(users, ASYNC_LIMIT, checkAndUpdateUser, error => {
if (error) {
return callback(error)
}
checkedUsersCount += users.length
console.log(
`Users checked: ${checkedUsersCount}. Mismatches: ${mismatchUsersCount}`
)
callback()
})
const loopForUsers = (lastUserId, callback) => {
const query = {}
if (lastUserId) {
query['_id'] = { $gt: lastUserId }
}
db.users
.find(query, { features: 1, lastLoggedIn: 1 })
.limit(FETCH_LIMIT, (error, users) => {
if (error) {
return callback(error)
}
if (users.length === 0) {
console.log('DONE')
return callback()
}
updateUsers(users, error => {
if (error) {
return callback(error)
}
const lastUserId = users[users.length - 1]._id
loopForUsers(lastUserId, callback)
})
})
}
let checkedUsersCount = 0
let mismatchUsersCount = 0
let allDaysSinceLastLoggedIn = []
let allMismatchReasons = {}
const run = () =>
loopForUsers(null, error => {
if (error) {
throw error
}
console.log({ allMismatchReasons })
console.log(
'Average Last Logged In (Days):',
_.sum(allDaysSinceLastLoggedIn) / allDaysSinceLastLoggedIn.length
)
console.log(
'Recent Logged In (Last 7 Days):',
_.filter(allDaysSinceLastLoggedIn, a => a < 7).length
)
console.log(
'Recent Logged In (Last 30 Days):',
_.filter(allDaysSinceLastLoggedIn, a => a < 30).length
)
process.exit()
})
let FETCH_LIMIT, ASYNC_LIMIT, COMMIT
const setup = () => {
const argv = minimist(process.argv.slice(2))
FETCH_LIMIT = argv.fetch ? argv.fetch : 100
ASYNC_LIMIT = argv.async ? argv.async : 10
COMMIT = argv.commit !== undefined
if (!COMMIT) {
console.log('Doing dry run without --commit')
}
}
setup()
run()