1

My apologies beforehand, because I'm finding it a bit difficult to explain the question at hand, so I'll divide it in parts.

I'm trying to find the best way to trigger retries based on a failed final retry attempt from another function (retry of a series of retries). What I'm thinking would work is to put something like throw new Err('Forfeit') within the else statement of each retry function (see codes for 1 and 2), then have the original call retry based on that (since there's a condition when it reaches the max attempt). Will that work? How would you approach it?

  1. I created my own retry function below (n=3 attempts):

     function retryFetchAttempt(attempt, maxAttempts, myFunction, error) {
    
     if (attempt < maxAttempts) {
         setTimeout(function() {
             myFunction()
             .then(result => console.log('Retry: ', attempt))
             .catch(err => {
                   console.log(err);
                   attempt += 1;
                   retryFetchAttempt(attempt, maxAttempts, myFunction, error)
                 }
             )
         }, 2000 ** attempt);
     } else {
         console.log('Forfeited at retry: ', attempt, error);
         // throw new Err('Forfeit'); ????
     }
     }
    
     module.exports = {
       retryFetchAttempt
     }
    
  2. I created another retry for each email being sent (10 attempts).

     const {google} = require('googleapis');
     const nodemailer = require('nodemailer');
    
     function retryEmail(attempt, sender, emailRecipients, bccRecipients, getSubject, htmlOutput, refreshToken, clientid, client_secret, REDIRECT_URI) {
    
     const maxRetries = 10;
     if (attempt < maxRetries) {
         setTimeout(function() {
             sendEmail(
                 sender,
                 emailRecipients,
                 bccRecipients,
                 getSubject,
                 htmlOutput,
                 refreshToken,
                 clientid,
                 client_secret,
                 REDIRECT_URI
             ).then(result => console.log('Email sent after retry: ', attempt, result))
             .catch(err => {
                   console.log(err);
                   attempt += 1;
                   retryEmail(
                     attempt,
                     sender,
                     emailRecipients,
                     bccRecipients,
                     getSubject,
                     htmlOutput,
                     refreshToken,
                     clientid,
                     client_secret,
                     REDIRECT_URI)
                 }
             )
         }, 3000);
     } else {
         console.log('Forfeited at retry: ', attempt);
         // throw new Err('Forfeit'); ????
     }
    }
    
     async function sendEmail(sender, emailRecipients, bccRecipients, getSubject, htmlOutput, refreshToken, clientid, client_secret, REDIRECT_URI) {
    
     try {
         const oAuth2Client = new google.auth.OAuth2(clientid, client_secret, REDIRECT_URI);
         oAuth2Client.setCredentials({refresh_token: refreshToken});
         const accessToken = await oAuth2Client.getAccessToken();
    
         let smtpConfigWithToken = {
             host: 'smtp.gmail.com',
             port: 465,
             secure: true,
             //pool: true,
             auth: {
                 type: 'OAuth2',
                 user: sender,
                 clientId: clientid,
                 clientSecret: client_secret,
                 refreshToken: accessToken.res.data.refresh_token,
                 accessToken: accessToken.res.data.accessToken
             }
         };
    
         let transporter = nodemailer.createTransport(smtpConfigWithToken);
    
         let HelperOptions = {
             from: sender,
             to: emailRecipients,
             bcc: bccRecipients,
             subject: getSubject,
             html: htmlOutput
         };
    
         const result = transporter.sendMail(HelperOptions);
         return result;
     } catch(err) {
         return err;
     }
    }
    
    module.exports = {
      sendEmail,
      retryEmail
    }
    
  3. I have a node cron job that triggers Mon-Fri at 11:30am UTC - 300. This job uses node-fetch to call another route that queries the database. That fetch call uses the 3 attempts retry. See the code below:

    // CRON schedule to run MON-FRI at 11:30am UTC - 300 // Reminder of returning equipment 7 days after being loaned // Also removes the 'snooze' status after 7 days (if snooze was applied)

     cron.schedule('30 11 * * 1,2,3,4,5', () => {
     let retryAttempt = 0;
     const maxAttempts = 3;
    
     async function scheduler() {
       try {
         const fetchResult = await fetch(process.env.REDIRECT_URI + config.route1, {
           method: 'GET',
           headers: {
             'Content-Type': 'application/json',
             'auth-token': process.env.API_TOKEN
           },
         });
         const response = await fetchResult;
         const jsonData = await response.json();
         console.log(jsonData)
       } catch(e) {
         console.log('')
       }
     }
    
     scheduler()
       .then(sch => console.log('Fetch Email reminder'))
       .catch(err => retryFetchAttempt(retryAttempt, maxAttempts, scheduler, ''))
    
     });
    
  4. The following route is called by the 1st fetch call. This one generates a call forEach DB record that matches the parameters. Then, it'll trigger a reminder via email. See the code below:

    // Route used by cron at 11:30am (Mon-Fri) to send email reminders about non-returned equipment

     router.get(config.route1, (req, res) => {
     let retryAttempt = 0;
     const maxAttempts = 3;
    
     async function fetchEachScheduled() {
       try {
         await postgres('metsupply').select('*').then(data => {
           if (data[0] !== undefined) {
             data.forEach(async (record, index) => {
               try {
                 setTimeout(async function() {
                   try {
                     const fetchResult = await fetch(process.env.REDIRECT_URI + config.route2, {
                       method: 'POST',
                       headers: {
                         'Content-Type': 'application/json',
                         'auth-token': process.env.API_TOKEN
                       },
                       body: JSON.stringify({
                         record: record,
                         index: index
                       })
                     });
                     const response = await fetchResult;
                     const jsonData = await response.json();
                     console.log(jsonData)
                   } catch(e) {
                     console.log(e)
                     console.log(record)
                   }
                 }, 1000 * index)
               } catch(e) {
                 console.log(e)
               }
             })
           } else {
             console.log(`Job finished at ${new Date().toString().split(" GMT",1)} -- No data`)
           }
           res.json('Closed query');
         })
       } catch(e) {
         console.log(e)
       }
     }
    
     fetchEachScheduled()
       .then(sch => console.log('Email reminder started'))
       .catch(err => retryFetchAttempt(retryAttempt, maxAttempts, fetchEachScheduled, ''))
     })
    
  5. Finally (yes... Finally!). The function below processes the forEach call.

     router.post(config.route2, async (req, res) => {
     try {
     const {
       record,
       index
     } = req.body;
    
     const currentDate = new Date();
     const currentDateString = currentDate.toString().split(" GMT",1);
     let retryAttempt = 0;
     const recordDate = new Date(record.jsdate.toString());
     recordDate.setDate(recordDate.getDate() + 1);
    
     const output = emailTemplates.supplyReminder(record);
     const sender = process.env.SUPPLY_SENDER;
     const emailRecipients = config.emailRecipient;
     const bccRecipients = [];
     const getSubject = 'Return equipment reminder';
     const refreshToken = process.env.SUPPLY_REFRESH_TOKEN;
     const clientid = process.env.SUPPLY_CLIENT_ID;
     const client_secret = process.env.SUPPLY_CLIENT_SECRET;
     const REDIRECT_URI = process.env.REDIRECT_URI;
    
    
     if ((currentDate >= recordDate) && (record.returned === 'No') && (record.needtoreplace === 'No') && (record.requesttype === 'Loan') && (record.snooze === 'No') && (record.notificationsoff === 'No')) {
       setTimeout(async function() {
         await sendEmail(
           sender,
           emailRecipients,
           bccRecipients,
           getSubject,
           output,
           refreshToken,
           clientid,
           client_secret,
           REDIRECT_URI
         ).then(async result => {
           //console.log('Email sent...', result);
         })
         .catch(err => {
             retryAttempt += 1;
             retryEmail(
               retryAttempt,
               sender,
               emailRecipients,
               bccRecipients,
               getSubject,
               output,
               refreshToken,
               clientid,
               client_secret,
               REDIRECT_URI
               );
           }
         )
       }, 1000);
       postgres(config.maindb).update('emailcount', record.emailcount + 1).where('requestid', record.requestid).then(async counter => {
         res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Sending Email`)
       }).catch(e => console.log(e))
     }
     if ((currentDate < recordDate) && (record.returned === 'No') && (record.needtoreplace === 'No') && (record.requesttype === 'Loan') && (record.snooze === 'No') && (record.notificationsoff === 'No')) {
       res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send -- Time is not up`)
     }
     if ((record.snooze === 'Yes') && (new Date().getTime() - new Date(record.lastmodified).getTime() >= (1000 * 60 * 60 * 24 * 7))) {
       await postgres(config.maindb).update({
         emailcount: record.emailcount + 1,
         snooze: 'No'
       }).where('requestid', record.requestid)
       .then(counter => {
         res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send -- Snooze: ${new Date().getTime() - new Date(record.lastmodified).getTime()}`)
       })
       .catch(e => console.log(e))
     }
     if ((record.returned === 'Yes') || (record.needtoreplace === 'Yes') || (record.notificationsoff === 'Yes') || (record.requesttype === 'Replace')) {
       res.json(`Job finished at ${new Date().toString().split(" GMT",1)} -- Nothing to send`)
     }
    } catch(e) {
      console.log(e)
    }
    })
    
Abak
  • 41
  • 1
  • 11

0 Answers0