private void runAssertion(CacheOperation operation)
      throws NotSupportedException, SystemException, HeuristicMixedException,
          HeuristicRollbackException, InvalidTransactionException, RollbackException {
    txStatus.reset();
    tm.begin();
    cache(1).put("k1", "v1");
    Transaction k1LockOwner = tm.suspend();
    assert lm1.isLocked("k1");

    assertEquals(1, txTable1.getLocalTxCount());
    tm.begin();
    cache(0).put("k2", "v2");
    assert lm0.isLocked("k2");
    assert !lm1.isLocked("k2");

    operation.execute();

    assertEquals(1, txTable1.getLocalTxCount());
    assertEquals(1, txTable0.getLocalTxCount());

    try {
      tm.commit();
      assert false;
    } catch (RollbackException re) {
      // expected
    }
    assert txStatus.teReceived;
    assert txStatus.isTxInTableAfterTeOnPrepare;
    // expect 1 as k1 is locked by the other tx
    assertEquals(
        txStatus.numLocksAfterTeOnPrepare,
        1,
        "This would make sure that locks are being released quickly, not waiting for a remote rollback to happen");

    assertEquals(0, txTable0.getLocalTxCount());
    assertEquals(1, txTable1.getLocalTxCount());

    log.trace("Right before second commit");
    tm.resume(k1LockOwner);
    tm.commit();
    assertEquals("v1", cache(0).get("k1"));
    assertEquals("v1", cache(1).get("k1"));
    assertEquals(0, txTable1.getLocalTxCount());
    assertEquals(0, txTable1.getLocalTxCount());
    assertEquals(0, lm0.getNumberOfLocksHeld());
    assertEquals(0, lm1.getNumberOfLocksHeld());
  }
 /**
  * Verifies the cache doesn't contain any lock
  *
  * @param cache
  */
 public static void assertNoLocks(Cache<?, ?> cache) {
   LockManager lm = TestingUtil.extractLockManager(cache);
   for (Object key : cache.keySet()) assert !lm.isLocked(key);
 }
  public void testCommitDoesntWriteAfterRollback() throws Exception {
    // Start a tx on A: put(k, v1), owners(k) = [B (primary) and C (backup)]
    // Block the commit on C so that it times out
    // Wait for the rollback command to be executed on B and C, and for the tx to end
    // Check that locks are released on B
    // Start another transaction on A: put(k, v2) with the same key
    // Check that the new transaction writes successfully
    // Allow the commit to proceed on C
    // Check that k=v2 everywhere
    StateSequencer sequencer = new StateSequencer();
    sequencer.logicalThread(
        "tx1",
        "tx1:begin",
        "tx1:block_commit_on_backup",
        "tx1:after_rollback_on_primary",
        "tx1:after_rollback_on_backup",
        "tx1:resume_commit_on_backup",
        "tx1:after_commit_on_backup",
        "tx1:check");
    sequencer.logicalThread("tx2", "tx2:begin", "tx2:end");

    sequencer.order(
        "tx1:after_rollback_on_backup", "tx2:begin", "tx2:end", "tx1:resume_commit_on_backup");

    advanceOnInterceptor(
            sequencer,
            cache(2),
            StateTransferInterceptor.class,
            matchCommand(CommitCommand.class).matchCount(0).build())
        .before("tx1:block_commit_on_backup", "tx1:resume_commit_on_backup")
        .after("tx1:after_commit_on_backup");

    advanceOnInterceptor(
            sequencer,
            cache(1),
            StateTransferInterceptor.class,
            matchCommand(RollbackCommand.class).build())
        .after("tx1:after_rollback_on_primary");

    advanceOnInterceptor(
            sequencer,
            cache(2),
            StateTransferInterceptor.class,
            matchCommand(RollbackCommand.class).build())
        .after("tx1:after_rollback_on_backup");

    assertEquals(
        Arrays.asList(address(1), address(2)),
        advancedCache(0).getDistributionManager().locate(TEST_KEY));
    sequencer.advance("tx1:begin");

    tm(0).begin();
    cache(0).put(TEST_KEY, TX1_VALUE);
    try {
      tm(0).commit();
    } catch (RollbackException e) {
      log.debugf("Commit timed out as expected", e);
    }

    sequencer.advance("tx2:begin");
    LockManager lockManager1 = TestingUtil.extractLockManager(cache(1));
    assertFalse(lockManager1.isLocked(TEST_KEY));

    tm(0).begin();
    cache(0).put(TEST_KEY, TX2_VALUE);
    tm(0).commit();

    checkValue();
    sequencer.advance("tx2:end");

    sequencer.advance("tx1:check");
    checkValue();
  }