back_fill_dummy_doc_meta.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. const { promisify } = require('util')
  2. const { ObjectId, ReadPreference } = require('mongodb')
  3. const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
  4. const sleep = promisify(setTimeout)
  5. const NOW_IN_S = Date.now() / 1000
  6. const ONE_WEEK_IN_S = 60 * 60 * 24 * 7
  7. const TEN_SECONDS = 10 * 1000
  8. const CACHE_SIZE = parseInt(process.env.CACHE_SIZE, 10) || 100
  9. const DRY_RUN = process.env.DRY_RUN === 'true'
  10. if (!process.env.FIRST_PROJECT_ID) {
  11. console.error('Set FIRST_PROJECT_ID and re-run.')
  12. process.exit(1)
  13. }
  14. const FIRST_PROJECT_ID = ObjectId(process.env.FIRST_PROJECT_ID)
  15. const INCREMENT_BY_S = parseInt(process.env.INCREMENT_BY_S, 10) || ONE_WEEK_IN_S
  16. const BATCH_SIZE = parseInt(process.env.BATCH_SIZE, 10) || 1000
  17. const STOP_AT_S = parseInt(process.env.STOP_AT_S, 10) || NOW_IN_S
  18. const LET_USER_DOUBLE_CHECK_INPUTS_FOR =
  19. parseInt(process.env.LET_USER_DOUBLE_CHECK_INPUTS_FOR, 10) || TEN_SECONDS
  20. const DUMMY_NAME = 'unknown.tex'
  21. const DUMMY_TIME = new Date('2021-04-12T00:00:00.000Z')
  22. const LRUCache = require('lru-cache')
  23. const deletedProjectsCache = new LRUCache({
  24. max: CACHE_SIZE,
  25. })
  26. function getSecondsFromObjectId(id) {
  27. return id.getTimestamp().getTime() / 1000
  28. }
  29. async function main() {
  30. await letUserDoubleCheckInputs()
  31. await waitForDb()
  32. let startId = FIRST_PROJECT_ID
  33. let nProcessed = 0
  34. while (getSecondsFromObjectId(startId) <= STOP_AT_S) {
  35. const end = getSecondsFromObjectId(startId) + INCREMENT_BY_S
  36. let endId = ObjectId.createFromTime(end)
  37. const query = {
  38. project_id: {
  39. // include edge
  40. $gte: startId,
  41. // exclude edge
  42. $lt: endId,
  43. },
  44. deleted: true,
  45. name: {
  46. $exists: false,
  47. },
  48. }
  49. const docs = await db.docs
  50. .find(query, { readPreference: ReadPreference.SECONDARY })
  51. .project({ _id: 1, project_id: 1 })
  52. .limit(BATCH_SIZE)
  53. .toArray()
  54. if (docs.length) {
  55. const docIds = docs.map(doc => doc._id)
  56. console.log('Back filling dummy meta data for', JSON.stringify(docIds))
  57. await processBatch(docs)
  58. nProcessed += docIds.length
  59. if (docs.length === BATCH_SIZE) {
  60. endId = docs[docs.length - 1].project_id
  61. }
  62. }
  63. console.error('Processed %d until %s', nProcessed, endId)
  64. startId = endId
  65. }
  66. }
  67. async function getDeletedProject(projectId) {
  68. const cacheKey = projectId.toString()
  69. if (deletedProjectsCache.has(cacheKey)) {
  70. return deletedProjectsCache.get(cacheKey)
  71. }
  72. const deletedProject = await db.deletedProjects.findOne(
  73. { 'deleterData.deletedProjectId': projectId },
  74. {
  75. projection: {
  76. _id: 1,
  77. 'project.deletedDocs': 1,
  78. },
  79. }
  80. )
  81. deletedProjectsCache.set(cacheKey, deletedProject)
  82. return deletedProject
  83. }
  84. async function processBatch(docs) {
  85. for (const doc of docs) {
  86. const { _id: docId, project_id: projectId } = doc
  87. const deletedProject = await getDeletedProject(projectId)
  88. let name = DUMMY_NAME
  89. let deletedAt = DUMMY_TIME
  90. if (deletedProject) {
  91. const project = deletedProject.project
  92. if (project) {
  93. const deletedDoc =
  94. project.deletedDocs &&
  95. project.deletedDocs.find(deletedDoc => docId.equals(deletedDoc._id))
  96. if (deletedDoc) {
  97. console.log('Found deletedDoc for %s', docId)
  98. name = deletedDoc.name
  99. deletedAt = deletedDoc.deletedAt
  100. } else {
  101. console.log('Missing deletedDoc for %s', docId)
  102. }
  103. } else {
  104. console.log('Orphaned deleted doc %s (failed hard deletion)', docId)
  105. }
  106. } else {
  107. console.log('Orphaned deleted doc %s (no deletedProjects entry)', docId)
  108. }
  109. if (DRY_RUN) continue
  110. await db.docs.updateOne({ _id: docId }, { $set: { name, deletedAt } })
  111. }
  112. }
  113. async function letUserDoubleCheckInputs() {
  114. console.error(
  115. 'Options:',
  116. JSON.stringify(
  117. {
  118. BATCH_SIZE,
  119. CACHE_SIZE,
  120. DRY_RUN,
  121. FIRST_PROJECT_ID,
  122. INCREMENT_BY_S,
  123. STOP_AT_S,
  124. LET_USER_DOUBLE_CHECK_INPUTS_FOR,
  125. },
  126. null,
  127. 2
  128. )
  129. )
  130. console.error(
  131. 'Waiting for you to double check inputs for',
  132. LET_USER_DOUBLE_CHECK_INPUTS_FOR,
  133. 'ms'
  134. )
  135. await sleep(LET_USER_DOUBLE_CHECK_INPUTS_FOR)
  136. }
  137. main()
  138. .then(() => {
  139. console.error('Done.')
  140. process.exit(0)
  141. })
  142. .catch(error => {
  143. console.error({ error })
  144. process.exit(1)
  145. })