regenerate_duplicate_referral_ids.js 2.9 KB

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