/* * 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; }
/** * 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(); } }
/** @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); } }
public void setLastAppliedVLSN(VLSN justApplied) { if (justApplied.compareTo(lastApplied) <= 0) { throw EnvironmentFailureException.unexpectedState( "Txn " + getId() + " attempted VLSN = " + justApplied + " txnLastApplied = " + lastApplied); } this.lastApplied = justApplied; }
/** @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; }
/** @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)); }
/** @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(); } }