overleaf/services/web/scripts/recurly/resync_subscriptions.js
Tim Down 800c9d63b4 Merge pull request #14048 from overleaf/mj-recurly-script-update
[web] recurly resync script update

GitOrigin-RevId: 798cb73f46b763f6ece6b12d63fe82fbcc6c2920
2023-07-28 08:04:14 +00:00

177 lines
4.9 KiB
JavaScript

const { Subscription } = require('../../app/src/models/Subscription')
const RecurlyWrapper = require('../../app/src/Features/Subscription/RecurlyWrapper')
const SubscriptionUpdater = require('../../app/src/Features/Subscription/SubscriptionUpdater')
const async = require('async')
const minimist = require('minimist')
// make sure all `allMismatchReasons` are displayed in the output
const util = require('util')
util.inspect.defaultOptions.maxArrayLength = null
const ScriptLogger = {
checkedSubscriptionsCount: 0,
mismatchSubscriptionsCount: 0,
allMismatchReasons: {},
recordMismatch: (subscription, recurlySubscription) => {
const mismatchReasons = {}
if (subscription.planCode !== recurlySubscription.plan.plan_code) {
mismatchReasons.recurlyPlan = recurlySubscription.plan.plan_code
mismatchReasons.olPlan = subscription.planCode
}
if (recurlySubscription.state === 'expired') {
mismatchReasons.state = 'expired'
}
if (!Object.keys(mismatchReasons).length) {
return
}
ScriptLogger.mismatchSubscriptionsCount += 1
const mismatchReasonsString = JSON.stringify(mismatchReasons)
if (ScriptLogger.allMismatchReasons[mismatchReasonsString]) {
ScriptLogger.allMismatchReasons[mismatchReasonsString].push({
id: subscription._id,
name: subscription.planCode,
})
} else {
ScriptLogger.allMismatchReasons[mismatchReasonsString] = [
{
id: subscription._id,
name: subscription.planCode,
},
]
}
},
printProgress: () => {
console.warn(
`Subscriptions checked: ${ScriptLogger.checkedSubscriptionsCount}. Mismatches: ${ScriptLogger.mismatchSubscriptionsCount}`
)
},
printSummary: () => {
console.log('All Mismatch Reasons:', ScriptLogger.allMismatchReasons)
console.log(
'Mismatch Subscriptions Count',
ScriptLogger.mismatchSubscriptionsCount
)
},
}
const slowCallback = callback => setTimeout(callback, 80)
const handleSyncSubscriptionError = (subscription, error, callback) => {
console.warn(`Errors with subscription id=${subscription._id}:`, error)
if (typeof error === 'string' && error.match(/429$/)) {
return setTimeout(callback, 1000 * 60 * 5)
}
if (typeof error === 'string' && error.match(/5\d\d$/)) {
return setTimeout(() => {
syncSubscription(subscription, callback)
}, 1000 * 60)
}
slowCallback(callback)
}
const syncSubscription = (subscription, callback) => {
RecurlyWrapper.getSubscription(
subscription.recurlySubscription_id,
(error, recurlySubscription) => {
if (error) {
return handleSyncSubscriptionError(subscription, error, callback)
}
ScriptLogger.recordMismatch(subscription, recurlySubscription)
if (!COMMIT) {
return callback()
}
SubscriptionUpdater.updateSubscriptionFromRecurly(
recurlySubscription,
subscription,
{},
error => {
if (error) {
return handleSyncSubscriptionError(subscription, error, callback)
}
slowCallback(callback)
}
)
}
)
}
const syncSubscriptions = (subscriptions, callback) => {
async.eachLimit(subscriptions, ASYNC_LIMIT, syncSubscription, callback)
}
const loopForSubscriptions = (skip, callback) => {
Subscription.find({
recurlySubscription_id: { $exists: true, $ne: '' },
})
.sort('_id')
.skip(skip)
.limit(FETCH_LIMIT)
.exec((error, subscriptions) => {
if (error) {
return callback(error)
}
if (subscriptions.length === 0) {
console.warn('DONE')
return callback()
}
syncSubscriptions(subscriptions, error => {
if (error) {
return callback(error)
}
ScriptLogger.checkedSubscriptionsCount += subscriptions.length
retryCounter = 0
ScriptLogger.printProgress()
ScriptLogger.printSummary()
loopForSubscriptions(
MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
callback
)
})
})
}
let retryCounter = 0
const run = () =>
loopForSubscriptions(
MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
error => {
if (error) {
if (retryCounter < 3) {
console.error(error)
retryCounter += 1
console.warn(`RETRYING IN 60 SECONDS. (${retryCounter}/3)`)
return setTimeout(run, 60000)
}
throw error
}
process.exit()
}
)
let FETCH_LIMIT, ASYNC_LIMIT, COMMIT, MONGO_SKIP
const setup = () => {
const argv = minimist(process.argv.slice(2))
FETCH_LIMIT = argv.fetch ? argv.fetch : 100
ASYNC_LIMIT = argv.async ? argv.async : 10
MONGO_SKIP = argv.skip ? argv.skip : 0
COMMIT = argv.commit !== undefined
if (!COMMIT) {
console.warn('Doing dry run without --commit')
}
if (MONGO_SKIP) {
console.warn(`Skipping first ${MONGO_SKIP} records`)
}
}
setup()
run()