fix_docs_with_missing_project.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. const Settings = require('@overleaf/settings')
  2. const logger = require('@overleaf/logger')
  3. const rclient = require('@overleaf/redis-wrapper').createClient(
  4. Settings.redis.documentupdater
  5. )
  6. const keys = Settings.redis.documentupdater.key_schema
  7. const ProjectFlusher = require('app/js/ProjectFlusher')
  8. const DocumentManager = require('app/js/DocumentManager')
  9. const util = require('util')
  10. const flushAndDeleteDocWithLock = util.promisify(
  11. DocumentManager.flushAndDeleteDocWithLock
  12. )
  13. const { MongoClient, ObjectId } = require('mongodb')
  14. const clientPromise = MongoClient.connect(
  15. Settings.mongo.url,
  16. Settings.mongo.options
  17. )
  18. const db = {}
  19. clientPromise.then(client => {
  20. db.docs = client.db().collection('docs')
  21. })
  22. async function fixDocsWithMissingProjectIds(dockeys, options) {
  23. const docIds = ProjectFlusher._extractIds(dockeys)
  24. for (const docId of docIds) {
  25. const projectId = await rclient.get(keys.projectKey({ doc_id: docId }))
  26. logger.debug({ docId, projectId }, 'checking doc')
  27. if (!projectId) {
  28. try {
  29. await insertMissingProjectId(docId, options)
  30. } catch (err) {
  31. logger.error({ docId, err }, 'error fixing doc without project id')
  32. }
  33. }
  34. }
  35. }
  36. async function insertMissingProjectId(docId, options) {
  37. const doc = await db.docs.findOne({ _id: ObjectId(docId) })
  38. if (!doc) {
  39. logger.warn({ docId }, 'doc not found in mongo')
  40. return
  41. }
  42. if (!doc.project_id) {
  43. logger.error({ docId }, 'doc does not have project id in mongo')
  44. return
  45. }
  46. logger.debug({ docId, doc }, 'found doc')
  47. const projectIdFromMongo = doc.project_id.toString()
  48. if (options.dryRun) {
  49. logger.info(
  50. { projectIdFromMongo, docId },
  51. 'dry run mode - would insert project id in redis'
  52. )
  53. return
  54. }
  55. // set the project id for this doc
  56. await rclient.set(keys.projectKey({ doc_id: docId }), projectIdFromMongo)
  57. logger.debug({ docId, projectIdFromMongo }, 'inserted project id in redis')
  58. if (projectIdFromMongo) {
  59. await flushAndDeleteDocWithLock(projectIdFromMongo, docId, {})
  60. logger.info(
  61. { docId, projectIdFromMongo },
  62. 'fixed doc with empty project id'
  63. )
  64. }
  65. return projectIdFromMongo
  66. }
  67. async function findAndProcessDocs(options) {
  68. logger.info({ options }, 'fixing docs with missing projcct id')
  69. let cursor = 0
  70. do {
  71. const [newCursor, doclinesKeys] = await rclient.scan(
  72. cursor,
  73. 'MATCH',
  74. keys.docLines({ doc_id: '*' }),
  75. 'COUNT',
  76. options.limit
  77. )
  78. await fixDocsWithMissingProjectIds(doclinesKeys, options)
  79. cursor = newCursor
  80. } while (cursor !== '0')
  81. }
  82. clientPromise.then(client => {
  83. findAndProcessDocs({ limit: 1000, dryRun: process.env.DRY_RUN !== 'false' })
  84. .then(result => {
  85. rclient.quit()
  86. client.close()
  87. console.log('DONE')
  88. })
  89. .catch(function (error) {
  90. if (error) {
  91. throw error
  92. }
  93. return process.exit()
  94. })
  95. })