Saturday, September 28, 2013

Paginate and Play Safe

Let's write a  simple application 'LicenseDB' that exposes an API to list the vehicle numbers in the United States.

public void printNumbers() {
    LicenseDB db = new LicenseDB();
    //print db.getVehicleNumbers();
}

Congratulations! We've just blown up someone's browser or mobile or a tablet or the caller is timed out before getting a response from us. Whenever we have to expose a list API, we should be careful about the amount of data we would end up dumping. We can get around this issue by paginating our result set.

How do we paginate ?
Lets rewrite our LicenseDB API so that when a client calls getVehicleNumbers(), it returns a continuation token along with a small list of numbers.

What is a continuation token ?
From a client's perspective, a continuation token is a unique identifier returned by getVehicleNumbers() which when passed to getVehicleNumbers() on a subsequent call, will return the next set of vehicle numbers.

In getVehicleNumbers() implementation, a continuation token is a bookmark that identifies the next list of vehicle numbers that gets returned to the caller. Let's give it a shot.

class ResultSet {
    private final List <String> results;
    private final String continuationToken;

    public ResultSet(List<String> results, String continuationToken) {
        this.results = result;
        this.continuationToken = continuationToken;
    }

    public List <String> getResults() {
        return results;
    }

    public String getContinuationToken() {
        return continuationToken;
    }
}

class LicenseDB {
    public ResultSet getVehicleNumbers(String continuationToken) {
        // parse the continuationToken to get the bookmark info.
        List <String> result = db.retrieve(bookmark);
        // construct a new continuation token. If there are no more results, return a null.
        String continuationToken = constructNewContinuationToken();
        return new ResultSet(result, continuationToken);
    }
}
A naive client side code would do something like,

public void printNumbers() {
    String continuationToken = null;
    do {
        ResultSet result =  db.getVehicleNumbers(continuationToken);
        continuationToken = result.getContinuationToken();
        // print result.getResults();
        // break if user doesn't want more results.
    } while (continuationToken != null);
}
We can also add the size of the result set along with continuation token on calls to getVehicleNumbers(), so that clients can control the size of the result they get every time.

What have we done ?
If we have to expose a list API without pagination as a web service, preparing the whole list in one HTTP request on our end is a bad customer experience. Often they will time out or their browser will run out of memory once we dump the response.

By returning a paginated result, we have given the control over to the clients to decide what they want from our API.

~Surya.

No comments:

Post a Comment