mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge pull request #11280 from overleaf/bg-issue11277
Move history migration logic to web module GitOrigin-RevId: 6f1ba33519277b9ba13ecb2a13ae2c43ee06f675
This commit is contained in:
parent
b4e9bf3449
commit
1a0e1def66
6 changed files with 92 additions and 93 deletions
|
@ -789,7 +789,12 @@ module.exports = {
|
||||||
editorLeftMenuSync: [],
|
editorLeftMenuSync: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
|
moduleImportSequence: [
|
||||||
|
'launchpad',
|
||||||
|
'server-ce-scripts',
|
||||||
|
'user-activate',
|
||||||
|
'history-migration',
|
||||||
|
],
|
||||||
|
|
||||||
csp: {
|
csp: {
|
||||||
enabled: process.env.CSP_ENABLED === 'true',
|
enabled: process.env.CSP_ENABLED === 'true',
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const { ReadPreference, ObjectId } = require('mongodb')
|
const { ReadPreference, ObjectId } = require('mongodb')
|
||||||
const { db } = require('../../app/src/infrastructure/mongodb')
|
const { db } = require('../../../../app/src/infrastructure/mongodb')
|
||||||
const Settings = require('@overleaf/settings')
|
const Settings = require('@overleaf/settings')
|
||||||
|
|
||||||
const ProjectHistoryHandler = require('../../app/src/Features/Project/ProjectHistoryHandler')
|
const ProjectHistoryHandler = require('../../../../app/src/Features/Project/ProjectHistoryHandler')
|
||||||
const HistoryManager = require('../../app/src/Features/History/HistoryManager')
|
const HistoryManager = require('../../../../app/src/Features/History/HistoryManager')
|
||||||
const ProjectHistoryController = require('../../modules/admin-panel/app/src/ProjectHistoryController')
|
const ProjectHistoryController = require('../../../admin-panel/app/src/ProjectHistoryController')
|
||||||
const ProjectEntityHandler = require('../../app/src/Features/Project/ProjectEntityHandler')
|
const ProjectEntityHandler = require('../../../../app/src/Features/Project/ProjectEntityHandler')
|
||||||
const ProjectEntityUpdateHandler = require('../../app/src/Features/Project/ProjectEntityUpdateHandler')
|
const ProjectEntityUpdateHandler = require('../../../../app/src/Features/Project/ProjectEntityUpdateHandler')
|
||||||
|
|
||||||
// Timestamp of when 'Enable history for SL in background' release
|
// Timestamp of when 'Enable history for SL in background' release
|
||||||
const ID_WHEN_FULL_PROJECT_HISTORY_ENABLED = '5a8d8a370000000000000000'
|
const ID_WHEN_FULL_PROJECT_HISTORY_ENABLED = '5a8d8a370000000000000000'
|
||||||
|
@ -16,6 +16,21 @@ const OBJECT_ID_WHEN_FULL_PROJECT_HISTORY_ENABLED = new ObjectId(
|
||||||
const DATETIME_WHEN_FULL_PROJECT_HISTORY_ENABLED =
|
const DATETIME_WHEN_FULL_PROJECT_HISTORY_ENABLED =
|
||||||
OBJECT_ID_WHEN_FULL_PROJECT_HISTORY_ENABLED.getTimestamp()
|
OBJECT_ID_WHEN_FULL_PROJECT_HISTORY_ENABLED.getTimestamp()
|
||||||
|
|
||||||
|
async function countProjects(query = {}) {
|
||||||
|
const count = await db.projects.count(query)
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
async function countDocHistory(query = {}) {
|
||||||
|
const count = await db.docHistory.count(query)
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findProjects(query = {}, projection = {}) {
|
||||||
|
const projects = await db.projects.find(query).project(projection).toArray()
|
||||||
|
return projects
|
||||||
|
}
|
||||||
|
|
||||||
async function determineProjectHistoryType(project) {
|
async function determineProjectHistoryType(project) {
|
||||||
if (project.overleaf && project.overleaf.history) {
|
if (project.overleaf && project.overleaf.history) {
|
||||||
if (project.overleaf.history.upgradeFailed) {
|
if (project.overleaf.history.upgradeFailed) {
|
||||||
|
@ -212,18 +227,28 @@ async function doUpgradeForNoneWithoutConversion(project) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doUpgradeForNoneWithConversion(project) {
|
async function doUpgradeForNoneWithConversion(project, options = {}) {
|
||||||
const result = {}
|
const result = {}
|
||||||
const projectId = project._id
|
const projectId = project._id
|
||||||
// migrateProjectHistory expects project id as a string
|
// migrateProjectHistory expects project id as a string
|
||||||
const projectIdString = project._id.toString()
|
const projectIdString = project._id.toString()
|
||||||
try {
|
try {
|
||||||
await ProjectHistoryController.migrateProjectHistory(projectIdString)
|
if (options.convertLargeDocsToFile) {
|
||||||
|
result.convertedDocCount = await convertLargeDocsToFile(
|
||||||
|
projectId,
|
||||||
|
options.userId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await ProjectHistoryController.migrateProjectHistory(
|
||||||
|
projectIdString,
|
||||||
|
options.migrationOptions
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// if migrateProjectHistory fails, it cleans up by deleting
|
// if migrateProjectHistory fails, it cleans up by deleting
|
||||||
// the history and unsetting the history id
|
// the history and unsetting the history id
|
||||||
// therefore a failed project will still look like a 'None with conversion' project
|
// therefore a failed project will still look like a 'None with conversion' project
|
||||||
result.error = err
|
result.error = err
|
||||||
|
// We set a failed flag so future runs of the script don't automatically retry
|
||||||
await db.projects.updateOne(
|
await db.projects.updateOne(
|
||||||
{ _id: projectId },
|
{ _id: projectId },
|
||||||
{
|
{
|
||||||
|
@ -238,7 +263,8 @@ async function doUpgradeForNoneWithConversion(project) {
|
||||||
{ _id: projectId },
|
{ _id: projectId },
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
'overleaf.history.upgradeReason': `none-with-conversion`,
|
'overleaf.history.upgradeReason':
|
||||||
|
`none-with-conversion` + options.reason ? `/${options.reason}` : ``,
|
||||||
},
|
},
|
||||||
$unset: {
|
$unset: {
|
||||||
'overleaf.history.upgradeFailed': true,
|
'overleaf.history.upgradeFailed': true,
|
||||||
|
@ -328,6 +354,9 @@ function docIsTooLarge(estimatedSize, lines, maxDocLength) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
countProjects,
|
||||||
|
countDocHistory,
|
||||||
|
findProjects,
|
||||||
determineProjectHistoryType,
|
determineProjectHistoryType,
|
||||||
getUpgradeFunctionForType,
|
getUpgradeFunctionForType,
|
||||||
upgradeProject,
|
upgradeProject,
|
1
services/web/modules/history-migration/index.js
Normal file
1
services/web/modules/history-migration/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = {}
|
|
@ -11,7 +11,10 @@ process.env.MONGO_SOCKET_TIMEOUT =
|
||||||
|
|
||||||
const { promiseMapWithLimit } = require('../../app/src/util/promises')
|
const { promiseMapWithLimit } = require('../../app/src/util/promises')
|
||||||
const { batchedUpdate } = require('../helpers/batchedUpdate')
|
const { batchedUpdate } = require('../helpers/batchedUpdate')
|
||||||
const { determineProjectHistoryType } = require('./HistoryUpgradeHelper')
|
const {
|
||||||
|
determineProjectHistoryType,
|
||||||
|
countProjects,
|
||||||
|
} = require('../../modules/history-migration/app/src/HistoryUpgradeHelper')
|
||||||
|
|
||||||
const COUNT = {
|
const COUNT = {
|
||||||
V2: 0,
|
V2: 0,
|
||||||
|
@ -22,6 +25,8 @@ const COUNT = {
|
||||||
NoneWithTemporaryHistory: 0,
|
NoneWithTemporaryHistory: 0,
|
||||||
UpgradeFailed: 0,
|
UpgradeFailed: 0,
|
||||||
ConversionFailed: 0,
|
ConversionFailed: 0,
|
||||||
|
MigratedProjects: 0,
|
||||||
|
TotalProjects: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processBatch(_, projects) {
|
async function processBatch(_, projects) {
|
||||||
|
@ -60,6 +65,10 @@ async function main() {
|
||||||
projection,
|
projection,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
COUNT.MigratedProjects = await countProjects({
|
||||||
|
'overleaf.history.display': true,
|
||||||
|
})
|
||||||
|
COUNT.TotalProjects = await countProjects()
|
||||||
console.log('Final')
|
console.log('Final')
|
||||||
console.log(COUNT)
|
console.log(COUNT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,15 @@ const USER_ID = process.env.USER_ID
|
||||||
const CONVERT_LARGE_DOCS_TO_FILE =
|
const CONVERT_LARGE_DOCS_TO_FILE =
|
||||||
process.env.CONVERT_LARGE_DOCS_TO_FILE === 'true'
|
process.env.CONVERT_LARGE_DOCS_TO_FILE === 'true'
|
||||||
|
|
||||||
const { ObjectId, ReadPreference } = require('mongodb')
|
const { ObjectId } = require('mongodb')
|
||||||
const { db, waitForDb } = require('../../app/src/infrastructure/mongodb')
|
const { db, waitForDb } = require('../../app/src/infrastructure/mongodb')
|
||||||
const { promiseMapWithLimit } = require('../../app/src/util/promises')
|
const { promiseMapWithLimit } = require('../../app/src/util/promises')
|
||||||
const { batchedUpdate } = require('../helpers/batchedUpdate')
|
const { batchedUpdate } = require('../helpers/batchedUpdate')
|
||||||
const ProjectHistoryController = require('../../modules/admin-panel/app/src/ProjectHistoryController')
|
const {
|
||||||
const HistoryUpgradeHelper = require('./HistoryUpgradeHelper')
|
anyDocHistoryExists,
|
||||||
|
anyDocHistoryIndexExists,
|
||||||
|
doUpgradeForNoneWithConversion,
|
||||||
|
} = require('../../modules/history-migration/app/src/HistoryUpgradeHelper')
|
||||||
|
|
||||||
console.log({
|
console.log({
|
||||||
DRY_RUN,
|
DRY_RUN,
|
||||||
|
@ -111,99 +114,49 @@ async function processProject(project) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const anyDocHistory = await anyDocHistoryExists(project)
|
|
||||||
if (anyDocHistory) {
|
|
||||||
return await doUpgradeForNoneWithConversion(project)
|
|
||||||
}
|
|
||||||
const anyDocHistoryIndex = await anyDocHistoryIndexExists(project)
|
|
||||||
if (anyDocHistoryIndex) {
|
|
||||||
return await doUpgradeForNoneWithConversion(project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doUpgradeForNoneWithConversion(project) {
|
|
||||||
if (RESULT.failed >= MAX_FAILURES) {
|
if (RESULT.failed >= MAX_FAILURES) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (MAX_UPGRADES_TO_ATTEMPT && RESULT.attempted >= MAX_UPGRADES_TO_ATTEMPT) {
|
if (MAX_UPGRADES_TO_ATTEMPT && RESULT.attempted >= MAX_UPGRADES_TO_ATTEMPT) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
RESULT.attempted += 1
|
const anyDocHistoryOrIndex =
|
||||||
const projectId = project._id
|
(await anyDocHistoryExists(project)) ||
|
||||||
// migrateProjectHistory expects project id as a string
|
(await anyDocHistoryIndexExists(project))
|
||||||
const projectIdString = project._id.toString()
|
if (anyDocHistoryOrIndex) {
|
||||||
if (!DRY_RUN) {
|
RESULT.attempted += 1
|
||||||
try {
|
if (DRY_RUN) {
|
||||||
if (CONVERT_LARGE_DOCS_TO_FILE) {
|
return
|
||||||
const convertedDocCount =
|
}
|
||||||
await HistoryUpgradeHelper.convertLargeDocsToFile(projectId, USER_ID)
|
const result = await doUpgradeForNoneWithConversion(project, {
|
||||||
console.log(
|
migrationOptions: {
|
||||||
`converted ${convertedDocCount} large docs to binary files for project ${projectId}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
await ProjectHistoryController.migrateProjectHistory(projectIdString, {
|
|
||||||
archiveOnFailure: ARCHIVE_ON_FAILURE,
|
archiveOnFailure: ARCHIVE_ON_FAILURE,
|
||||||
fixInvalidCharacters: FIX_INVALID_CHARACTERS,
|
fixInvalidCharacters: FIX_INVALID_CHARACTERS,
|
||||||
forceNewHistoryOnFailure: FORCE_NEW_HISTORY_ON_FAILURE,
|
forceNewHistoryOnFailure: FORCE_NEW_HISTORY_ON_FAILURE,
|
||||||
importZipFilePath: IMPORT_ZIP_FILE_PATH,
|
importZipFilePath: IMPORT_ZIP_FILE_PATH,
|
||||||
cutoffDate: CUTOFF_DATE,
|
cutoffDate: CUTOFF_DATE,
|
||||||
})
|
},
|
||||||
} catch (err) {
|
convertLargeDocsToFile: CONVERT_LARGE_DOCS_TO_FILE,
|
||||||
// if migrateProjectHistory fails, it cleans up by deleting
|
userId: USER_ID,
|
||||||
// the history and unsetting the history id
|
reason: `${SCRIPT_VERSION}`,
|
||||||
// therefore a failed project will still look like a 'None with conversion' project
|
})
|
||||||
RESULT.failed += 1
|
if (result.convertedDocCount) {
|
||||||
console.error(`project ${projectId} FAILED with error: `, err)
|
console.log(
|
||||||
// We set a failed flag so future runs of the script don't automatically retry
|
`project ${project._id} converted ${result.convertedDocCount} docs to filestore`
|
||||||
await db.projects.updateOne(
|
|
||||||
{ _id: projectId },
|
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
'overleaf.history.conversionFailed': true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
await db.projects.updateOne(
|
if (result.error) {
|
||||||
{ _id: projectId },
|
console.error(`project ${project._id} FAILED with error: `, result.error)
|
||||||
{
|
RESULT.failed += 1
|
||||||
$set: {
|
} else if (result.upgraded) {
|
||||||
'overleaf.history.upgradeReason': `none-with-conversion/${SCRIPT_VERSION}`,
|
if (VERBOSE_LOGGING) {
|
||||||
},
|
console.log(
|
||||||
$unset: {
|
`project ${project._id} converted and upgraded to full project history`
|
||||||
'overleaf.history.upgradeFailed': true,
|
)
|
||||||
'overleaf.history.conversionFailed': true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
RESULT.projectsUpgraded += 1
|
||||||
}
|
|
||||||
if (VERBOSE_LOGGING) {
|
|
||||||
console.log(
|
|
||||||
`project ${projectId} converted and upgraded to full project history`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
RESULT.projectsUpgraded += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
async function anyDocHistoryExists(project) {
|
|
||||||
return await db.docHistory.findOne(
|
|
||||||
{ project_id: { $eq: project._id } },
|
|
||||||
{
|
|
||||||
projection: { _id: 1 },
|
|
||||||
readPreference: ReadPreference.SECONDARY,
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function anyDocHistoryIndexExists(project) {
|
|
||||||
return await db.docHistoryIndex.findOne(
|
|
||||||
{ project_id: { $eq: project._id } },
|
|
||||||
{
|
|
||||||
projection: { _id: 1 },
|
|
||||||
readPreference: ReadPreference.SECONDARY,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const { ReadPreference, ObjectId } = require('mongodb')
|
const { ReadPreference, ObjectId } = require('mongodb')
|
||||||
const { db, waitForDb } = require('../../app/src/infrastructure/mongodb')
|
const { db, waitForDb } = require('../../app/src/infrastructure/mongodb')
|
||||||
const { upgradeProject } = require('./HistoryUpgradeHelper')
|
const {
|
||||||
|
upgradeProject,
|
||||||
|
} = require('../../modules/history-migration/app/src/HistoryUpgradeHelper')
|
||||||
|
|
||||||
async function processProject(project) {
|
async function processProject(project) {
|
||||||
const result = await upgradeProject(project)
|
const result = await upgradeProject(project)
|
||||||
|
|
Loading…
Reference in a new issue