1   package org.daisy.pipeline.tts.scheduler;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   import java.util.UUID;
6   
7   import org.daisy.pipeline.tts.scheduler.FatalError;
8   import org.daisy.pipeline.tts.scheduler.RecoverableError;
9   import org.daisy.pipeline.tts.scheduler.Schedulable;
10  import org.daisy.pipeline.tts.scheduler.Scheduler;
11  
12  /**
13   * Scheduler that increases the delay between actions launch, using a number of seconds equals to
14   * apower of 2 times retries and a random number of milliseconds.
15   *
16   * <p>This scheduler implementation is based on <a
17   * href="https://cloud.google.com/storage/docs/exponential-backoff">Google</a> and <a
18   * href="https://docs.aws.amazon.com/general/latest/gr/api-retries.html">Amazon</a> recommendations.</p>
19   *
20   * @author Louis Caille @ braillenet.org
21   *
22   * @param <Action> class of request objects to handle
23   */
24  public class ExponentialBackoffScheduler<Action extends Schedulable> implements Scheduler<Action> {
25  
26  	/**
27  	 *  maximum waiting time in millisecond
28  	 */
29  	private static final int MAXIMUM_BACKOFF = 64000;
30  
31  	/**
32  	 * Waiting time computed following <a
33  	 * href="https://cloud.google.com/storage/docs/exponential-backoff">Google</a> and <a
34  	 * href="https://docs.aws.amazon.com/general/latest/gr/api-retries.html">Amazon</a>
35  	 * recommendations.
36  	 *
37  	 * @return the waiting time in milliseconds
38  	 */
39  	private long waitingTime(int attemptCounter){
40  		long random_number_milliseconds = (long)(Math.random() * 1000);
41  		long waitingTime = (long)Math.min(Math.pow(2, attemptCounter) * 1000L + random_number_milliseconds, MAXIMUM_BACKOFF);
42  		return waitingTime;
43  	}
44  
45  	/**
46  	 * When a recoverable error is thrown, the scheduler keep the action in its queue and delays all
47  	 * launches by sleeping the thread while in locked state
48  	 */
49  	public void launch(Action action) throws InterruptedException, FatalError {
50  		int attempt = 0;
51  		boolean retry = true;
52  		while (retry) {
53  			try {
54  				action.launch();
55  				retry = false;
56  			} catch (RecoverableError e){
57  				Thread.sleep(waitingTime(attempt));
58  				attempt++;
59  			}
60  		}
61  	}
62  }