delete_orphaned_chat_threads.mjs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import mongodb from 'mongodb-legacy'
  2. import { promiseMapWithLimit } from '@overleaf/promise-utils'
  3. import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
  4. import ChatApiHandler from '../app/src/Features/Chat/ChatApiHandler.mjs'
  5. import DeleteOrphanedDataHelper from './delete_orphaned_data_helper.mjs'
  6. import { ensureMongoTimeout } from './helpers/env_variable_helper.mjs'
  7. import { db } from '../app/src/infrastructure/mongodb.mjs'
  8. const { ObjectId } = mongodb
  9. const { getHardDeletedProjectIds } = DeleteOrphanedDataHelper
  10. const READ_CONCURRENCY_SECONDARY =
  11. parseInt(process.env.READ_CONCURRENCY_SECONDARY, 10) || 1000
  12. const READ_CONCURRENCY_PRIMARY =
  13. parseInt(process.env.READ_CONCURRENCY_PRIMARY, 10) || 500
  14. const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
  15. const BATCH_SIZE = parseInt(process.env.BATCH_SIZE, 10) || 100
  16. const DRY_RUN = process.env.DRY_RUN !== 'false'
  17. const MAX_CHATS_TO_DESTROY =
  18. parseInt(process.env.MAX_CHATS_TO_DESTROY, 10) || false
  19. // persist fallback in order to keep batchedUpdate in-sync
  20. process.env.BATCH_SIZE = BATCH_SIZE
  21. // ensure set mongo timeout to 10mins if otherwise unspecified
  22. if (!process.env.MONGO_SOCKET_TIMEOUT) {
  23. ensureMongoTimeout(600000)
  24. }
  25. console.log({
  26. DRY_RUN,
  27. WRITE_CONCURRENCY,
  28. BATCH_SIZE,
  29. MAX_CHATS_TO_DESTROY,
  30. })
  31. const RESULT = {
  32. DRY_RUN,
  33. projectChatsDestroyed: 0,
  34. continueFrom: null,
  35. }
  36. async function processBatch(rooms) {
  37. if (rooms.length && rooms[0]._id) {
  38. RESULT.continueFrom = rooms[0]._id
  39. }
  40. const projectIds = Array.from(
  41. new Set(rooms.map(room => room.project_id.toString()))
  42. ).map(id => new ObjectId(id))
  43. console.log(
  44. `Checking projects (${projectIds.length})`,
  45. JSON.stringify(projectIds)
  46. )
  47. const projectsWithOrphanedChat = await getHardDeletedProjectIds({
  48. projectIds,
  49. READ_CONCURRENCY_PRIMARY,
  50. READ_CONCURRENCY_SECONDARY,
  51. })
  52. console.log(
  53. `Destroying chat for projects (${projectsWithOrphanedChat.length})`,
  54. JSON.stringify(projectsWithOrphanedChat)
  55. )
  56. if (!DRY_RUN) {
  57. await promiseMapWithLimit(
  58. WRITE_CONCURRENCY,
  59. projectsWithOrphanedChat,
  60. ChatApiHandler.promises.destroyProject
  61. )
  62. }
  63. RESULT.projectChatsDestroyed += projectsWithOrphanedChat.length
  64. console.log(RESULT)
  65. if (
  66. MAX_CHATS_TO_DESTROY &&
  67. RESULT.projectChatsDestroyed >= MAX_CHATS_TO_DESTROY
  68. ) {
  69. console.log(
  70. `MAX_CHATS_TO_DELETE limit (${MAX_CHATS_TO_DESTROY}) reached. Stopping.`
  71. )
  72. process.exit(0)
  73. }
  74. }
  75. async function main() {
  76. const projection = {
  77. _id: 1,
  78. project_id: 1,
  79. }
  80. await batchedUpdate(db.rooms, {}, processBatch, projection)
  81. console.log('Final')
  82. console.log(RESULT)
  83. }
  84. main()
  85. .then(() => {
  86. console.log('Done.')
  87. process.exit(0)
  88. })
  89. .catch(error => {
  90. console.error({ error })
  91. process.exit(1)
  92. })