0

What I want to do:

I want to implement a retry mechanism for function checkExpiryForADate. There is one part within function checkExpiryForADate where we will call externalService.getRecord, which could return an empty list.

If this is the case, I want to rerun the function every 10 min until it does not return an empty list (Once externalService.getRecord does not return an empty list when we run checkExpiryForADate, we would not run it anymore within the scheduled timeframe (from 7AM to 9AM)

I have changed checkExpiryForADate to be like this to implement the retry mechanism

What I have tried:

@Scheduled(cron = "${scheduler: */10 7-8 * * MON-FRI}")
public void loadAndCheckExpiry() {
  boolean checkedRecordIsAvailable = false; //initialise the flag as false
  if (!checkedRecordIsAvailable) { //if flag is false, run `checkExpiryForADate`
      checkedRecordIsAvailable = checkExpiryForADate(dateService.today()); //update flag's value
  }
}

public boolean checkExpiryForADate(LocalDate asOf)
{
    Collection<Product> listOfProducts = productService.getProducts(asOf)

    for (Product product : listOfProducts) {

        //What I want to do here is that: if 1 of the products couldn't get `expiryRecord` in the `checkExpiry` function, 
        then we return false for the this `checkExpiryForADate` function

        if (!checkExpiry(product, asOf) {
            //get out of the for loop
            return false;
        }
    }

private boolean checkExpiry(Product product, LocalDate asOf) {
    //code to perform some operations - omitted

    List<Pair<ZonedDateTime, Double>> expiryRecords = new ArrayList<>(externalService.getRecord());
    if (expiryRecords.isEmpty()) {
        return false;
    }
    
    //code to perform some operations - omitted
    return true;
}

The problem with my code:

  1. In the loadAndCheckExpiry function, whenever the function is being run, checkedRecordIsAvailable will be initialised to false again. Is it that I should put the flag outside of the loadAndCheckExpiry? If so, it could be updated to true between 7-8AM (the schedule) - but I need to set it to false again everyday for the next day.

  2. Is it a correct way for me to update the flag using this code checkedRecordIsAvailable = checkExpiryForADate(dateService.today());? (When checkExpiryForADate returns true, the boolean checkedRecordIsAvailable will indeed be updated?

    In addition, There is a warning of redundant assignment saying that the value checkExpiryForADate(dateService.today()) assigned to checkedRecordIsAvailable is never used. But I think I am using it in a if statement?

  3. In checkExpiryForADate, if for either 1 of the product incheckExpiry(product, asOf) returns false, then I want to break out of the for loop and return false for checkExpiryForADate directly. Is this the correct way to do it?

  4. In general, I think my code is very cumbersome, I wonder if there is any more elegant way to do it?

Any help would be greatly appreciated, thanks in advance.

exponents
  • 33
  • 4
w97802
  • 109
  • 6

1 Answers1

0

I think this question is similar to How to implement retry mechanism with exception and try catch, here is a possible solution:

Defining lastSucess instead of checkedRecordIsAvailable as the flag for checking, outside of the loadAndCheckExpiry function:

private Long lastSuccess = null;

loadAndCheckExpiry:

@Scheduled(cron = "${scheduler: */10 7-8 * * MON-FRI}")
public void loadAndCheckExpiry() {
    if (!sameDate(lastSuccess, System.currentTimeMillis())) { //if flag is false, run `checkExpiryForADate`
        checkExpiryForADate(dateService.today()); //update flag's value
    }
}

checkExpiryForADate, breaking the loop if checkExpiry returns false for any product, setting lastSuccess to current time if every products passes checkExpiry:

private void checkExpiryForADate(LocalDate asOf){
    Collection<Product> listOfProducts = productService.getProducts(asOf);
    for (Product product : listOfProducts) {
        if (!checkExpiry(product, asOf)) return;
    }
    lastSuccess = System.currentTimeMillis();
}

checkExpiry:

private boolean checkExpiry(Product product, LocalDate asOf) {
    // your code ...
    List<Pair<ZonedDateTime, Double>> expiryRecords = new ArrayList<>(externalService.getRecord());

    // your code ...
    return !expiryRecords.isEmpty();
}

sameDate for checking two Long values to be within the same date, returning false if either value is null:

private Boolean sameDate(Long d1, Long d2){
    if(d1 == null || d2 == null) return false;
    Calendar cal1 = Calendar.getInstance();
    Calendar cal2 = Calendar.getInstance();
    cal1.setTimeInMillis(d1);
    cal2.setTimeInMillis(d2);
    return cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
                            cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
}

Hope it can help a bit.

Phil Ku
  • 191
  • 1
  • 6