Search code examples
javajava-8scheduled-tasksscheduleretry-logic

Retry function call n times when function throw exception in y interval of time


I wanted to retry my function call whenever it get failed in some duration of time. What is the best way do this. Is this will work fine.

CompletableFuture.runAsync(() -> {
    for (int i = 0; i < 3; i++) {
        try {
            dndService.initateDNDRequest(transactionId, circle, category, "PREPAID");       
            break;
        } catch (Exception e) {
            try {
                TimeUnit.SECONDS.sleep(10);//wait for few minutes while next attempt
            } catch (InterruptedException e1) {
                LOGGER.error("Error while retrying request for DND.");
            }
            LOGGER.error("Request retry for DND count"+i);
        }
    }
}, executor);

Solution

  • You should not put an executor’s worker thread to sleep.

    One solution to schedule a new attempt, would be

        Executor executor; // … the actual executor
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
        Executor afterTenSeconds
            = r -> ses.schedule(() -> executor.execute(r), 10, TimeUnit.SECONDS);
    
        Runnable primaryAction
            = () -> dndService.initateDNDRequest(transactionId, circle, category, "PREPAID");
    
        CompletableFuture<Void> cf = CompletableFuture.runAsync(primaryAction, executor);
        for(int i = 0; i < 3; i++) {
            cf = cf.handle((v,t) -> t == null? CompletableFuture.completedFuture(v):
                                    CompletableFuture.runAsync(primaryAction, afterTenSeconds))
                   .thenCompose(Function.identity());
        }
    

    Then handle action will schedule a new attempt, to be executed after the timeout (ten seconds) in the failure case. The thenCompose(Function.identity()) is necessary as there is no single method to combine handle and compose semantics.

    Note that starting with Java 9, you can create the delayed executor as simple as

    Executor afterTenSeconds = CompletableFuture.delayedExecutor(10,TimeUnit.SECONDS,executor);
    

    without the need to deal with ScheduledExecutorService yourself.