public QueryStatus getQueryStatus(QueryId queryId) throws ServiceException {
    GetQueryStatusRequest.Builder builder = GetQueryStatusRequest.newBuilder();
    builder.setQueryId(queryId.getProto());

    GetQueryStatusResponse res = null;
    if (queryMasterMap.containsKey(queryId)) {
      NettyClientBase qmClient = null;
      try {
        qmClient =
            connPool.getConnection(
                queryMasterMap.get(queryId), QueryMasterClientProtocol.class, false);
        QueryMasterClientProtocolService.BlockingInterface queryMasterService = qmClient.getStub();
        res = queryMasterService.getQueryStatus(null, builder.build());
      } catch (Exception e) {
        throw new ServiceException(e.getMessage(), e);
      } finally {
        connPool.releaseConnection(qmClient);
      }
    } else {
      NettyClientBase tmClient = null;
      try {
        tmClient = connPool.getConnection(tajoMasterAddr, TajoMasterClientProtocol.class, false);
        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = tmClient.getStub();
        res = tajoMasterService.getQueryStatus(null, builder.build());

        String queryMasterHost = res.getQueryMasterHost();
        if (queryMasterHost != null && !queryMasterHost.isEmpty()) {
          NettyClientBase qmClient = null;
          try {
            InetSocketAddress qmAddr =
                NetUtils.createSocketAddr(queryMasterHost, res.getQueryMasterPort());
            qmClient = connPool.getConnection(qmAddr, QueryMasterClientProtocol.class, false);
            QueryMasterClientProtocolService.BlockingInterface queryMasterService =
                qmClient.getStub();
            res = queryMasterService.getQueryStatus(null, builder.build());

            queryMasterMap.put(queryId, qmAddr);
          } catch (Exception e) {
            throw new ServiceException(e.getMessage(), e);
          } finally {
            connPool.releaseConnection(qmClient);
          }
        }
      } catch (Exception e) {
        throw new ServiceException(e.getMessage(), e);
      } finally {
        connPool.releaseConnection(tmClient);
      }
    }
    return new QueryStatus(res);
  }
  /**
   * It submits a query statement and get a response. The main difference from {@link
   * #executeQuery(String)} is a blocking method. So, this method is wait for the finish of the
   * submitted query.
   *
   * @return If failed, return null.
   */
  public ResultSet executeQueryAndGetResult(final String sql) throws ServiceException, IOException {
    GetQueryStatusResponse response =
        new ServerCallable<GetQueryStatusResponse>(
            connPool, tajoMasterAddr, TajoMasterClientProtocol.class, false, true) {
          public GetQueryStatusResponse call(NettyClientBase client) throws ServiceException {
            final QueryRequest.Builder builder = QueryRequest.newBuilder();
            builder.setQuery(sql);
            TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
            return tajoMasterService.submitQuery(null, builder.build());
          }
        }.withRetries();

    QueryId queryId = new QueryId(response.getQueryId());
    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
      return this.createNullResultSet(queryId);
    } else {
      return this.getQueryResultAndWait(queryId);
    }
  }