/*
   * Provoke sufficient log cleaning to move the entire VLSN right
   * sufficiently that the new VLSN range no longer overlaps the VLSN
   * range upon entry thus guaranteeing a LogFileRefreshException.
   */
  private void shiftVLSNRight(ReplicatedEnvironment menv) {
    /* Shift the vlsn range window. */

    RepImpl menvImpl = repEnvInfo[0].getRepImpl();
    final VLSNIndex vlsnIndex = menvImpl.getRepNode().getVLSNIndex();
    VLSN masterHigh = vlsnIndex.getRange().getLast();

    CheckpointConfig checkpointConfig = new CheckpointConfig();
    checkpointConfig.setForce(true);

    do {

      /*
       * Populate just the master, leaving the replica behind Re-populate
       * with the same keys to create Cleaner fodder.
       */
      populateDB(menv, TEST_DB_NAME, 100);

      /*
       * Sleep to permit the cbvlsn on the master to be updated. It's
       * done with the period: FeederManager.MASTER_CHANGE_CHECK_TIMEOUT
       */
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        fail("unexpected interrupt");
      }
      menv.cleanLog();
      menv.checkpoint(checkpointConfig);
    } while (masterHigh.compareTo(vlsnIndex.getRange().getFirst()) > 0);
  }
  /**
   * In this test, we often want to check that the last item in the replicated stream is not a
   * matchpoint candidate (that VLSNRange.lastVLSN != VLSNRange.lastSync) There's nothing wrong
   * intrinsically with that being so, it's just that this test is trying to ensure that we test
   * partial rollbacks.
   *
   * @return lastVLSN
   * @throws InterruptedException
   */
  private VLSN ensureDistinctLastAndSyncVLSN(ReplicatedEnvironment master, RepEnvInfo[] repEnvInfo)
      throws InterruptedException {

    VLSNIndex vlsnIndex = RepInternal.getRepImpl(master).getVLSNIndex();
    VLSNRange range = vlsnIndex.getRange();
    VLSN lastVLSN = range.getLast();
    VLSN syncVLSN = range.getLastSync();
    assertFalse("lastVLSN = " + lastVLSN + " syncVLSN = " + syncVLSN, lastVLSN.equals(syncVLSN));

    return lastVLSN;
  }
Пример #3
0
    /**
     * Check the RepNode.currentCommitVLSN difference to see if there is any dirty data between two
     * actions. We only do the flush when there exists dirty data.
     *
     * <p>The reason that why we only cares about the commit VLSN is those unlogged
     * uncommitted/abort transaction will be aborted during recovery. It's useless to keep track of
     * those VLSNs.
     */
    @Override
    public void run() {
      final VLSN newTxnEndVLSN = repNode.getCurrentTxnEndVLSN();

      /* Do nothing if no updates. */
      if (newTxnEndVLSN == null) {
        return;
      }

      if (lastTxnEndVLSN == null || newTxnEndVLSN.compareTo(lastTxnEndVLSN) == 1) {
        lastTxnEndVLSN = newTxnEndVLSN;
        repNode.getRepImpl().getLogManager().flush();
      }
    }
Пример #4
0
 /** @see Loggable#writeToLog */
 public void writeToLog(ByteBuffer buffer) {
   LogUtils.writePackedLong(buffer, matchpointVLSN.getSequence());
   LogUtils.writePackedLong(buffer, matchpointLSN);
   LogUtils.writeTimestamp(buffer, time);
   LogUtils.writePackedInt(buffer, activeTxnIds.size());
   for (Long id : activeTxnIds) {
     LogUtils.writePackedLong(buffer, id);
   }
 }
Пример #5
0
 public void setLastAppliedVLSN(VLSN justApplied) {
   if (justApplied.compareTo(lastApplied) <= 0) {
     throw EnvironmentFailureException.unexpectedState(
         "Txn "
             + getId()
             + " attempted VLSN = "
             + justApplied
             + " txnLastApplied = "
             + lastApplied);
   }
   this.lastApplied = justApplied;
 }
Пример #6
0
  /** @see Loggable#getLogSize */
  public int getLogSize() {
    int size =
        LogUtils.getPackedLongLogSize(matchpointVLSN.getSequence())
            + LogUtils.getPackedLongLogSize(matchpointLSN)
            + LogUtils.getTimestampLogSize(time)
            + LogUtils.getPackedIntLogSize(activeTxnIds.size());

    for (Long id : activeTxnIds) {
      size += LogUtils.getPackedLongLogSize(id);
    }

    return size;
  }
