code: private GetRecordsResult getRecords(String shardItr, int maxNumberOfRecords) throws Exception {
  		GetRecordsResult getRecordsResult = null;
  		while (getRecordsResult == null) {
  			try {
  				getRecordsResult = kinesis.getRecords(shardItr, maxNumberOfRecords);
  
  				// Update millis behind latest so it gets reported by the millisBehindLatest gauge
  				Long millisBehindLatest = getRecordsResult.getMillisBehindLatest();
  				if (millisBehindLatest != null) {
  					shardMetricsReporter.setMillisBehindLatest(millisBehindLatest);
  				}
  			} catch (ExpiredIteratorException eiEx) {
  				LOG.warn("Encountered an unexpected expired iterator {} for shard {};" +
  					" refreshing the iterator ...", shardItr, subscribedShard);
  
  				shardItr = getShardIterator(lastSequenceNum);
  
  				// sleep for the fetch interval before the next getRecords attempt with the refreshed iterator
  				if (fetchIntervalMillis != 0) {
  					Thread.sleep(fetchIntervalMillis);
  				}
  			}
  		}
  		return getRecordsResult;
  	}
docs: Calls {@link KinesisProxyInterface#getRecords(String, int)}, while also handling unexpected
  AWS {@link ExpiredIteratorException}s to assure that we get results and don't just fail on
  such occasions. The returned shard iterator within the successful {@link GetRecordsResult} should
  be used for the next call to this method.
  
  <p>Note: it is important that this method is not called again before all the records from the last result have been
  fully collected with {@link ShardConsumer#deserializeRecordForCollectionAndUpdateState(UserRecord)}, otherwise
  {@link ShardConsumer#lastSequenceNum} may refer to a sub-record in the middle of an aggregated record, leading to
  incorrect shard iteration if the iterator had to be refreshed.
  
  @param shardItr shard iterator to use
  @param maxNumberOfRecords the maximum number of records to fetch for this getRecords attempt
  @return get records result
  @throws InterruptedException