private void doTestIterator(final OP iteratorType, final int redundancy, final OP op) {
    VM accessor = VM.getVM(0);
    VM datastore1 = VM.getVM(1);
    VM datastore2 = VM.getVM(2);
    initAccessorAndDataStore(accessor, datastore1, datastore2, redundancy);

    final TXId txId = (TXId) accessor.invoke(new SerializableCallable<Object>() {
      @Override
      public Object call() {
        Region custRegion = getCache().getRegion(CUSTOMER);
        Set originalSet;
        TXManagerImpl mgr = getCache().getTxManager();
        mgr.begin();
        switch (iteratorType) {
          case KEYS:
            originalSet = getCustIdSet(5);
            assertThat(originalSet.containsAll(custRegion.keySet())).isTrue();
            assertThat(custRegion.keySet().size()).isEqualTo(5);
            break;
          case VALUES:
            originalSet = getCustomerSet(5);
            assertThat(originalSet.containsAll(custRegion.values())).isTrue();
            assertThat(custRegion.values().size()).isEqualTo(5);
            break;
          case ENTRIES:
            Set<CustId> originalKeySet = getCustIdSet(5);
            Set<Customer> originalValueSet = getCustomerSet(5);
            Set<Object> entrySet = new HashSet<Object>();
            Region.Entry entry;
            for (final Object value : custRegion.entrySet()) {
              entrySet.add(value);
            }
            for (final Object o : entrySet) {
              entry = (Entry) o;
              assertThat(originalKeySet.contains(entry.getKey())).isTrue();
              assertThat(originalValueSet.contains(entry.getValue())).isTrue();
            }
            assertThat(custRegion.entrySet().size()).isEqualTo(5);
            break;
          default:
            throw new IllegalArgumentException();
        }
        assertThat(mgr.getTXState()).isNotNull();
        return mgr.suspend();
      }
    });
    datastore1.invoke(verifyNoTxState);
    datastore2.invoke(verifyNoTxState);

    accessor.invoke(new SerializableCallable<Object>() {
      @Override
      public Object call() {
        Region custRegion = getCache().getRegion(CUSTOMER);
        Region orderRegion = getCache().getRegion(ORDER);

        TXManagerImpl mgr = getCache().getTxManager();
        mgr.resume(txId);
        assertThat(mgr.getTXState()).isNotNull();
        int expectedSetSize = 0;
        switch (op) {
          case PUT:
            CustId custId = new CustId(5);
            OrderId orderId = new OrderId(5, custId);
            custRegion.put(custId, new Customer("customer5", "address5"));
            orderRegion.put(orderId, new Order("order5"));
            expectedSetSize = 6;
            break;
          case DESTROY:
            CustId custId1 = new CustId(4);
            OrderId orderId1 = new OrderId(4, custId1);
            custRegion.destroy(custId1);
            orderRegion.destroy(orderId1);
            expectedSetSize = 4;
            break;
          default:
            throw new IllegalStateException();
        }

        switch (iteratorType) {
          case KEYS:
            assertThat(getCustIdSet(expectedSetSize).containsAll(custRegion.keySet())).isTrue();
            assertThat(expectedSetSize).isEqualTo(custRegion.keySet().size());
            break;
          case VALUES:
            assertThat(getCustomerSet(expectedSetSize).containsAll(custRegion.values())).isTrue();
            assertThat(expectedSetSize).isEqualTo(custRegion.values().size());
            break;
          case ENTRIES:
            Set originalKeySet = getCustIdSet(expectedSetSize);
            Set originalValueSet = getCustomerSet(expectedSetSize);
            Set entrySet = new HashSet();
            Region.Entry entry;
            for (final Object value : custRegion.entrySet()) {
              entrySet.add(value);
            }
            for (final Object o : entrySet) {
              entry = (Entry) o;
              assertThat(originalKeySet.contains(entry.getKey())).isTrue();
              assertThat(originalValueSet.contains(entry.getValue())).isTrue();
            }
            assertThat(expectedSetSize).isEqualTo(custRegion.entrySet().size());
            break;
          default:
            throw new IllegalArgumentException();
        }

        return mgr.suspend();
      }
    });
    final Integer txOnDatastore1 = (Integer) datastore1.invoke(getNumberOfTXInProgress);
    final Integer txOnDatastore2 = (Integer) datastore2.invoke(getNumberOfTXInProgress);
    assertThat(txOnDatastore1 + txOnDatastore2).isOne();

    accessor.invoke(new SerializableCallable<Object>() {
      @Override
      public Object call() {
        CacheTransactionManager mgr = getCache().getTxManager();
        mgr.resume(txId);
        mgr.commit();
        return null;
      }
    });
    final Integer txOnDatastore1_1 = (Integer) datastore1.invoke(getNumberOfTXInProgress);
    final Integer txOnDatastore2_2 = (Integer) datastore2.invoke(getNumberOfTXInProgress);
    assertThat(txOnDatastore1_1 + txOnDatastore2_2).isZero();

    datastore1.invoke(new SerializableCallable<Object>() {
      CustId custId;
      Customer customer;
      PartitionedRegion custRegion;
      int originalSetSize;
      int expectedSetSize;

      @Override
      public Object call() {
        TXManagerImpl mgr = getCache().getTXMgr();
        custRegion = (PartitionedRegion) getCache().getRegion(CUSTOMER);
        mgr.begin();
        doLocalOp();
        Set expectedSet;
        switch (iteratorType) {
          case KEYS:
            expectedSet = getExpectedCustIdSet();
            assertThat(expectedSet).isEqualTo(custRegion.keySet());
            assertThat(expectedSetSize).isEqualTo(custRegion.keySet().size());
            break;
          case VALUES:
            expectedSet = getExpectedCustomerSet();
            assertThat(expectedSet).isEqualTo(custRegion.values());
            assertThat(expectedSetSize).isEqualTo(custRegion.values().size());
            break;
          case ENTRIES:
            Set originalKeySet = getExpectedCustIdSet();
            Set originalValueSet = getExpectedCustomerSet();
            Set entrySet = new HashSet();
            Region.Entry entry;
            for (final Object value : custRegion.entrySet()) {
              entrySet.add(value);
            }
            for (final Object o : entrySet) {
              entry = (Entry) o;
              assertThat(originalKeySet.contains(entry.getKey())).isTrue();
              assertThat(originalValueSet.contains(entry.getValue())).isTrue();
            }
            assertThat(expectedSetSize).isEqualTo(custRegion.entrySet().size());
            break;
          default:
            throw new IllegalArgumentException();
        }
        mgr.commit();
        return null;
      }

      private void doLocalOp() {
        switch (op) {
          case PUT:
            for (int i = 6;; i++) {
              custId = new CustId(i);
              customer = new Customer("customer" + i, "address" + i);
              int bucketId = PartitionedRegionHelper.getHashKey(custRegion, custId);
              InternalDistributedMember primary = custRegion.getBucketPrimary(bucketId);
              if (primary.equals(getCache().getMyId())) {
                custRegion.put(custId, customer);
                break;
              }
            }
            originalSetSize = 6;
            expectedSetSize = 7;
            break;
          case DESTROY:
            for (int i = 3;; i--) {
              custId = new CustId(i);
              customer = new Customer("customer" + i, "address" + i);
              int bucketId = PartitionedRegionHelper.getHashKey(custRegion, custId);
              InternalDistributedMember primary = custRegion.getBucketPrimary(bucketId);
              if (primary.equals(getCache().getMyId())) {
                custRegion.destroy(custId);
                break;
              }
            }
            originalSetSize = 4;
            expectedSetSize = 3;
            break;
          default:
            throw new IllegalStateException();
        }
      }

      private Set getExpectedCustIdSet() {
        Set retVal = getCustIdSet(originalSetSize);
        switch (op) {
          case PUT:
            retVal.add(custId);
            break;
          case DESTROY:
            retVal.remove(custId);
            break;
          default:
            throw new IllegalStateException();
        }
        return retVal;
      }

      private Set getExpectedCustomerSet() {
        Set retVal = getCustomerSet(originalSetSize);
        switch (op) {
          case PUT:
            retVal.add(customer);
            break;
          case DESTROY:
            retVal.remove(customer);
            break;
          default:
            throw new IllegalStateException();
        }
        return retVal;
      }

    });
  }
When we have narrowed down on a target node for a transaction, test that we throw an exception
if that node does not host primary for subsequent entries
geode-core/src/distributedTest/java/org/apache/geode/internal/cache/RemoteTransactionDUnitTest.java