regenerate_duplicate_referral_ids.mjs 3.0 KB

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