openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
120 lines (102 loc) • 3.4 kB
JavaScript
import logger from 'winston'
import moment from 'moment'
import { AutoRetryModel } from './model/autoRetry'
import { TaskModel } from './model/tasks'
import * as Channels from './model/channels'
const { ChannelModel } = Channels
export function reachedMaxAttempts (tx, channel) {
return (channel.autoRetryMaxAttempts != null) &&
(channel.autoRetryMaxAttempts > 0) &&
(tx.autoRetryAttempt >= channel.autoRetryMaxAttempts)
}
export async function queueForRetry (tx) {
const retry = new AutoRetryModel({
transactionID: tx._id,
channelID: tx.channelID,
requestTimestamp: tx.request.timestamp
})
try {
await retry.save()
} catch (err) {
logger.error(`Failed to queue transaction ${tx._id} for auto retry: ${err}`)
}
}
const getChannels = callback => ChannelModel.find({ autoRetryEnabled: true, status: 'enabled' }, callback)
function popTransactions (channel, callback) {
const to = moment().subtract(channel.autoRetryPeriodMinutes - 1, 'minutes')
const query = {
$and: [
{ channelID: channel._id },
{
requestTimestamp: {
$lte: to.toDate()
}
}
]
}
logger.debug(`Executing query autoRetry.findAndRemove(${JSON.stringify(query)})`)
AutoRetryModel.find(query, (err, transactions) => {
if (err) { return callback(err) }
if (transactions.length === 0) { return callback(null, []) }
AutoRetryModel.deleteOne({ _id: { $in: (transactions.map(t => t._id)) } }, (err) => {
if (err) { return callback(err) }
return callback(null, transactions)
})
})
}
function createRerunTask (transactionIDs, callback) {
logger.info(`Rerunning failed transactions: ${transactionIDs}`)
const task = new TaskModel({
transactions: (transactionIDs.map(t => ({ tid: t }))),
totalTransactions: transactionIDs.length,
remainingTransactions: transactionIDs.length,
user: 'internal'
})
task.save((err) => {
if (err) { logger.error(err) }
return callback()
})
}
function autoRetryTask (job, done) {
const _taskStart = new Date()
const transactionsToRerun = []
getChannels((err, channels) => {
if (err) { return done(err) }
const promises = channels.map((channel) => {
return new Promise((resolve, reject) => {
popTransactions(channel, (err, transactions) => {
if (err) {
logger.error(err)
return reject(err)
} else if (transactions != null) {
const tranIDs = transactions.map(r => r.transactionID)
transactionsToRerun.push(...tranIDs)
}
return resolve()
})
})
})
Promise.all(promises).then(() => {
function end () {
logger.debug(`Auto retry task total time: ${new Date() - _taskStart} ms`)
return done()
}
if (transactionsToRerun.length > 0) {
return createRerunTask(transactionsToRerun, end)
} else {
end()
}
}).catch(done)
})
}
function setupAgenda (agenda) {
agenda.define('auto retry failed transactions', (job, done) => autoRetryTask(job, done))
return agenda.every('1 minutes', 'auto retry failed transactions')
}
export { setupAgenda }
if (process.env.NODE_ENV === 'test') {
exports.getChannels = getChannels
exports.popTransactions = popTransactions
exports.createRerunTask = createRerunTask
exports.autoRetryTask = autoRetryTask
}