Last active
September 25, 2019 09:31
-
-
Save imVinayPandya/5230c9d6198aaba9e1f649fe24f47791 to your computer and use it in GitHub Desktop.
How to get notification when CRON fails (Node.js).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Main cron's logic here which will return array of messages | |
// and it will tell, how i am returning partially/full success result from this function | |
const accountInformationSyncer = ({ logger, utils, userUtils, interactiveBrokers }) => async () => { | |
const messages = []; // Array of result | |
const unexpectedErrors = []; // array of unexpectedErrors | |
function logInfo(msg) { | |
const m = `Account information sync: ${msg}`; | |
logger.info(m); // this will log to console/file | |
messages.push(m); // this will push message in array | |
} | |
function logUnexpectedError(payload) { | |
const { message = 'Unknown error' } = payload; | |
unexpectedErrors.push(payload); | |
logger.error(message); | |
} | |
logInfo('Started.'); | |
// ... More business logic here | |
// Iterate over Array of accountsInfo | |
await pMapSeries(accountsInfo, async accountInfo => { | |
const { accountId, accountInformation } = accountInfo; | |
const { customerType, name } = accountInformation; | |
const userId = await getUserIdFromAccountId(accountId); | |
// Example of partial success | |
// this is error is not that crusial, we can just skill process for current account | |
if (!userId) { | |
logInfo(`Skipping accountId ${accountId}, no userId found.`); | |
return; | |
} | |
// Example of partial success | |
// this is error is not that crusial, we can just skill process for current account | |
if (ignoredCustomerTypes.includes(customerType)) { | |
logInfo(`Skipping ignored customerType: '${customerType}' for userId: ${userId}.`); | |
return; | |
} | |
// Example for fanger error, which we can not ignore | |
// for some user from the array, this condition can occurs | |
if(danger === true) { | |
logUnexpectedError({ | |
userId: 'someUserId', | |
errorCode: 'THIS_IS_THE_REASON', | |
message: `${err.message}: this is log error message which tells user that what went wrong with cron` | |
}); | |
} | |
// ... More business logic here | |
}); | |
// doing some serious db task here, which i want to do after interating overloop | |
// if this db operation fails then this cron logic is failed | |
try { | |
await db.peroformSomeTask(); | |
logInfo(`Finished. Looked at ${flexResults.length} accounts from Interactive Brokers.`); | |
} catch(err) { | |
// log the error store the error in array and return it | |
logUnexpectedError({ | |
userId: 'someUserId', | |
errorCode: 'THIS_IS_THE_REASON', | |
message: `${err.message}: this is log error message which tells user that what went wrong with cron` | |
}); | |
} finally { | |
// Either its success, danger error or partial success, | |
// at the end i will return the both result | |
return { messages, unexpectedErrors }; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Daily at PDT(17:00) = UTC(1:00). | |
cron.schedule('0 1 * * *', async () => { | |
try { | |
logger.info('Starting sync account information from cron'); | |
// it will get machine userId from database, so later on we can come to know that, | |
// the cron was ran by admin user or its get executed automatically | |
const executedBy = await getAutomatedProcessUserId(); | |
const results = await syncAccountInformation({ executedBy }); | |
logger.info('account information cron results', results); | |
} catch (err) { | |
// following function will send error to sentry and log it in console | |
errorUtil.captureException(err); | |
} finally { | |
logger.info('Ending sync account information'); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this function will execute cron code and will insert the result of cron in database | |
// also you can call this function from api end-point, doing this i can allow admin to execute cron manually | |
const syncAccountInformation = async ({ executedBy = null, ipAddress = null }) => { | |
const jobResultName = 'syncAccountInformation'; | |
try { | |
// calling actual business logic here, and it will return the job result | |
// that result could be fully successful or partially successful or unexpectedErrors in the case of job failed | |
const { messages = [], unexpectedErrors = [] } = await accountInformationSyncer({ some, dependency, here })(); | |
// if job is returning message | |
if(messages && messages.length > 0) { | |
// then i am adding job result in database, so admin can view result it in admin panel | |
await jobResults.add({ | |
name: jobResultName, | |
isSuccess: true, | |
results: messages.join('\n'), | |
executedBy, | |
ipAddress, | |
}); | |
} | |
// if job is returning unexpectedErrors | |
if(unexpectedErrors && unexpectedErrors.length > 0) { | |
// then i am sending slack notification here | |
await sendSlackNotificaiton(unexpectedErrors).catch(() => { | |
// handle this catch part here because i don't want to execute next catch | |
logger.error('sending notification to slack has been failed'); | |
}); | |
} | |
return { messages, unexpectedErrors }; | |
} catch (err) { | |
logger.error(`${err.message}:\n${err.stack.split('\n')}`); | |
const results = `${jobResultName} failed with error: ${err.message}.`; | |
// if it is getting failed here, still i am inserting result into database | |
await jobResults.add({ | |
name: jobResultName, | |
isSuccess: false, | |
results, | |
executedBy, | |
ipAddress, | |
}); | |
// then i am sending slack notification here | |
await sendSlackNotificaiton(unexpectedErrors).catch(() => { | |
// handle this catch part here because i don't want raise an error from catch part | |
logger.error('sending notification to slack has been failed'); | |
}); | |
return [results]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here i am just trying to explain that, how you can make reusable cron job function. Using that you can allow api endpoint to execute cron as well as you can execute automatically as cron on specific time. Apart from these i have explained how you can get notified on slack if job is success, partial success or complete failed.
Hope this will help.