/*
   * Test the API: RepImpl.setBackupProhibited would disable the DbBackup in
   * DbBackup.startBackup, may be caused by Replay.rollback().
   */
  @Test
  public void testRollingBackDbBackupAPI() throws Throwable {

    RepEnvInfo[] repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 1);
    ReplicatedEnvironment master = RepTestUtils.joinGroup(repEnvInfo);
    RepImpl repImpl = RepInternal.getRepImpl(master);

    DbBackup backupHelper = new DbBackup(master);
    repImpl.setBackupProhibited(true);

    try {
      backupHelper.startBackup();
      fail("Should throw out a LogOverwriteException here.");
    } catch (LogOverwriteException e) {
      /* Expect a LogOverwriteException here. */
    }

    repImpl.setBackupProhibited(false);
    try {
      backupHelper.startBackup();
      backupHelper.endBackup();
    } catch (Exception e) {
      fail("Shouldn't get an exception here.");
    } finally {
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
    public void run() {
      try {
        /* Get FileLocks. */
        FileChannel channel = lockFile.getChannel();
        FileLock lockA = channel.lock(1, 1, false);
        FileLock lockC = channel.lock(3, 1, false);

        ReplicatedEnvironment master = getMaster();
        doWork(master, dbName, 1);
        /* Release lock A so that read process can do reads. */
        lockA.release();

        /* Make sure read process get lock B before this process. */
        Thread.sleep(sleepTime);

        /* Get lock B means read process finish reading, do updates. */
        FileLock lockB = getLockWithReTry(channel, 2, 1);
        doWork(master, dbName, 101);

        /* Release lock B and lock C. */
        lockB.release();
        lockC.release();
      } catch (Exception e) {
        /* Dump exceptions and exit with value 6. */
        e.printStackTrace();
        System.exit(7);
      } finally {
        RepTestUtils.shutdownRepEnvs(repEnvInfo);
        closeLockFile(lockFile);
      }
    }
  private void doReplicaHasGapNetworkRestore(boolean multiGaps) throws Throwable {

    Durability noAck =
        new Durability(SyncPolicy.NO_SYNC, SyncPolicy.NO_SYNC, ReplicaAckPolicy.NONE);
    db = null;
    try {
      Environment master = setupLogWithCleanedGaps(multiGaps);
      int masterIdx = findMasterIndex(master);
      /*
       * Write a record, so that we are sure that there will be a
       * network restore, because we have to cross a checkpoint.
       */
      generateData(master, 1, noAck, false);
      CheckpointConfig cc = new CheckpointConfig();
      master.checkpoint(cc);
      EnvironmentStats stats = master.getStats(clearConfig);
      assertEquals(0, stats.getCleanerBacklog());
      if (multiGaps) {
        logger.info("Multigap: deletion backlog = " + stats.getFileDeletionBacklog());
      } else {
        assertEquals(0, stats.getFileDeletionBacklog());
      }

      db.close();
      db = null;
      repEnvInfo[masterIdx].closeEnv();

      /* Start up the two replicas */
      openReplicas(masterIdx);

      /* Start the node that had been the master */
      try {
        repEnvInfo[masterIdx].openEnv();
        fail("Should be a network restore");
      } catch (InsufficientLogException ile) {
        repEnvInfo[masterIdx].closeEnv();
        NetworkRestore restore = new NetworkRestore();
        NetworkRestoreConfig config = new NetworkRestoreConfig();
        config.setRetainLogFiles(true);
        restore.execute(ile, config);
        repEnvInfo[masterIdx].openEnv();
      }

      /* Check its last VLSN and size. */

    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    } finally {
      if (db != null) {
        db.close();
      }
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
  /*
   * Test the API: RepImpl.invalidateDbBackups would disable the DbBackup
   * at endBackup, may be caused by Replay.rollback().
   */
  @Test
  public void testRollBackInvalidateDbBackup() throws Exception {

    RepEnvInfo[] repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 1);
    ReplicatedEnvironment master = RepTestUtils.joinGroup(repEnvInfo);
    final RepImpl repImpl = RepInternal.getRepImpl(master);

    DbBackup backupHelper = new DbBackup(master);
    backupHelper.startBackup();

    backupHelper.setTestHook(
        new TestHook<Object>() {
          public void doHook() {
            repImpl.invalidateBackups(8L);
          }

          public Object getHookValue() {
            throw new UnsupportedOperationException();
          }

          public void doIOHook() {
            throw new UnsupportedOperationException();
          }

          public void hookSetup() {
            throw new UnsupportedOperationException();
          }

          public void doHook(Object obj) {
            throw new UnsupportedOperationException();
          }
        });

    try {
      backupHelper.endBackup();
      fail("Should throw out a LogOverwriteException here.");
    } catch (LogOverwriteException e) {
      /* Expect to get a LogOverwriteException here. */
    } finally {
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
  /**
   * On the master, generate a log that has section A: a lot of records packed together section B: a
   * lot of junk that gets cleaned away, creating a gap in the log section C: a new section of data
   *
   * <p>Bring the replicas down after A is replicated, but before C is written. When the replicas
   * come up, they will have to be fed by the feeder from point A.
   */
  @Test
  public void testFeederHasGap() throws Throwable {

    Durability noAck =
        new Durability(SyncPolicy.NO_SYNC, SyncPolicy.NO_SYNC, ReplicaAckPolicy.NONE);
    db = null;
    try {
      Environment master = setupLogWithCleanedGaps(false);
      int masterIdx = findMasterIndex(master);

      /*
       * Write a single record, and then junk, so that we are sure there
       * is a new VLSN, and that the replicas will have to sync up to
       * this point, across the gap of cleaned junk.
       */
      generateData(master, 1, noAck, false);
      EnvironmentStats stats = master.getStats(clearConfig);
      assertEquals(0, stats.getCleanerBacklog());
      assertEquals(0, stats.getFileDeletionBacklog());

      /* Start up the two replicas */
      for (int i = 0; i < repEnvInfo.length; i++) {
        if (i != masterIdx) {

          repEnvInfo[i].openEnv();
          /* make sure we have up to date data */
          readData(repEnvInfo[i].getEnv(), 50);
        }
      }
    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    } finally {
      if (db != null) {
        db.close();
      }
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
 private void close(boolean normalShutdown) {
   try {
     if (normalShutdown) {
       replicaApp1.close();
       replicaApp2.close();
       masterApp.close();
       RepTestUtils.shutdownRepEnvs(repEnvInfo);
     } else {
       for (RepEnvInfo info : repEnvInfo) {
         info.abnormalCloseEnv();
       }
     }
   } finally {
     repEnvInfo = null;
     masterEnv = null;
     replicaEnv1 = null;
     replicaEnv2 = null;
     masterApp = null;
     replicaApp1 = null;
     replicaApp2 = null;
   }
 }
  @Test
  public void testReplicaHasGap() throws Throwable {

    db = null;
    try {
      Environment master = setupLogWithCleanedGaps(false);
      int masterIdx = findMasterIndex(master);
      db.close();
      db = null;
      repEnvInfo[masterIdx].closeEnv();

      /* Start up the two replicas */
      openReplicas(masterIdx);

      /* Start the master */
      try {
        repEnvInfo[masterIdx].openEnv();
      } catch (InsufficientLogException ile) {
        repEnvInfo[masterIdx].closeEnv();
        NetworkRestore restore = new NetworkRestore();
        NetworkRestoreConfig config = new NetworkRestoreConfig();
        config.setRetainLogFiles(true);
        restore.execute(ile, config);
        repEnvInfo[masterIdx].openEnv();
      }

    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    } finally {
      if (db != null) {
        db.close();
      }
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
  /** Test the basic configuration of LogFlusher. */
  @Test
  public void testBasicConfig() throws Throwable {

    try {
      EnvironmentConfig envConfig = RepTestUtils.createEnvConfig(Durability.COMMIT_NO_SYNC);
      ReplicationConfig repConfig = new ReplicationConfig();
      repConfig.setConfigParam(ReplicationMutableConfig.LOG_FLUSH_TASK_INTERVAL, "30 s");
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3, envConfig, repConfig);
      RepTestUtils.joinGroup(repEnvInfo);
      assertTrue(repEnvInfo[0].isMaster());
      /* Check the LogFlusher configuration. */
      TimerTask[] oldTasks = new TimerTask[repEnvInfo.length];
      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        oldTasks[i] = flusher.getFlushTask();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() != null);
        assertTrue(flusher.getFlushInterval() == 30000);
      }

      /* Check that those configuratins are mutable. */
      repConfig.setConfigParam(ReplicationMutableConfig.LOG_FLUSH_TASK_INTERVAL, "50 s");
      for (int i = 0; i < repEnvInfo.length; i++) {
        repEnvInfo[i].getEnv().setRepMutableConfig(repConfig);
      }

      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() != null);
        assertTrue(flusher.getFlushTask() != oldTasks[i]);
        assertTrue(flusher.getFlushInterval() == 50000);
      }

      repConfig.setConfigParam(ReplicationMutableConfig.RUN_LOG_FLUSH_TASK, "false");
      for (int i = 0; i < repEnvInfo.length; i++) {
        repEnvInfo[i].getEnv().setRepMutableConfig(repConfig);
      }

      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() == null);
      }

      RepTestUtils.shutdownRepEnvs(repEnvInfo);
      RepTestUtils.removeRepEnvironments(envRoot);

      repConfig.setConfigParam(ReplicationConfig.RUN_LOG_FLUSH_TASK, "false");
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3, envConfig, repConfig);
      RepTestUtils.joinGroup(repEnvInfo);
      /* Check that the task is disabled. */
      for (int i = 0; i < repEnvInfo.length; i++) {
        assertTrue(repEnvInfo[i].getRepNode().getLogFlusher() == null);
      }
    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    } finally {
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }
 private void closeGroup() {
   store.close();
   RepTestUtils.shutdownRepEnvs(repEnvInfo);
 }