/** {@inheritDoc} */
  @Override
  public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid, UUID arg) {
    assert arg != null;
    assert subgrid.size() > 1
        : "Test requires at least 2 nodes. One with load and another one to steal.";

    int jobsNum = subgrid.size();

    Map<GridStealingLoadTestJob, ClusterNode> map = new HashMap<>(jobsNum);

    stealingNodeId = arg;

    Iterator<ClusterNode> iter = subgrid.iterator();

    Collection<UUID> assigned = new ArrayList<>(subgrid.size());

    for (int i = 0; i < jobsNum; i++) {
      ClusterNode node = null;

      boolean nextNodeFound = false;

      while (iter.hasNext() && !nextNodeFound) {
        node = iter.next();

        // Do not map jobs to the stealing node.
        if (!node.id().equals(stealingNodeId)) nextNodeFound = true;

        // Recycle iterator.
        if (!iter.hasNext()) iter = subgrid.iterator();
      }

      assert node != null;

      assigned.add(node.id());

      map.put(new GridStealingLoadTestJob(), node);
    }

    taskSes.setAttribute("nodes", assigned);

    return map;
  }
    /** {@inheritDoc} */
    @Override
    public String execute() {
      assert ignite != null;
      assert taskSes != null;

      assert startLatch != null;

      assert read1Latch != null;
      assert read2Latch != null;
      assert read3Latch != null;

      assert read1FinishedLatch != null;
      assert read2FinishedLatch != null;
      assert read3FinishedLatch != null;

      assert rmvLatch != null;

      startLatch.countDown();

      try {
        startLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      X.println(">>> Producer started.");

      taskSes.saveCheckpoint(GLOBAL_KEY, GLOBAL_VAL, GLOBAL_SCOPE, 0);
      taskSes.saveCheckpoint(SES_KEY, SES_VAL, SESSION_SCOPE, 0);

      read1Latch.countDown();

      try {
        read1FinishedLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      // No retries here as other thread should have seen checkpoint already.
      assert GLOBAL_VAL.equals(taskSes.loadCheckpoint(GLOBAL_KEY));
      assert SES_VAL.equals(taskSes.loadCheckpoint(SES_KEY));

      taskSes.saveCheckpoint(GLOBAL_KEY, SES_VAL + "-notoverwritten", GLOBAL_SCOPE, 0, false);
      taskSes.saveCheckpoint(SES_KEY, GLOBAL_VAL + "-notoverwritten", SESSION_SCOPE, 0, false);

      read2Latch.countDown();

      try {
        read2FinishedLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      assert GLOBAL_VAL.equals(taskSes.loadCheckpoint(GLOBAL_KEY));
      assert SES_VAL.equals(taskSes.loadCheckpoint(SES_KEY));

      // Swap values.
      taskSes.saveCheckpoint(GLOBAL_KEY, SES_VAL_OVERWRITTEN, GLOBAL_SCOPE, 0, true);
      taskSes.saveCheckpoint(SES_KEY, GLOBAL_VAL_OVERWRITTEN, SESSION_SCOPE, 0, true);

      read3Latch.countDown();

      try {
        read3FinishedLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      assert SES_VAL_OVERWRITTEN.equals(taskSes.loadCheckpoint(GLOBAL_KEY));
      assert GLOBAL_VAL_OVERWRITTEN.equals(taskSes.loadCheckpoint(SES_KEY));

      // Remove checkpoints.
      assert taskSes.removeCheckpoint(GLOBAL_KEY);
      assert taskSes.removeCheckpoint(SES_KEY);

      // Check checkpoints are actually removed.
      assert !taskSes.removeCheckpoint(GLOBAL_KEY);
      assert !taskSes.removeCheckpoint(SES_KEY);

      rmvLatch.countDown();

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert taskSes.loadCheckpoint(GLOBAL_KEY) == null;
              assert taskSes.loadCheckpoint(SES_KEY) == null;
            }
          });

      return null;
    }
    /** {@inheritDoc} */
    @Override
    public String execute() {
      assert taskSes != null;

      assert startLatch != null;

      assert read1Latch != null;
      assert read2Latch != null;
      assert read3Latch != null;

      assert read1FinishedLatch != null;
      assert read2FinishedLatch != null;
      assert read3FinishedLatch != null;

      assert rmvLatch != null;

      startLatch.countDown();

      try {
        startLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      X.println(">>> Consumer started.");

      try {
        read1Latch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      // Test that checkpoints were saved properly.
      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert GLOBAL_VAL.equals(taskSes.loadCheckpoint(GLOBAL_KEY));
              assert SES_VAL.equals(taskSes.loadCheckpoint(SES_KEY));
            }
          });

      read1FinishedLatch.countDown();

      try {
        read2Latch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      // Test that checkpoints were not overwritten.
      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert GLOBAL_VAL.equals(taskSes.loadCheckpoint(GLOBAL_KEY));
              assert SES_VAL.equals(taskSes.loadCheckpoint(SES_KEY));
            }
          });

      read2FinishedLatch.countDown();

      try {
        read3Latch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assertEquals(SES_VAL_OVERWRITTEN, taskSes.loadCheckpoint(GLOBAL_KEY));
              assertEquals(GLOBAL_VAL_OVERWRITTEN, taskSes.loadCheckpoint(SES_KEY));
            }
          });

      read3FinishedLatch.countDown();

      try {
        rmvLatch.await();
      } catch (InterruptedException e) {
        throw new IgniteException("Thread has been interrupted.", e);
      }
      // Check checkpoints are actually removed.
      assert !taskSes.removeCheckpoint(GLOBAL_KEY);
      assert !taskSes.removeCheckpoint(SES_KEY);

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert taskSes.loadCheckpoint(GLOBAL_KEY) == null;
              assert taskSes.loadCheckpoint(SES_KEY) == null;
            }
          });

      return null;
    }
    /** {@inheritDoc} */
    @SuppressWarnings({"TooBroadScope"})
    @Override
    public String execute() {
      assert ignite != null;
      assert taskSes != null;

      final String key1 = "test-checkpoint-key1";
      final String val1 = "test-checkpoint-value1";

      final String key2 = "test-checkpoint-key2";
      final String val2 = "test-checkpoint-value2";

      String key3 = "test-checkpoint-key3";
      String val3 = "test-checkpoint-value3";

      taskSes.saveCheckpoint(key1, val1, GLOBAL_SCOPE, 0);
      taskSes.saveCheckpoint(key2, val2, SESSION_SCOPE, 0);

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert val1.equals(taskSes.loadCheckpoint(key1));
              assert val2.equals(taskSes.loadCheckpoint(key2));
            }
          });

      // Don't overwrite.
      taskSes.saveCheckpoint(key1, val2, GLOBAL_SCOPE, 0, false);
      taskSes.saveCheckpoint(key2, val1, SESSION_SCOPE, 0, false);

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert val1.equals(taskSes.loadCheckpoint(key1));
              assert val2.equals(taskSes.loadCheckpoint(key2));
            }
          });

      taskSes.saveCheckpoint(key1, val2, GLOBAL_SCOPE, 0, true);
      taskSes.saveCheckpoint(key2, val1, SESSION_SCOPE, 0, true);

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert val2.equals(taskSes.loadCheckpoint(key1));
              assert val1.equals(taskSes.loadCheckpoint(key2));
            }
          });

      assert taskSes.removeCheckpoint(key1);
      assert taskSes.removeCheckpoint(key2);
      assert !taskSes.removeCheckpoint(key1);
      assert !taskSes.removeCheckpoint(key2);

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert taskSes.loadCheckpoint(key1) == null;
              assert taskSes.loadCheckpoint(key2) == null;
            }
          });

      taskSes.saveCheckpoint(key1, val1, GLOBAL_SCOPE, 0);

      ((IgniteMXBean) ignite).removeCheckpoint(key1);

      // This checkpoint will not be automatically removed for cache SPI.
      taskSes.saveCheckpoint(key1, val1, GLOBAL_SCOPE, 5000);

      // This will be automatically removed by cache SPI.
      taskSes.saveCheckpoint(key2, val2, SESSION_SCOPE, 5000);

      try {
        Thread.sleep(6000);
      } catch (InterruptedException e) {
        throw new IgniteException(e);
      }

      assertWithRetries(
          new GridAbsClosureX() {
            @Override
            public void applyx() {
              assert taskSes.loadCheckpoint(key1) == null;
              assert taskSes.loadCheckpoint(key2) == null;
            }
          });

      // This checkpoint will be removed when task session end.
      taskSes.saveCheckpoint(key3, val3, SESSION_SCOPE, 0);

      return null;
    }