refresh_features.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
  2. const minimist = require('minimist')
  3. const _ = require('lodash')
  4. const async = require('async')
  5. const FeaturesUpdater = require('../app/src/Features/Subscription/FeaturesUpdater')
  6. const FeaturesHelper = require('../app/src/Features/Subscription/FeaturesHelper')
  7. const UserFeaturesUpdater = require('../app/src/Features/Subscription/UserFeaturesUpdater')
  8. const ScriptLogger = {
  9. checkedUsersCount: 0,
  10. mismatchUsersCount: 0,
  11. allDaysSinceLastLoggedIn: [],
  12. allMismatchReasons: {},
  13. recordMismatch: (user, mismatchReasons) => {
  14. const mismatchReasonsString = JSON.stringify(mismatchReasons)
  15. if (ScriptLogger.allMismatchReasons[mismatchReasonsString]) {
  16. ScriptLogger.allMismatchReasons[mismatchReasonsString].push(user._id)
  17. } else {
  18. ScriptLogger.allMismatchReasons[mismatchReasonsString] = [user._id]
  19. }
  20. ScriptLogger.mismatchUsersCount += 1
  21. if (user.lastLoggedIn) {
  22. const daysSinceLastLoggedIn =
  23. (new Date() - user.lastLoggedIn) / 1000 / 3600 / 24
  24. ScriptLogger.allDaysSinceLastLoggedIn.push(daysSinceLastLoggedIn)
  25. }
  26. },
  27. printProgress: () => {
  28. console.warn(
  29. `Users checked: ${ScriptLogger.checkedUsersCount}. Mismatches: ${ScriptLogger.mismatchUsersCount}`
  30. )
  31. },
  32. printSummary: () => {
  33. console.log('All Mismatch Reasons:', ScriptLogger.allMismatchReasons)
  34. console.log('Mismatch Users Count', ScriptLogger.mismatchUsersCount)
  35. console.log(
  36. 'Average Last Logged In (Days):',
  37. _.sum(ScriptLogger.allDaysSinceLastLoggedIn) /
  38. ScriptLogger.allDaysSinceLastLoggedIn.length
  39. )
  40. console.log(
  41. 'Recent Logged In (Last 7 Days):',
  42. _.filter(ScriptLogger.allDaysSinceLastLoggedIn, a => a < 7).length
  43. )
  44. console.log(
  45. 'Recent Logged In (Last 30 Days):',
  46. _.filter(ScriptLogger.allDaysSinceLastLoggedIn, a => a < 30).length
  47. )
  48. },
  49. }
  50. const checkAndUpdateUser = (user, callback) =>
  51. FeaturesUpdater.computeFeatures(user._id, (error, freshFeatures) => {
  52. if (error) {
  53. return callback(error)
  54. }
  55. const mismatchReasons = FeaturesHelper.compareFeatures(
  56. user.features,
  57. freshFeatures
  58. )
  59. if (Object.keys(mismatchReasons).length === 0) {
  60. // features are matching; nothing else to do
  61. return callback()
  62. }
  63. ScriptLogger.recordMismatch(user, mismatchReasons)
  64. if (!COMMIT) {
  65. // not saving features; nothing else to do
  66. return callback()
  67. }
  68. UserFeaturesUpdater.overrideFeatures(user._id, freshFeatures, callback)
  69. })
  70. const checkAndUpdateUsers = (users, callback) =>
  71. async.eachLimit(users, ASYNC_LIMIT, checkAndUpdateUser, callback)
  72. const loopForUsers = (skip, callback) => {
  73. db.users
  74. .find({})
  75. .project({ features: 1, lastLoggedIn: 1 })
  76. .sort({ _id: 1 })
  77. .skip(skip)
  78. .limit(FETCH_LIMIT)
  79. .toArray((error, users) => {
  80. if (error) {
  81. return callback(error)
  82. }
  83. if (users.length === 0) {
  84. console.warn('DONE')
  85. return callback()
  86. }
  87. checkAndUpdateUsers(users, error => {
  88. if (error) {
  89. return callback(error)
  90. }
  91. ScriptLogger.checkedUsersCount += users.length
  92. retryCounter = 0
  93. ScriptLogger.printProgress()
  94. ScriptLogger.printSummary()
  95. loopForUsers(MONGO_SKIP + ScriptLogger.checkedUsersCount, callback)
  96. })
  97. })
  98. }
  99. let retryCounter = 0
  100. const run = () =>
  101. loopForUsers(MONGO_SKIP + ScriptLogger.checkedUsersCount, error => {
  102. if (error) {
  103. if (retryCounter < 3) {
  104. console.error(error)
  105. retryCounter += 1
  106. console.warn(`RETRYING IN 60 SECONDS. (${retryCounter}/3)`)
  107. return setTimeout(run, 6000)
  108. }
  109. throw error
  110. }
  111. process.exit()
  112. })
  113. let FETCH_LIMIT, ASYNC_LIMIT, COMMIT, MONGO_SKIP
  114. const setup = () => {
  115. const argv = minimist(process.argv.slice(2))
  116. FETCH_LIMIT = argv.fetch ? argv.fetch : 100
  117. ASYNC_LIMIT = argv.async ? argv.async : 10
  118. MONGO_SKIP = argv.skip ? argv.skip : 0
  119. COMMIT = argv.commit !== undefined
  120. if (!COMMIT) {
  121. console.warn('Doing dry run without --commit')
  122. }
  123. if (MONGO_SKIP) {
  124. console.warn(`Skipping first ${MONGO_SKIP} records`)
  125. }
  126. }
  127. waitForDb().then(() => {
  128. setup()
  129. run()
  130. })