private void doRun() {
    if (isSuspended()) {
      LOG.trace("Cannot start to poll: {} as its suspended", this.getEndpoint());
      return;
    }

    // should we backoff if its enabled, and either the idle or error counter is > the threshold
    if (backoffMultiplier > 0
            // either idle or error threshold could be not in use, so check for that and use
            // MAX_VALUE if not in use
            && (idleCounter
                >= (backoffIdleThreshold > 0 ? backoffIdleThreshold : Integer.MAX_VALUE))
        || errorCounter
            >= (backoffErrorThreshold > 0 ? backoffErrorThreshold : Integer.MAX_VALUE)) {
      if (backoffCounter++ < backoffMultiplier) {
        // yes we should backoff
        if (idleCounter > 0) {
          LOG.debug(
              "doRun() backoff due subsequent {} idles (backoff at {}/{})",
              new Object[] {idleCounter, backoffCounter, backoffMultiplier});
        } else {
          LOG.debug(
              "doRun() backoff due subsequent {} errors (backoff at {}/{})",
              new Object[] {errorCounter, backoffCounter, backoffMultiplier});
        }
        return;
      } else {
        // we are finished with backoff so reset counters
        idleCounter = 0;
        errorCounter = 0;
        backoffCounter = 0;
        LOG.trace("doRun() backoff finished, resetting counters.");
      }
    }

    int retryCounter = -1;
    boolean done = false;
    Throwable cause = null;
    int polledMessages = 0;

    while (!done) {
      try {
        cause = null;
        // eager assume we are done
        done = true;
        if (isPollAllowed()) {

          if (retryCounter == -1) {
            LOG.trace("Starting to poll: {}", this.getEndpoint());
          } else {
            LOG.debug("Retrying attempt {} to poll: {}", retryCounter, this.getEndpoint());
          }

          // mark we are polling which should also include the begin/poll/commit
          polling = true;
          try {
            boolean begin = pollStrategy.begin(this, getEndpoint());
            if (begin) {
              retryCounter++;
              polledMessages = poll();
              LOG.trace("Polled {} messages", polledMessages);

              if (polledMessages == 0 && isSendEmptyMessageWhenIdle()) {
                // send an "empty" exchange
                processEmptyMessage();
              }

              pollStrategy.commit(this, getEndpoint(), polledMessages);

              if (polledMessages > 0 && isGreedy()) {
                done = false;
                retryCounter = -1;
                LOG.trace("Greedy polling after processing {} messages", polledMessages);
              }
            } else {
              LOG.debug("Cannot begin polling as pollStrategy returned false: {}", pollStrategy);
            }
          } finally {
            polling = false;
          }
        }

        LOG.trace("Finished polling: {}", this.getEndpoint());
      } catch (Exception e) {
        try {
          boolean retry = pollStrategy.rollback(this, getEndpoint(), retryCounter, e);
          if (retry) {
            // do not set cause as we retry
            done = false;
          } else {
            cause = e;
            done = true;
          }
        } catch (Throwable t) {
          cause = t;
          done = true;
        }
      } catch (Throwable t) {
        cause = t;
        done = true;
      }

      if (cause != null && isRunAllowed()) {
        // let exception handler deal with the caused exception
        // but suppress this during shutdown as the logs may get flooded with exceptions during
        // shutdown/forced shutdown
        try {
          getExceptionHandler()
              .handleException(
                  "Consumer "
                      + this
                      + " failed polling endpoint: "
                      + getEndpoint()
                      + ". Will try again at next poll",
                  cause);
        } catch (Throwable e) {
          LOG.warn("Error handling exception. This exception will be ignored.", e);
        }
      }
    }

    if (cause != null) {
      idleCounter = 0;
      errorCounter++;
    } else {
      idleCounter = polledMessages == 0 ? ++idleCounter : 0;
      errorCounter = 0;
    }
    LOG.trace("doRun() done with idleCounter={}, errorCounter={}", idleCounter, errorCounter);

    // avoid this thread to throw exceptions because the thread pool wont re-schedule a new thread
  }