pr_14200.patch 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. --- services/web/modules/history-migration/app/src/HistoryUpgradeHelper.js
  2. +++ services/web/modules/history-migration/app/src/HistoryUpgradeHelper.js
  3. @@ -1,6 +1,9 @@
  4. +const _ = require('lodash')
  5. +const fs = require('fs')
  6. const { ReadPreference, ObjectId } = require('mongodb')
  7. const { db } = require('../../../../app/src/infrastructure/mongodb')
  8. const Settings = require('@overleaf/settings')
  9. +const logger = require('@overleaf/logger')
  10. const ProjectHistoryHandler = require('../../../../app/src/Features/Project/ProjectHistoryHandler')
  11. const HistoryManager = require('../../../../app/src/Features/History/HistoryManager')
  12. @@ -8,6 +11,8 @@ const ProjectHistoryController = require('./ProjectHistoryController')
  13. const ProjectEntityHandler = require('../../../../app/src/Features/Project/ProjectEntityHandler')
  14. const ProjectEntityUpdateHandler = require('../../../../app/src/Features/Project/ProjectEntityUpdateHandler')
  15. const DocumentUpdaterHandler = require('../../../../app/src/Features/DocumentUpdater/DocumentUpdaterHandler')
  16. +const { Doc } = require('../../../../app/src/models/Doc')
  17. +const FileWriter = require('../../../../app/src/infrastructure/FileWriter')
  18. // Timestamp of when 'Enable history for SL in background' release
  19. const ID_WHEN_FULL_PROJECT_HISTORY_ENABLED =
  20. @@ -340,9 +345,33 @@ async function anyDocHistoryIndexExists(project) {
  21. )
  22. }
  23. +async function convertDeletedDocToFile(projectId, docId, userId, source, doc) {
  24. + // write the doc to a temporary file and upload to filestore
  25. + const tmpFilePath = await FileWriter.promises.writeLinesToDisk(
  26. + projectId,
  27. + doc.lines
  28. + )
  29. + await ProjectEntityUpdateHandler.promises.upsertFileWithPath(
  30. + projectId,
  31. + `/_deleted/${docId}/${doc.name}`,
  32. + tmpFilePath,
  33. + null,
  34. + userId,
  35. + source
  36. + )
  37. + // hard delete the original doc, otherwise it will get picked up again
  38. + // by readDeletedDocs in ProjectHistoryController and the final
  39. + // resync of the history will fail.
  40. + await db.docs.deleteOne({ _id: docId })
  41. + await db.docOps.deleteOne({ doc_id: docId })
  42. + // clean up the temporary file
  43. + await fs.promises.unlink(tmpFilePath)
  44. +}
  45. +
  46. async function convertLargeDocsToFile(projectId, userId) {
  47. - const docs = await ProjectEntityHandler.promises.getAllDocs(projectId)
  48. let convertedDocCount = 0
  49. + const docs = await ProjectEntityHandler.promises.getAllDocs(projectId)
  50. + // Convert large docs to files
  51. for (const doc of Object.values(docs)) {
  52. const sizeBound = JSON.stringify(doc.lines)
  53. if (docIsTooLarge(sizeBound, doc.lines, Settings.max_doc_length)) {
  54. @@ -355,6 +384,39 @@ async function convertLargeDocsToFile(projectId, userId) {
  55. convertedDocCount++
  56. }
  57. }
  58. + // Convert deleted docs to files, these cannot be converted by
  59. + // ProjectEntityUpdateHandler so we do it manually
  60. + const docsCursor = Doc.find({
  61. + project_id: ObjectId(projectId),
  62. + })
  63. + .lean()
  64. + .cursor()
  65. + for await (const doc of docsCursor) {
  66. + // check whether the doc is present in the filetree instead of
  67. + // relying on the deletedAt property
  68. + const docExistsInFiletree = _.find(docs, existingDoc =>
  69. + existingDoc._id.equals(doc._id)
  70. + )
  71. + if (docExistsInFiletree || doc.inS3) {
  72. + continue
  73. + }
  74. + const sizeBound = JSON.stringify(doc.lines)
  75. + if (docIsTooLarge(sizeBound, doc.lines, Settings.max_doc_length)) {
  76. + const docId = doc._id.toString()
  77. + if (!_.isEmpty(doc.ranges)) {
  78. + throw new Error(`found too large deleted doc with ranges: ${docId}`)
  79. + }
  80. + logger.warn({ projectId, docId }, 'converting large deleted doc')
  81. + await convertDeletedDocToFile(
  82. + projectId,
  83. + doc._id,
  84. + userId,
  85. + 'history-migration',
  86. + doc
  87. + )
  88. + convertedDocCount++
  89. + }
  90. + }
  91. return convertedDocCount
  92. }