/**
   * Change topology.
   *
   * @param parent Grid to execute tasks on.
   * @param add New nodes count.
   * @param rmv Remove nodes count.
   * @param type Type of nodes to manipulate.
   */
  private static void changeTopology(Ignite parent, int add, int rmv, String type) {
    Collection<ComputeTaskFuture<?>> tasks = new ArrayList<>();

    IgniteCompute comp = parent.compute().withAsync();

    // Start nodes in parallel.
    while (add-- > 0) {
      comp.execute(ClientStartNodeTask.class, type);

      tasks.add(comp.future());
    }

    for (ComputeTaskFuture<?> task : tasks) task.get();

    // Stop nodes in sequence.
    while (rmv-- > 0) parent.compute().execute(ClientStopNodeTask.class, type);

    // Wait for node stops.
    // U.sleep(1000);

    Collection<String> gridNames = new ArrayList<>();

    for (Ignite g : G.allGrids()) gridNames.add(g.name());

    parent.log().info(">>> Available grids: " + gridNames);
  }
    /** {@inheritDoc} */
    @Override
    public Collection<? extends ComputeJob> split(int gridSize, Serializable arg) {
      if (log.isInfoEnabled())
        log.info("Splitting task [task=" + this + ", gridSize=" + gridSize + ", arg=" + arg + ']');

      Collection<GridCancelTestJob> jobs = new ArrayList<>(SPLIT_COUNT);

      for (int i = 0; i < SPLIT_COUNT; i++) jobs.add(new GridCancelTestJob());

      return jobs;
    }
  /**
   * Gets random value from given collection.
   *
   * @param c Input collection (no {@code null} and not emtpy).
   * @return Random value from the input collection.
   */
  @SuppressWarnings("UnusedDeclaration")
  private static <T> T rand(Collection<? extends T> c) {
    if (c == null) throw new IllegalArgumentException();

    int n = ThreadLocalRandom.current().nextInt(c.size());

    int i = 0;

    for (T t : c) {
      if (i++ == n) return t;
    }

    throw new ConcurrentModificationException();
  }