/**
   * Checks that cache is empty.
   *
   * @param cache Cache to check.
   * @throws org.apache.ignite.internal.IgniteInterruptedCheckedException If interrupted while
   *     sleeping.
   */
  @SuppressWarnings({"ErrorNotRethrown", "TypeMayBeWeakened"})
  private void checkEmpty(IgniteCache<String, String> cache)
      throws IgniteInterruptedCheckedException {
    for (int i = 0; i < 3; i++) {
      try {
        assertTrue(!cache.iterator().hasNext());

        break;
      } catch (AssertionError e) {
        if (i == 2) throw e;

        info(">>> Cache is not empty, flushing evictions.");

        U.sleep(1000);
      }
    }
  }
  /**
   * Executes command using {@code shell} channel.
   *
   * @param ses SSH session.
   * @param cmd Command.
   * @throws JSchException In case of SSH error.
   * @throws IOException If IO error occurs.
   * @throws IgniteInterruptedCheckedException If thread was interrupted while waiting.
   */
  private void shell(Session ses, String cmd)
      throws JSchException, IOException, IgniteInterruptedCheckedException {
    ChannelShell ch = null;

    try {
      ch = (ChannelShell) ses.openChannel("shell");

      ch.connect();

      try (PrintStream out = new PrintStream(ch.getOutputStream(), true)) {
        out.println(cmd);

        U.sleep(1000);
      }
    } finally {
      if (ch != null && ch.isConnected()) ch.disconnect();
    }
  }
  /**
   * Sends cache query response.
   *
   * @param nodeId Node to send response.
   * @param res Cache query response.
   * @param timeout Message timeout.
   * @return {@code true} if response was sent, {@code false} otherwise.
   */
  private boolean sendQueryResponse(UUID nodeId, GridCacheQueryResponse res, long timeout) {
    ClusterNode node = cctx.node(nodeId);

    if (node == null) return false;

    int attempt = 1;

    IgniteCheckedException err = null;

    while (!Thread.currentThread().isInterrupted()) {
      try {
        if (log.isDebugEnabled()) log.debug("Send query response: " + res);

        Object topic = topic(nodeId, res.requestId());

        cctx.io()
            .sendOrderedMessage(
                node, topic, res, cctx.ioPolicy(), timeout > 0 ? timeout : Long.MAX_VALUE);

        return true;
      } catch (ClusterTopologyCheckedException ignored) {
        if (log.isDebugEnabled())
          log.debug(
              "Failed to send query response since node left grid [nodeId="
                  + nodeId
                  + ", res="
                  + res
                  + "]");

        return false;
      } catch (IgniteCheckedException e) {
        if (err == null) err = e;

        if (Thread.currentThread().isInterrupted()) break;

        if (attempt < RESEND_ATTEMPTS) {
          if (log.isDebugEnabled())
            log.debug(
                "Failed to send queries response (will try again) [nodeId="
                    + nodeId
                    + ", res="
                    + res
                    + ", attempt="
                    + attempt
                    + ", err="
                    + e
                    + "]");

          if (!Thread.currentThread().isInterrupted())
            try {
              U.sleep(RESEND_FREQ);
            } catch (IgniteInterruptedCheckedException e1) {
              U.error(
                  log,
                  "Waiting for queries response resending was interrupted (response will not be sent) "
                      + "[nodeId="
                      + nodeId
                      + ", response="
                      + res
                      + "]",
                  e1);

              return false;
            }
        } else {
          U.error(
              log,
              "Failed to sender cache response [nodeId=" + nodeId + ", response=" + res + "]",
              err);

          return false;
        }
      }

      attempt++;
    }

    return false;
  }