/**
   * Syncup the group and check for these requirements: - the master has all the data we expect -
   * the replicas have all the data that is on the master.
   *
   * <p>- the last VLSN is not a sync VLSN. We want to ensure that the matchpoint is not the last
   * VLSN, so the test will need to do rollback
   *
   * @throws InterruptedException
   * @return lastVLSN on the master
   */
  private VLSN checkIfWholeGroupInSync(
      ReplicatedEnvironment master, RepEnvInfo[] repEnvInfo, RollbackWorkload workload)
      throws InterruptedException {

    /*
     * Make sure we're testing partial rollbacks, and that the replication
     * stream is poised at a place where the last sync VLSN != lastVLSN.
     */
    VLSN lastVLSN = ensureDistinctLastAndSyncVLSN(master, repEnvInfo);

    RepTestUtils.syncGroupToVLSN(repEnvInfo, repEnvInfo.length, lastVLSN);

    /*
     * All nodes in the group should have the same data, and it should
     * consist of committed and uncommitted updates.
     */
    assertTrue(workload.containsAllData(master));

    /*
     * TODO: Node equality check is temporarily disabled because it (or
     * perhaps just the passing of time that allows for a heartbeat) causes
     * a GroupDB record to be written, which becomes the matchpoint and
     * defeats the test of partial rollback (because there is none).
     */
    // RepTestUtils.checkNodeEquality(lastVLSN, verbose, repEnvInfo);

    /*
     * TODO: The following fails if checkNodeEquality is called.  Perhaps
     * we should just do this here and not above at the top of the method.
     */
    lastVLSN = ensureDistinctLastAndSyncVLSN(master, repEnvInfo);

    return lastVLSN;
  }
  /**
   * Since the master never dies in this test, no rollbacks should occur, but no data should be lost
   * either.
   *
   * <p>TODO: Should the workload param be of a different class (not a RollbackWorkload), since its
   * masterSteadyWork method is only called here?
   */
  private void replicasDieAndRejoin(RollbackWorkload workload, int numIterations) throws Throwable {

    RepEnvInfo[] repEnvInfo = null;

    try {
      /* Create a  3 node group. Assign identities. */
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3);
      ReplicatedEnvironment master = RepTestUtils.joinGroup(repEnvInfo);
      logger.severe("master=" + master);

      RepEnvInfo replicaA = null;
      RepEnvInfo replicaB = null;

      for (RepEnvInfo info : repEnvInfo) {
        if (info.getEnv().getState().isMaster()) {
          continue;
        }

        if (replicaA == null) {
          replicaA = info;
        } else {
          replicaB = info;
        }
      }

      /*
       * For the sake of easy test writing, make sure numIterations is an
       * even number.
       */
      assertTrue((numIterations % 2) == 0);
      replicaA.abnormalCloseEnv();
      for (int i = 0; i < numIterations; i++) {
        workload.masterSteadyWork(master);
        waitForReplicaToSync(master, repEnvInfo);
        if ((i % 2) == 0) {
          flushLogAndCrash(replicaB);
          replicaA.openEnv();
        } else {
          flushLogAndCrash(replicaA);
          replicaB.openEnv();
        }
        waitForReplicaToSync(master, repEnvInfo);
      }
      replicaA.openEnv();

      VLSN lastVLSN = RepInternal.getRepImpl(master).getVLSNIndex().getRange().getLast();
      RepTestUtils.syncGroupToVLSN(repEnvInfo, repEnvInfo.length, lastVLSN);

      assertTrue(workload.containsAllData(master));
      RepTestUtils.checkNodeEquality(lastVLSN, verbose, repEnvInfo);

      workload.close();
      for (RepEnvInfo repi : repEnvInfo) {
        /*
         * We're done with the test. Bringing down these replicators
         * forcibly, without closing transactions and whatnot.
         */
        repi.abnormalCloseEnv();
      }
    } catch (Throwable e) {
      e.printStackTrace();
      throw e;
    }
  }