/** {@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;
    }