resync_subscriptions.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. const { Subscription } = require('../../app/src/models/Subscription')
  2. const RecurlyWrapper = require('../../app/src/Features/Subscription/RecurlyWrapper')
  3. const SubscriptionUpdater = require('../../app/src/Features/Subscription/SubscriptionUpdater')
  4. const async = require('async')
  5. const minimist = require('minimist')
  6. const ScriptLogger = {
  7. checkedSubscriptionsCount: 0,
  8. mismatchSubscriptionsCount: 0,
  9. allMismatchReasons: {},
  10. recordMismatch: (subscription, recurlySubscription) => {
  11. const mismatchReasons = {}
  12. if (subscription.planCode !== recurlySubscription.plan.plan_code) {
  13. mismatchReasons['recurlyPlan'] = recurlySubscription.plan.plan_code
  14. mismatchReasons['olPlan'] = subscription.planCode
  15. }
  16. if (recurlySubscription.state === 'expired') {
  17. mismatchReasons['state'] = 'expired'
  18. }
  19. if (!Object.keys(mismatchReasons).length) {
  20. return
  21. }
  22. ScriptLogger.mismatchSubscriptionsCount += 1
  23. const mismatchReasonsString = JSON.stringify(mismatchReasons)
  24. if (ScriptLogger.allMismatchReasons[mismatchReasonsString]) {
  25. ScriptLogger.allMismatchReasons[mismatchReasonsString].push(
  26. subscription._id
  27. )
  28. } else {
  29. ScriptLogger.allMismatchReasons[mismatchReasonsString] = [
  30. subscription._id
  31. ]
  32. }
  33. },
  34. printProgress: () => {
  35. console.warn(
  36. `Subscriptions checked: ${
  37. ScriptLogger.checkedSubscriptionsCount
  38. }. Mismatches: ${ScriptLogger.mismatchSubscriptionsCount}`
  39. )
  40. },
  41. printSummary: () => {
  42. console.log('All Mismatch Reasons:', ScriptLogger.allMismatchReasons)
  43. console.log(
  44. 'Mismatch Subscriptions Count',
  45. ScriptLogger.mismatchSubscriptionsCount
  46. )
  47. }
  48. }
  49. const slowCallback = callback => setTimeout(callback, 80)
  50. const handleAPIError = (subscriptionId, error, callback) => {
  51. console.warn(`Errors with subscription id=${subscriptionId}:`, error)
  52. if (typeof error === 'string' && error.match(/429$/)) {
  53. return setTimeout(callback, 1000 * 60 * 5)
  54. }
  55. slowCallback(callback)
  56. }
  57. const syncSubscription = (subscription, callback) => {
  58. RecurlyWrapper.getSubscription(
  59. subscription.recurlySubscription_id,
  60. (error, recurlySubscription) => {
  61. if (error) {
  62. return handleAPIError(subscription._id, error, callback)
  63. }
  64. ScriptLogger.recordMismatch(subscription, recurlySubscription)
  65. if (!COMMIT) {
  66. return callback()
  67. }
  68. SubscriptionUpdater._updateSubscriptionFromRecurly(
  69. recurlySubscription,
  70. subscription,
  71. {},
  72. error => {
  73. if (error) {
  74. return handleAPIError(subscription._id, error, callback)
  75. }
  76. slowCallback(callback)
  77. }
  78. )
  79. }
  80. )
  81. }
  82. const syncSubscriptions = (subscriptions, callback) => {
  83. async.eachLimit(subscriptions, ASYNC_LIMIT, syncSubscription, callback)
  84. }
  85. const loopForSubscriptions = (skip, callback) => {
  86. Subscription.find({ recurlySubscription_id: { $exists: true } })
  87. .sort('_id')
  88. .skip(skip)
  89. .limit(FETCH_LIMIT)
  90. .exec((error, subscriptions) => {
  91. if (error) {
  92. return callback(error)
  93. }
  94. if (subscriptions.length === 0) {
  95. console.warn('DONE')
  96. return callback()
  97. }
  98. syncSubscriptions(subscriptions, error => {
  99. if (error) {
  100. return callback(error)
  101. }
  102. ScriptLogger.checkedSubscriptionsCount += subscriptions.length
  103. retryCounter = 0
  104. ScriptLogger.printProgress()
  105. ScriptLogger.printSummary()
  106. loopForSubscriptions(
  107. MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
  108. callback
  109. )
  110. })
  111. })
  112. }
  113. let retryCounter = 0
  114. const run = () =>
  115. loopForSubscriptions(
  116. MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
  117. error => {
  118. if (error) {
  119. if (retryCounter < 3) {
  120. console.error(error)
  121. retryCounter += 1
  122. console.warn(`RETRYING IN 60 SECONDS. (${retryCounter}/3)`)
  123. return setTimeout(run, 60000)
  124. }
  125. throw error
  126. }
  127. process.exit()
  128. }
  129. )
  130. let FETCH_LIMIT, ASYNC_LIMIT, COMMIT, MONGO_SKIP
  131. const setup = () => {
  132. const argv = minimist(process.argv.slice(2))
  133. FETCH_LIMIT = argv.fetch ? argv.fetch : 100
  134. ASYNC_LIMIT = argv.async ? argv.async : 10
  135. MONGO_SKIP = argv.skip ? argv.skip : 0
  136. COMMIT = argv.commit !== undefined
  137. if (!COMMIT) {
  138. console.warn('Doing dry run without --commit')
  139. }
  140. if (MONGO_SKIP) {
  141. console.warn(`Skipping first ${MONGO_SKIP} records`)
  142. }
  143. }
  144. setup()
  145. run()