blob: a1be171961dc2cc496a64228d0a73bb53933f2eb [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.rep.stream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.rep.impl.RepParams;
import com.sleepycat.je.txn.TxnAbort;
import com.sleepycat.je.txn.TxnCommit;
import com.sleepycat.je.utilint.DbLsn;
import com.sleepycat.je.utilint.Timestamp;
import com.sleepycat.je.utilint.VLSN;
/**
* Holds information seen by the ReplicaSyncupReader when
* scanning a replica's log for a matchpoint.
*/
public class MatchpointSearchResults implements Serializable {
private static final long serialVersionUID = 1L;
private long matchpointLSN;
/*
* Track whether we passed a checkpoint which deleted cleaned log files.
* If so, we cannot do a hard recovery.
*/
private boolean passedCheckpointEnd;
/*
* If we skip a gap in the log file when searching for a matchpoint, we
* no longer can be sure if we have passed checkpoints.
*/
private boolean passedSkippedGap;
/*
* We save a set number of passed transactions for debugging information.
* The list is limited in size in case we end up passing a large number
* of transactions on our hunt for a matchpoint. Note that we save both
* durable and non-durable transactions to aid with debugging.
*/
private final List<PassedTxnInfo> passedTxns;
private final int passedTxnLimit;
/*
* We need to keep the penultimate passed txn so we can readjust the
* passed transaction information when the matchpoint is found. For
* example, suppose the log contains:
* txn A commit
* txn B commit
* txn C commit
* and txn A commit is the matchpoint. The way the loop works, we'll
* have earliestPassedTxn = txnA, and penultimate = txn B. If the
* matchpoint is txnA, the log will be truncated at txnB, and we
* should reset earliestPassedTxn = txnB.
*
* Note that numPassedDurableCommits is a subset of numPassedCommits
*/
private PassedTxnInfo earliestPassedTxn;
private PassedTxnInfo penultimatePassedTxn;
private int numPassedCommits;
private int numPassedDurableCommits;
/*
* The DTVLSN established when scanning the log backwards from high to low
* VLSNS
*/
private VLSN dtvlsn = VLSN.NULL_VLSN;
public MatchpointSearchResults(EnvironmentImpl envImpl) {
/*
* The matchpointLSN should be a non-null value, because we always have
* to provide a valid truncation point.
*/
matchpointLSN = DbLsn.makeLsn(0, 0);
passedTxnLimit =
envImpl.getConfigManager().getInt(RepParams.TXN_ROLLBACK_LIMIT);
passedTxns = new ArrayList<PassedTxnInfo>();
numPassedCommits = 0;
numPassedDurableCommits = 0;
}
/**
* If we see a checkpoint end record, see if it is a barrier to
* rolling back, and advance the file reader position.
*/
void notePassedCheckpointEnd() {
passedCheckpointEnd = true;
}
/**
* Keep track if we have jumped over a gap in the log files, and might have
* missed a checkpoint end.
*/
void noteSkippedGap() {
passedSkippedGap = true;
}
/**
* At the end of the search for a matchpoint, set the matchpointLSN and
* adjust the debugging list of passed transactions. The matchpoint entry
* is just before the truncation point, and does not get truncated.
*/
void setMatchpoint(long match) {
matchpointLSN = match;
if ((earliestPassedTxn != null) &&
(earliestPassedTxn.lsn == matchpointLSN)) {
numPassedCommits--;
if (passedTxns.size() > 0) {
int lastSavedTxn = passedTxns.size() - 1;
if (passedTxns.get(lastSavedTxn).lsn == match) {
passedTxns.remove(lastSavedTxn);
}
earliestPassedTxn = penultimatePassedTxn;
}
}
}
/**
* The reader saw a transaction commit; record relevant information. Note
* that we record all transactions (replicated and non-replicated) to avoid
* cutting off a checkpoint. Note that the stream can be a combination of
* log records from pre and post-dtvlsn masters. Note that we are scanning
* the log backwards, so we are encountering commits in decreasing VLSN
* sequence.
*
* @param commit the commit being passed. Note that this object is reused
* and so should not be saved.
*
* @param commitVLSN the VLSN associated with the commit. It could be null
* if the commit was local and not HA
*
* @param commitLSN the LSN at which the commit is located
*/
void notePassedCommits(TxnCommit commit,
VLSN commitVLSN,
long commitLSN) {
boolean durableCommit = false;
final Timestamp commitTime = commit.getTime();
final long txnId = commit.getId();
final long commitDTVLSN = commit.getDTVLSN();
if ((commitVLSN != null) && !commitVLSN.isNull()) {
/* A replicated txn */
processDTVLSN(commitVLSN, commitDTVLSN);
if (commit.hasLoggedEntries() &&
((dtvlsn.getSequence() == VLSN.UNINITIALIZED_VLSN_SEQUENCE) ||
(commitVLSN.compareTo(dtvlsn) <= 0))) {
numPassedDurableCommits++;
durableCommit = true;
}
}
numPassedCommits++;
if (earliestPassedTxn != null) {
penultimatePassedTxn = earliestPassedTxn;
}
earliestPassedTxn =
new PassedTxnInfo(commitTime, txnId, commitVLSN, commitLSN,
durableCommit);
/*
* Save only a limited number of passed txns, for displaying to the log
*/
if (numPassedCommits <= passedTxnLimit) {
passedTxns.add(earliestPassedTxn);
}
}
private void processDTVLSN(VLSN txnEndVLSN, final long txnEndDTVLSN) {
if (dtvlsn.isNull()) {
/*
* The first commit/abort record, set the dtvlsn to a
* non null value
*/
dtvlsn = new VLSN(txnEndDTVLSN);
} else {
/*
* Already set, verify sequencing. Make allowance for transitions
* from pre to post-DTVLSN stream segments. Note that we are going
* backwards in the log at this point.
*/
if ((txnEndDTVLSN > dtvlsn.getSequence()) &&
(dtvlsn.getSequence() != VLSN.UNINITIALIZED_VLSN_SEQUENCE)) {
throw new IllegalStateException
("DTVLSNs should only decrease with decreasing VLSNs." +
" prev:" + dtvlsn + " next:" + txnEndDTVLSN +
" commit VLSN:" + txnEndVLSN);
}
}
}
/**
* Use the DTVLSN value from a replicated abort to set the dtvlsn.
*
* @param abort the abort log record
*
* @param abortVLSN the VLSN associated with the abort log record
*/
void notePassedAborts(TxnAbort abort,
VLSN abortVLSN) {
if (abortVLSN.isNull()) {
/* A non-replicated abort. */
return;
}
final long abortDTVLSN = abort.getDTVLSN();
processDTVLSN(abortVLSN, abortDTVLSN);
}
boolean getPassedCheckpointEnd() {
return passedCheckpointEnd;
}
boolean getSkippedGap() {
return passedSkippedGap;
}
public long getMatchpointLSN() {
return matchpointLSN;
}
public int getNumPassedCommits() {
return numPassedCommits;
}
public int getNumPassedDurableCommits() {
return numPassedDurableCommits;
}
public PassedTxnInfo getEarliestPassedTxn() {
return earliestPassedTxn;
}
public VLSN getDTVLSN() {
return dtvlsn;
}
/**
* Display the saved transaction information.
*/
public String dumpPassedTxns() {
StringBuilder sb = new StringBuilder();
for (PassedTxnInfo info : passedTxns) {
sb.append(info);
sb.append("\n");
}
return sb.toString();
}
@Override
public String toString() {
return "matchpointLSN=" + DbLsn.getNoFormatString(matchpointLSN) +
" passedCkpt=" + passedCheckpointEnd +
" passedTxnLimit=" + passedTxnLimit +
" passedTxns=" + passedTxns +
" earliestTxn=" + earliestPassedTxn +
" penultimateTxn=" + penultimatePassedTxn +
" numPassedCommits=" + numPassedCommits +
" numPassedDurableCommits=" + numPassedDurableCommits +
" passedSkippedGap=" + passedSkippedGap;
}
/**
* If 1 or more commits was passed, construct a message that can
* be used by RollbackException and RollbackProhibitedException.
*/
public String getRollbackMsg() {
if (numPassedCommits == 0) {
return " uncommitted operations";
}
if (numPassedDurableCommits == 0) {
return " " + numPassedCommits +
" total commits to the earliest point indicated by transaction " +
earliestPassedTxn;
}
return " " + numPassedCommits + " total commits(" +
numPassedDurableCommits + " of which were durable) " +
"to the earliest point indicated by transaction " +
earliestPassedTxn;
}
/* Struct to hold information about passed txns. */
public static class PassedTxnInfo implements Serializable {
private static final long serialVersionUID = 1L;
public final Timestamp time;
public final long id;
public final VLSN vlsn;
public final long lsn;
public final boolean durableCommit;
PassedTxnInfo(Timestamp time, long id, VLSN vlsn, long lsn,
boolean durableCommit) {
this.time = time;
this.id = id;
this.vlsn = vlsn;
this.lsn = lsn;
this.durableCommit = durableCommit;
}
@Override
public String toString() {
return "id=" + id +
" time=" + time +
" vlsn=" + vlsn +
" lsn=" + DbLsn.getNoFormatString(lsn) +
" durable=" + durableCommit;
}
}
}