Pagination in the Amazon EC2 API
We recommend that you use pagination when calling describe actions that can potentially
return a large number of results, such as DescribeInstances
. Using pagination
bounds the number of items returned by a describe call and the time it takes for the call to
return. If you have a large number of resources, unpaginated calls might be throttled and
could time out. Therefore, overall latency is better with paginated calls than with
unpaginated calls because paginated calls are consistently successful.
For more information, see Pagination in the Amazon EC2 API Reference.
Best practices
Where possible, specify a list of resource IDs in your describe calls. This is the fastest way to describe a large number of resources. Note that you should not specify more than 1,000 IDs in a single call. The following is an example.
private List<Reservation> describeMyInstances(List<String> ids){ if (ids == null || ids.isEmpty()) { return ImmutableList.of(); } final DescribeInstancesRequest request = new DescribeInstancesRequest() .withInstanceIds(ids); return ec2.describeInstances(request).getReservations(); }
If you can't specify resource IDs in your describe calls, we strongly recommend using pagination. The following is an example.
private List<Reservation> describeMyInstances(final Collection<Filter> filters){ final DescribeInstancesRequest request = new DescribeInstancesRequest() .withFilters(filters) .withMaxResults(1000); List<Reservation> reservations = new ArrayList<>(); String nextToken = null; do { request.setNextToken(nextToken); final DescribeInstancesResult response = ec2.describeInstances(request); reservations.addAll(response.getReservations()); nextToken = response.getNextToken(); } while (nextToken != null); return reservations; }
If you need to retry a paginated call, use exponential back-off with jitter.
Common issues
The following are examples of code that inadvertently makes unpaginated calls.
Example issue: Passing an empty list of resource IDs
The following code uses a list of IDs. However, if the list is empty, the result is an unpaginated call.
private List<Reservation> describeMyInstances(List<String> ids){ final DescribeInstancesRequest request = new DescribeInstancesRequest() .withInstanceIds(ids); return ec2.describeInstances(request).getReservations(); }
To correct this issue, ensure that the list is not empty before making the describe call.
private List<Reservation> describeMyInstances(List<String> ids){ if (ids == null || ids.isEmpty()) { return ImmutableList.of(); // OR return Lists.newArrayList(); // OR return new ArrayList<>(); } final DescribeInstancesRequest request = new DescribeInstancesRequest() .withInstanceIds(ids); return ec2.describeInstances(request).getReservations(); }
Example issue: Not setting MaxResults
The following code checks and uses nextToken
, but does not set
MaxResults
.
private List<Reservation> describeMyInstances(final Collection<Filter> filters){ final DescribeInstancesRequest request = new DescribeInstancesRequest() .withFilters(filters); List<Reservation> reservations = new ArrayList<>(); String nextToken = null; do { request.setNextToken(nextToken); final DescribeInstancesResult response = ec2.describeInstances(request); reservations.addAll(response.getReservations()); nextToken = response.getNextToken(); } while (nextToken != null); return reservations; }
To correct this issue, add withMaxResults
as follows.
private List<Reservation> describeMyInstances(final Collection<Filter> filters){ final DescribeInstancesRequest request = new DescribeInstancesRequest() .withFilters(filters) .withMaxResults(1000); List<Reservation> reservations = new ArrayList<>(); String nextToken = null; do { request.setNextToken(nextToken); final DescribeInstancesResult response = ec2.describeInstances(request); reservations.addAll(response.getReservations()); nextToken = response.getNextToken(); } while (nextToken != null); return reservations; }