Пример #7
0
  /** @see Loggable#logicalEquals */
  public boolean logicalEquals(Loggable other) {

    if (!(other instanceof RollbackStart)) {
      return false;
    }

    RollbackStart otherRS = (RollbackStart) other;

    return (matchpointVLSN.equals(otherRS.matchpointVLSN)
        && (matchpointLSN == otherRS.matchpointLSN)
        && time.equals(otherRS.time)
        && activeTxnIds.equals(otherRS.activeTxnIds));
  }
Пример #8
0
  /** @see Loggable#dumpLog */
  public void dumpLog(StringBuilder sb, boolean verbose) {
    sb.append(" matchpointVLSN=").append(matchpointVLSN.getSequence());
    sb.append(" matchpointLSN=");
    sb.append(DbLsn.getNoFormatString(matchpointLSN));

    /* Make sure the active txns are listed in order, partially for the sake
     * of the LoggableTest unit test, which expects the toString() for two
     * equivalent objects to always display the same, and partially for
     * ease of debugging.
     */
    List<Long> displayTxnIds = new ArrayList<Long>(activeTxnIds);
    Collections.sort(displayTxnIds);
    sb.append(" activeTxnIds=").append(displayTxnIds);
    sb.append("\" time=\"").append(time);
  }
  /**
   * This is really multiple tests in one. It tests network restore with a replica in each of the
   * following three states:
   *
   * <p>1) A brand new node joining the group and needing a network restore.
   *
   * <p>2) An existing node with its own unique log needing a network restore.
   *
   * <p>3) Repeated network restores, reflecting a mature node.
   */
  @Test
  public void testBasic() throws DatabaseException, Exception {

    /*
     * The cleaner thread can see InsufficientLogExceptions so just stifle
     * those exceptions from stderr.
     */
    DaemonThread.stifleExceptionChatter = true;

    configureForMaxCleaning(2);

    final RepEnvInfo info1 = repEnvInfo[0];
    RepEnvInfo info2 = repEnvInfo[1];

    ReplicatedEnvironment masterRep = info1.openEnv();
    Environment menv = masterRep;
    EnvironmentMutableConfig mconfig = menv.getMutableConfig();
    mconfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER.getName(), "false");
    menv.setMutableConfig(mconfig);

    /*
     * Have just the master join first. We do this to test the special case
     * of a brand new node joining a group and needing VLSN 1. The same
     * node then rejoins with its VLSN > 1 to test subsequent rejoins
     * where the node has already participated in the replication.
     */
    populateDB(masterRep, TEST_DB_NAME, 100);

    mconfig = menv.getMutableConfig();
    mconfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER.getName(), "true");
    menv.setMutableConfig(mconfig);

    File cenvDir = info2.getEnvHome();
    final int cid = 2;

    for (int i = 0; i < RESTORE_CYCLES; i++) {

      leaveGroupAllButMaster();
      shiftVLSNRight(masterRep);
      RepNodeImpl memberPrev =
          info1.getRepNode().getGroup().getMember(info2.getRepConfig().getNodeName());
      /* Node1 is not known on the first iteration. */
      final VLSN prevSync = (i == 0) ? null : memberPrev.getBarrierState().getLastCBVLSN();
      try {
        /* Should force a network restore. */
        setExceptionListener(info2);
        info2.openEnv();
        fail("exception expected");
      } catch (InsufficientLogException e) {
        RepNodeImpl member =
            info1.getRepNode().getGroup().getMember(info2.getRepConfig().getNodeName());

        /*
         * The sync state should have been advanced to help contribute
         * to the global CBVLSN and prevent it from advancing.
         */
        final VLSN currSync = member.getBarrierState().getLastCBVLSN();
        assertTrue((i == 0) || currSync.compareTo(prevSync) >= 0);

        NetworkRestore networkRestore = new NetworkRestore();
        networkRestore.execute(e, new NetworkRestoreConfig());
        final NetworkBackupStats stats = networkRestore.getNetworkBackupStats();
        assertThat(stats.getExpectedBytes(), greaterThan(0));
        assertThat(stats.getTransferredBytes(), greaterThan(0));
        /* Create a replacement replicator. */
        info2 = RepTestUtils.setupEnvInfo(cenvDir, RepTestUtils.DEFAULT_DURABILITY, cid, info1);
        setExceptionListener(info2);
        info2.openEnv();
      }
      /* Verify that we can continue with the "restored" log files. */
      populateDB(masterRep, TEST_DB_NAME, 100, 100);
      VLSN commitVLSN = RepTestUtils.syncGroupToLastCommit(repEnvInfo, 2);
      RepTestUtils.checkNodeEquality(commitVLSN, false, repEnvInfo);
      info2.closeEnv();
    }
  }