Total Pageviews

Showing posts with label Failsafe. Show all posts
Showing posts with label Failsafe. Show all posts

2017/11/03

SMS Service with Failsafe Mechanism

Scenario


How-To
Making good use of failsafe library can let you handle failure easily.
Remember to add dependency into your pom.xml
1
2
3
4
5
6
        <!-- retry -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>failsafe</artifactId>
            <version>1.0.4</version>
        </dependency>


Sample code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    private void sendSmsWithRetry(final String otp, final SmsParams params) {
        final String mobilePhone = params.getMobilePhone();
        final CallerEnum sourceName = params.getSourceName();
        final String policyNumber = params.getPolicyNumber();
        final String userId = params.getUserId();

        RetryPolicy retryPolicy = new RetryPolicy();
        retryPolicy.withDelay(1, TimeUnit.SECONDS); // Sets the delay between retries.
        retryPolicy.withMaxRetries(5);              // Sets the max number of retries to perform. -1 indicates to retry forever.

        // Creates and returns a new SyncFailsafe instance that will perform executions and 
        // retries synchronously according to the retryPolicy.
        Failsafe.with(retryPolicy)
        // Registers the listener to be called when an execution is successful.
        .onSuccess(new CheckedConsumer() {
            @Override
            public void accept(Object t) throws Exception {
                // The execution is successful, then write success log into database
            }
        })
        // Registers the listener to be called when an execution attempt fails.
        .onFailedAttempt(new CheckedConsumer() {
            @Override
            public void accept(Object t) throws Exception {
                if (t instanceof Exception) {
                    Exception e = (Exception) t;
                    // The execution attempt fails, then write error log into database
                }
            }
        })
        // Registers the listener to be called when an execution fails and cannot be retried.
        .onFailure(new ContextualResultListener() {
            @Override
            public void onResult(Object result, Throwable failure, ExecutionContext context)
                    throws Exception {
                String exeMsg = "驗證碼傳送失敗。手機號碼:" + mobilePhone + ", 失敗原因:" + failure.getMessage();
                failure.printStackTrace();                
                throw new RuntimeException(exeMsg);
            }
        })
        // Executes the runnable until successful or until the configured RetryPolicy is exceeded.
        .run(new CheckedRunnable() {
            @Override
            public void run() throws Exception {
                sendSms(mobilePhone, getSmsText(params.getOtpServiceType(), otp));
            }
        });
    }

2017/04/06

[Failsafe] Retry with backoff

Problem
I would like do retry if I fail to connect to database.  
My requirement is 
  • retry 5 times at most and sleep 5 second then retry again
  • Sets the delay between retries (i.e. 5 seconds), exponentially backing off to the maxDelay (i.e. 120 seconds) and multiplying successive delays by the delayFactor (i.e. 2).

How-to
Here has the sample code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package albert.practice.retry;

import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;
import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;

@Slf4j
public class RetryTest {

    private int count = 0;

    public static void main(String[] args) throws ConnectionException {
        new RetryTest().connectWithRetry();
    }

    public Connection connectWithRetry() {
        // create a retry policy with 5 max retries and have 2 seconds delay among retries
        RetryPolicy retryPolicy = new RetryPolicy();

        // create a retry policy and sets the delay 5 seconds between retries, exponentially backing off to the maxDelay 120
        // seconds and multiplying successive delays by the delayFactor 2.
        retryPolicy.retryOn(ConnectionException.class).withMaxRetries(5).withBackoff(5, 120,
                TimeUnit.SECONDS, 2);

        // Using Fallbacks allow you to provide an alternative result for a failed execution.
        // In this example, it will retry again after 5 seconds.
        Connection conn = Failsafe.with(retryPolicy).withFallback(() -> retryIfFail())
                .get(() -> connect());

        return conn;
    }

    public void retryIfFail() throws InterruptedException {
        log.debug("GG at " + getCurrentTime());
        Thread.sleep(5000);

        log.debug("retry....." + getCurrentTime());
        connectWithRetry();
    }

    public Connection connect() throws ConnectionException {
        log.debug(" time = " + getCurrentTime());
        Connection conn = null;
        if (count < 9) {
            count++;
            throw new ConnectionException("connection fail!");
        } else {
            log.debug("get connection successfuly...");
        }
        return conn;
    }

    private String getCurrentTime() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        return dateFormat.format(new Date());
    }
}

