@Override
  public void schedulePush(final PullMessageTask task) {
    if (log.isDebugEnabled()) {
      log.debug(
          "Schedule push(correlation id: {}) for client: {}",
          task.getCorrelationId(),
          task.getTpg());
    }

    if (m_stopped.get() || (task.isWithOffset() && task.getStartOffset() == null)) {
      response(task, null, null);
    } else {
      m_scheduledThreadPool.submit(
          new Runnable() {
            @Override
            public void run() {
              executeTask(
                  task,
                  new ExponentialSchedulePolicy( //
                      m_config.getLongPollingCheckIntervalBaseMillis(), //
                      m_config.getLongPollingCheckIntervalMaxMillis()));
            }
          });
    }
  }
  protected void response(PullMessageTask pullTask, List<TppConsumerMessageBatch> batches) {
    PullMessageResultCommand cmd = new PullMessageResultCommand();
    if (batches != null) {
      cmd.addBatches(batches);
    }
    cmd.getHeader().setCorrelationId(pullTask.getCorrelationId());

    pullTask.getChannel().writeAndFlush(cmd);
  }
  private void executeTask(final PullMessageTask pullMessageTask, final SchedulePolicy policy) {
    if (m_stopped.get()) {
      return;
    }
    try {
      // skip expired task
      if (pullMessageTask.getExpireTime() < m_systemClockService.now()) {
        if (log.isDebugEnabled()) {
          log.debug(
              "Client expired(correlationId={}, topic={}, partition={}, groupId={})",
              pullMessageTask.getCorrelationId(),
              pullMessageTask.getTpg().getTopic(),
              pullMessageTask.getTpg().getPartition(),
              pullMessageTask.getTpg().getGroupId());
        }
        return;
      }

      if (!pullMessageTask.getBrokerLease().isExpired()) {
        if (!queryAndResponseData(pullMessageTask)) {
          if (!m_stopped.get()) {
            m_scheduledThreadPool.schedule(
                new Runnable() {

                  @Override
                  public void run() {
                    executeTask(pullMessageTask, policy);
                  }
                },
                policy.fail(false),
                TimeUnit.MILLISECONDS);
          }
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug(
              "Broker no lease for this request(correlationId={}, topic={}, partition={}, groupId={})",
              pullMessageTask.getCorrelationId(),
              pullMessageTask.getTpg().getTopic(),
              pullMessageTask.getTpg().getPartition(),
              pullMessageTask.getTpg().getGroupId());
        }
        // no lease, return empty cmd
        response(pullMessageTask, null, null);
      }
    } catch (Exception e) {
      log.error("Exception occurred while executing pull message task", e);
    }
  }
  protected void response(
      PullMessageTask pullTask, List<TppConsumerMessageBatch> batches, Offset offset) {
    Command cmd = null;
    switch (pullTask.getPullMessageCommandVersion()) {
      case 1:
        cmd = new PullMessageResultCommand();
        if (batches != null) {
          ((PullMessageResultCommand) cmd).addBatches(batches);
        }
        break;
      case 2:
      default:
        cmd = new PullMessageResultCommandV2();
        if (batches != null) {
          ((PullMessageResultCommandV2) cmd).addBatches(batches);
        }
        ((PullMessageResultCommandV2) cmd).setOffset(offset);
        break;
    }
    cmd.getHeader().setCorrelationId(pullTask.getCorrelationId());

    pullTask.getChannel().writeAndFlush(cmd);
  }
  private boolean queryAndResponseData(PullMessageTask pullTask) {
    Tpg tpg = pullTask.getTpg();

    MessageQueueCursor cursor =
        m_queueManager.getCursor(tpg, pullTask.getBrokerLease(), pullTask.getStartOffset());

    if (cursor == null) {
      return false;
    }

    Pair<Offset, List<TppConsumerMessageBatch>> p = null;

    try {
      p = cursor.next(pullTask.getBatchSize());
    } finally {
      cursor.stop();
    }

    if (p != null) {
      Offset currentOffset = p.getKey();
      List<TppConsumerMessageBatch> batches = p.getValue();

      if (batches != null && !batches.isEmpty()) {

        String ip = NettyUtils.parseChannelRemoteAddr(pullTask.getChannel(), false);
        for (TppConsumerMessageBatch batch : batches) {
          // TODO remove legacy code
          boolean needServerSideAckHolder =
              pullTask.getPullMessageCommandVersion() < 3 ? true : false;
          m_queueManager.delivered(
              batch, tpg.getGroupId(), pullTask.isWithOffset(), needServerSideAckHolder);

          bizLogDelivered(ip, batch.getMessageMetas(), tpg);
        }

        response(pullTask, batches, currentOffset);
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }