regenerate_duplicate_referral_ids.mjs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import {
  2. db,
  3. READ_PREFERENCE_SECONDARY,
  4. } from '../app/src/infrastructure/mongodb.js'
  5. import { promiseMapWithLimit } from '@overleaf/promise-utils'
  6. import TokenGenerator from '../app/src/Features/TokenGenerator/TokenGenerator.js'
  7. import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
  8. const VERBOSE_LOGGING = process.env.VERBOSE_LOGGING === 'true'
  9. const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
  10. const BATCH_SIZE = parseInt(process.env.BATCH_SIZE, 10) || 100
  11. // persist fallback in order to keep batchedUpdate in-sync
  12. process.env.BATCH_SIZE = BATCH_SIZE
  13. async function rewriteDuplicates(duplicateReferralIds) {
  14. // duplicateReferralIds contains at least one duplicate.
  15. // Find out which is the duplicate in parallel and update
  16. // any users if necessary.
  17. await promiseMapWithLimit(
  18. WRITE_CONCURRENCY,
  19. duplicateReferralIds,
  20. async referralId => {
  21. try {
  22. const users = await db.users
  23. .find(
  24. { referal_id: referralId },
  25. {
  26. projection: { _id: 1 },
  27. readPreference: READ_PREFERENCE_SECONDARY,
  28. }
  29. )
  30. .toArray()
  31. if (users.length === 1) {
  32. // This referral id was part of a batch of duplicates.
  33. // Keep the write load low and skip the update.
  34. return
  35. }
  36. if (VERBOSE_LOGGING) {
  37. console.log('Found duplicate:', referralId)
  38. }
  39. for (const user of users) {
  40. const newReferralId = TokenGenerator.generateReferralId()
  41. await db.users.updateOne(
  42. { _id: user._id },
  43. {
  44. $set: {
  45. referal_id: newReferralId,
  46. },
  47. }
  48. )
  49. }
  50. } catch (error) {
  51. console.error(
  52. { err: error },
  53. `Failed to generate new referral ID for duplicate ID: ${referralId}`
  54. )
  55. }
  56. }
  57. )
  58. }
  59. async function processBatch(users) {
  60. const uniqueReferalIdsInBatch = Array.from(
  61. new Set(users.map(user => user.referal_id))
  62. )
  63. if (uniqueReferalIdsInBatch.length !== users.length) {
  64. if (VERBOSE_LOGGING) {
  65. console.log('Got duplicates from looking at batch.')
  66. }
  67. await rewriteDuplicates(uniqueReferalIdsInBatch)
  68. return
  69. }
  70. const matches = await db.users
  71. .find(
  72. { referal_id: { $in: uniqueReferalIdsInBatch } },
  73. {
  74. readPreference: READ_PREFERENCE_SECONDARY,
  75. projection: { _id: true },
  76. }
  77. )
  78. .toArray()
  79. if (matches.length !== uniqueReferalIdsInBatch.length) {
  80. if (VERBOSE_LOGGING) {
  81. console.log('Got duplicates from running count.')
  82. }
  83. await rewriteDuplicates(uniqueReferalIdsInBatch)
  84. }
  85. }
  86. async function main() {
  87. await batchedUpdate(
  88. db.users,
  89. { referal_id: { $exists: true } },
  90. processBatch,
  91. { _id: 1, referal_id: 1 }
  92. )
  93. }
  94. main()
  95. .then(() => {
  96. console.error('Done.')
  97. process.exit(0)
  98. })
  99. .catch(error => {
  100. console.error({ error })
  101. process.exit(1)
  102. })