Execution log:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:31:51.495
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:32:01.509
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:32:21.515
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:33:01.498
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:34:21.499
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:36:21.539
[main] DEBUG albert.practice.retry.RetryTest - GG at 2016/11/23 17:36:21.539
[main] DEBUG albert.practice.retry.RetryTest - retry.....2016/11/23 17:36:26.545
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:36:26.545
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:36:36.554
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:36:56.564
[main] DEBUG albert.practice.retry.RetryTest -  time = 2016/11/23 17:37:36.571
[main] DEBUG albert.practice.retry.RetryTest - get connection successfuly...

2017/04/05

[Failsafe] Retry with Fallback

Scenario
If we cannot get connection, then we will retry 5 times with 2 seconds delay.
If we fail to retry with 5 times, then we will pause X minutes then retry again.
How to implement it?


How-to
You can make good use of failsafe to fulfill this implement.
In this exampe, it will demonstrate:
1. create a retry policy with 5 max retries and have 2 seconds delay among retries
2. If you got failed execution, it will retry again after 5 seconds.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package albert.practice.retry;

import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;
import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;

@Slf4j
public class RetryTest {

    private int count = 0;

    public static void main(String[] args) throws ConnectionException {
        new RetryTest().connectWithRetry();
    }

    public Connection connectWithRetry() {
        // create a retry policy with 5 max retries and have 2 seconds delay among retries 
        RetryPolicy retryPolicy = new RetryPolicy();
        retryPolicy.retryOn(ConnectionException.class).withDelay(2, TimeUnit.SECONDS)
                .withMaxRetries(5);

        // Using Fallbacks allow you to provide an alternative result for a failed execution. 
        // In this example, it will retry again after 5 seconds.
        Connection conn = Failsafe.with(retryPolicy).withFallback(() -> retryIfFail())
                .get(() -> connect());

        return conn;
    }

    public void retryIfFail() throws InterruptedException {
        log.debug("GG at " + getCurrentTime());
        Thread.sleep(5000);

        log.debug("retry....." + getCurrentTime());
        connectWithRetry();
    }

    public Connection connect() throws ConnectionException {
        log.debug(" time = " + getCurrentTime());
        Connection conn = null;
        if (count < 9) {
            count++;
            throw new ConnectionException("connection fail!");
        } else {
            log.debug("get connection successfuly...");
        }
        return conn;
    }

    private String getCurrentTime() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        return dateFormat.format(new Date());
    }
}


Console log:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:13.832
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:15.837
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:17.837
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:19.837
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:21.838
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:23.839
DEBUG albert.practice.retry.RetryTest - GG at 2016/11/05 11:42:23.839
DEBUG albert.practice.retry.RetryTest - retry.....2016/11/05 11:42:28.839
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:28.839
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:30.839
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:32.839
DEBUG albert.practice.retry.RetryTest -  time = 2016/11/05 11:42:34.840
DEBUG albert.practice.retry.RetryTest - get connection successfuly...

Reference
[1] https://github.com/jhalterman/failsafe#fallbacks

2017/04/02

[Failsafe] Simple, sophisticated failure handling

Scenario
We have a Java application to connect to OPC server, if it fail to connect to OPC server it will retry 5 times.


How-to
You can make good use of Failsafe to fulfill this requirement. 
Failsafe is a lightweight, zero-dependency library for handling failures. It was designed to be as easy to use as possible, with a concise API for handling everyday use cases and the flexibility to handle everything else.

Maven dependency:
1
2
3
4
5
6
7
<dependencies>
   <dependency>
      <groupId>net.jodah</groupId>
      <artifactId>failsafe</artifactId>
      <version>0.9.5</version>
   </dependency>
</dependencies>


If I catch ConnectionException, it will do retry, the retry policy is as bellows:

  • it will delay 2 seconds between retries
  • it will retry 5 times at most


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package albert.practice.retry;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;
import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;

@Slf4j
public class RetryTest {

    public static void main(String[] args) throws ConnectionException {
        @SuppressWarnings("unchecked")
        RetryPolicy retryPolicy = new RetryPolicy().retryOn(ConnectionException.class)
                .withDelay(2, TimeUnit.SECONDS).withMaxRetries(5);
        Failsafe.with(retryPolicy).run(() -> new RetryTest().connect());
    }

    public void connect() throws ConnectionException {
        log.debug("time=" + new Date(Calendar.getInstance().getTimeInMillis()));
        if (true) {
             throw new ConnectionException("connection fail!");
        }
    }
}



Reference
[1] https://github.com/jhalterman/failsafe