public Records fetch() throws IOException {
    int retryCount = 0;
    final QueryStatistics.Measurer statTracker = new QueryStatistics.Measurer();
    statTracker.recordQueryPrepStart();
    PreparedStatement preparedStatement = getPreparedStatement();
    statTracker.recordQueryPrepEnd();

    while (true) {
      try {
        statTracker.recordAttempt();

        statTracker.recordQueryExecStart();
        final Records queryResults = QueryFetcher.getQueryResults(preparedStatement, selectedColumns, dbConnection);
        statTracker.recordQueryExecEnd();

        final QueryStatistics statistics = statTracker.calculate();
        queryResults.addStatistics(statistics);

        try {
          postQueryAction.perform(statistics);
        } catch (Exception ignoredException) {
          LOG.error(String.format("Error occurred running post-query action %s", postQueryAction), ignoredException);
        }

        return queryResults;
      } catch (SQLRecoverableException e) {
        LOG.error(e.toString());
        if (++retryCount > MAX_CONNECTION_RETRIES) {
          throw new IOException(e);
        }
      } catch (SQLException e) {
        throw new IOException(e);
      }
    }
  }