refresh_features.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. const { db } = require('../app/src/infrastructure/mongojs')
  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 UserFeaturesUpdater = require('../app/src/Features/Subscription/UserFeaturesUpdater')
  7. const getMismatchReasons = (currentFeatures, expectedFeatures) => {
  8. currentFeatures = _.clone(currentFeatures)
  9. expectedFeatures = _.clone(expectedFeatures)
  10. if (_.isEqual(currentFeatures, expectedFeatures)) {
  11. return {}
  12. }
  13. let mismatchReasons = {}
  14. Object.keys(currentFeatures)
  15. .sort()
  16. .forEach(key => {
  17. if (expectedFeatures[key] !== currentFeatures[key]) {
  18. mismatchReasons[key] = expectedFeatures[key]
  19. }
  20. })
  21. if (mismatchReasons.compileTimeout) {
  22. // store the compile timeout difference instead of the new compile timeout
  23. mismatchReasons.compileTimeout =
  24. expectedFeatures.compileTimeout - currentFeatures.compileTimeout
  25. }
  26. if (mismatchReasons.collaborators) {
  27. // store the collaborators difference instead of the new number only
  28. // replace -1 by 100 to make it clearer
  29. if (expectedFeatures.collaborators === -1) {
  30. expectedFeatures.collaborators = 100
  31. }
  32. if (currentFeatures.collaborators === -1) {
  33. currentFeatures.collaborators = 100
  34. }
  35. mismatchReasons.collaborators =
  36. expectedFeatures.collaborators - currentFeatures.collaborators
  37. }
  38. return mismatchReasons
  39. }
  40. const recordMismatch = (user, mismatchReasons) => {
  41. const mismatchReasonsString = JSON.stringify(mismatchReasons)
  42. if (allMismatchReasons[mismatchReasonsString]) {
  43. allMismatchReasons[mismatchReasonsString] += 1
  44. } else {
  45. allMismatchReasons[mismatchReasonsString] = 1
  46. }
  47. mismatchUsersCount += 1
  48. if (user.lastLoggedIn) {
  49. let daysSinceLastLoggedIn =
  50. (new Date() - user.lastLoggedIn) / 1000 / 3600 / 24
  51. allDaysSinceLastLoggedIn.push(daysSinceLastLoggedIn)
  52. }
  53. }
  54. const checkAndUpdateUser = (user, callback) =>
  55. FeaturesUpdater._computeFeatures(user._id, (error, freshFeatures) => {
  56. if (error) {
  57. return callback(error)
  58. }
  59. let mismatchReasons = getMismatchReasons(user.features, freshFeatures)
  60. if (Object.keys(mismatchReasons).length === 0) {
  61. // features are matching; nothing else to do
  62. return callback()
  63. }
  64. recordMismatch(user, mismatchReasons)
  65. if (!COMMIT) {
  66. // not saving features; nothing else to do
  67. return callback()
  68. }
  69. UserFeaturesUpdater.overrideFeatures(user._id, freshFeatures, callback)
  70. })
  71. const updateUsers = (users, callback) =>
  72. async.eachLimit(users, ASYNC_LIMIT, checkAndUpdateUser, error => {
  73. if (error) {
  74. return callback(error)
  75. }
  76. checkedUsersCount += users.length
  77. console.log(
  78. `Users checked: ${checkedUsersCount}. Mismatches: ${mismatchUsersCount}`
  79. )
  80. callback()
  81. })
  82. const loopForUsers = (lastUserId, callback) => {
  83. const query = {}
  84. if (lastUserId) {
  85. query['_id'] = { $gt: lastUserId }
  86. }
  87. db.users
  88. .find(query, { features: 1, lastLoggedIn: 1 })
  89. .sort('_id')
  90. .limit(FETCH_LIMIT, (error, users) => {
  91. if (error) {
  92. return callback(error)
  93. }
  94. if (users.length === 0) {
  95. console.log('DONE')
  96. return callback()
  97. }
  98. updateUsers(users, error => {
  99. if (error) {
  100. return callback(error)
  101. }
  102. const lastUserId = users[users.length - 1]._id
  103. loopForUsers(lastUserId, callback)
  104. })
  105. })
  106. }
  107. const printSummary = () => {
  108. console.log({ allMismatchReasons })
  109. console.log(
  110. 'Average Last Logged In (Days):',
  111. _.sum(allDaysSinceLastLoggedIn) / allDaysSinceLastLoggedIn.length
  112. )
  113. console.log(
  114. 'Recent Logged In (Last 7 Days):',
  115. _.filter(allDaysSinceLastLoggedIn, a => a < 7).length
  116. )
  117. console.log(
  118. 'Recent Logged In (Last 30 Days):',
  119. _.filter(allDaysSinceLastLoggedIn, a => a < 30).length
  120. )
  121. }
  122. let checkedUsersCount = 0
  123. let mismatchUsersCount = 0
  124. let allDaysSinceLastLoggedIn = []
  125. let allMismatchReasons = {}
  126. const run = () =>
  127. loopForUsers(null, error => {
  128. if (error) {
  129. throw error
  130. }
  131. printSummary()
  132. process.exit()
  133. })
  134. let FETCH_LIMIT, ASYNC_LIMIT, COMMIT
  135. const setup = () => {
  136. const argv = minimist(process.argv.slice(2))
  137. FETCH_LIMIT = argv.fetch ? argv.fetch : 100
  138. ASYNC_LIMIT = argv.async ? argv.async : 10
  139. COMMIT = argv.commit !== undefined
  140. if (!COMMIT) {
  141. console.log('Doing dry run without --commit')
  142. }
  143. }
  144. setup()
  145. run()