Skip to content

Instantly share code, notes, and snippets.

@imVinayPandya
Last active September 25, 2019 09:31
Show Gist options
  • Save imVinayPandya/5230c9d6198aaba9e1f649fe24f47791 to your computer and use it in GitHub Desktop.
Save imVinayPandya/5230c9d6198aaba9e1f649fe24f47791 to your computer and use it in GitHub Desktop.
How to get notification when CRON fails (Node.js).
// 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 };
}
};
// 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 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];
}
}
@imVinayPandya
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment