Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions persistit/core/src/main/java/com/persistit/IntegrityCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class IntegrityCheck extends Task {

private Volume _currentVolume;
private Tree _currentTree;
private long _currentPage;
private LongBitSet _usedPageBits = new LongBitSet();
private long _totalPages = 0;
private long _pagesVisited = 0;
Expand All @@ -79,6 +80,7 @@ public class IntegrityCheck extends Task {
private boolean _csv;

private final ArrayList<Fault> _faults = new ArrayList<Fault>();
private int _markedVersionFaultCount;
private final ArrayList<CleanupIndexHole> _holes = new ArrayList<CleanupIndexHole>();

// Used in checking long values
Expand All @@ -98,6 +100,7 @@ private static class Counters {
private long _mvvCount = 0;
private long _mvvOverhead = 0;
private long _mvvAntiValues = 0;
private long _markedVersionCount = 0;
private long _pruningErrorCount = 0;
private long _prunedPageCount = 0;
private long _garbagePageCount = 0;
Expand All @@ -118,6 +121,7 @@ private static class Counters {
_mvvCount = counters._mvvCount;
_mvvOverhead = counters._mvvOverhead;
_mvvAntiValues = counters._mvvAntiValues;
_markedVersionCount = counters._markedVersionCount;
_pruningErrorCount = counters._pruningErrorCount;
_prunedPageCount = counters._prunedPageCount;
_garbagePageCount = counters._garbagePageCount;
Expand All @@ -135,6 +139,7 @@ void difference(final Counters counters) {
_mvvCount = counters._mvvCount - _mvvCount;
_mvvOverhead = counters._mvvOverhead - _mvvOverhead;
_mvvAntiValues = counters._mvvAntiValues - _mvvAntiValues;
_markedVersionCount = counters._markedVersionCount - _markedVersionCount;
_pruningErrorCount = counters._pruningErrorCount - _pruningErrorCount;
_prunedPageCount = counters._prunedPageCount - _prunedPageCount;
_garbagePageCount = counters._garbagePageCount - _garbagePageCount;
Expand All @@ -144,20 +149,21 @@ void difference(final Counters counters) {
public String toString() {
return String.format("Index pages/bytes: %,d / %,d Data pages/bytes: %,d / %,d"
+ " LongRec pages/bytes: %,d / %,d MVV pages/records/bytes/antivalues: "
+ "%,d / %,d / %,d / %,d Holes %,d Pages pruned %,d", _indexPageCount, _indexBytesInUse,
_dataPageCount, _dataBytesInUse, _longRecordPageCount, _longRecordBytesInUse, _mvvPageCount,
_mvvCount, _mvvOverhead, _mvvAntiValues, _indexHoleCount, _prunedPageCount);
+ "%,d / %,d / %,d / %,d Holes %,d Pages pruned %,d Marked versions %,d", _indexPageCount,
_indexBytesInUse, _dataPageCount, _dataBytesInUse, _longRecordPageCount, _longRecordBytesInUse,
_mvvPageCount, _mvvCount, _mvvOverhead, _mvvAntiValues, _indexHoleCount, _prunedPageCount,
_markedVersionCount);
}

private String toCSV() {
return String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", _indexPageCount, _indexBytesInUse,
return String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", _indexPageCount, _indexBytesInUse,
_dataPageCount, _dataBytesInUse, _longRecordPageCount, _longRecordBytesInUse, _mvvPageCount,
_mvvCount, _mvvOverhead, _mvvAntiValues, _indexHoleCount, _prunedPageCount);
_mvvCount, _mvvOverhead, _mvvAntiValues, _indexHoleCount, _prunedPageCount, _markedVersionCount);
}

private final static String CSV_HEADERS = "IndexPages,IndexBytes,"
+ "DataPages,DataBytes,LongRecordPages,LongRecordBytes,MvvPages,"
+ "MvvRecords,MvvOverhead,MvvAntiValues,IndexHoles,PrunedPages";
+ "MvvRecords,MvvOverhead,MvvAntiValues,IndexHoles,PrunedPages,MarkedVersions";

}

Expand Down Expand Up @@ -186,6 +192,20 @@ public void sawVersion(final long version, final int offset, final int valueLeng
protected void visitDataRecord(final Key key, final int foundAt, final int tail, final int klength,
final int offset, final int length, final byte[] bytes) throws PersistitException {
MVV.visitAllVersions(_versionVisitor, bytes, offset, length);
/*
* Scanned only after visitAllVersions has validated the version
* structure. Leftover prune marks read normally (the length
* accessors strip the mark bit), so only this dedicated scan can
* detect and report them - see issue #290.
*/
final int marked = MVV.countMarkedVersions(bytes, offset, length);
if (marked > 0) {
_counters._markedVersionCount += marked;
if (addFault("MVV has " + plural(marked, "version") + " with a leftover mark bit", _currentPage, 0,
foundAt)) {
_markedVersionFaultCount++;
}
}
if (_versionVisitor._count > 0) {
_counters._mvvCount++;
final int voffset = _versionVisitor._lastOffset;
Expand Down Expand Up @@ -289,7 +309,14 @@ protected void runTask() {
postMessage("Total " + toString(), LOG_NORMAL);
}
if (_pruneAndClear) {
if (_faults.isEmpty() && _counters._mvvPageCount == _counters._prunedPageCount
/*
* Leftover mark-bit faults (issue #290) identify volumes
* affected by a pre-#286 interrupted prune but do not block
* clearing the TransactionIndex: the prune pass above has
* already rewritten those marks (issue #292). Only other
* faults and incomplete pruning count as failures here.
*/
if (_faults.size() == _markedVersionFaultCount && _counters._mvvPageCount == _counters._prunedPageCount
&& _counters._pruningErrorCount == 0) {
final int count = _persistit.getTransactionIndex().resetMVVCounts(startTimestamp);
postMessage(String.format("%,d aborted transactions were cleared by pruning", count), LOG_NORMAL);
Expand Down Expand Up @@ -329,11 +356,13 @@ private String plural(final int n, final String m) {
}
}

private void addFault(final String description, final long page, final int level, final int position) {
private boolean addFault(final String description, final long page, final int level, final int position) {
final Fault fault = new Fault(resourceName(), this, description, page, _treeDepth, level, position);
if (_faults.size() < MAX_FAULTS)
final boolean recorded = _faults.size() < MAX_FAULTS;
if (recorded)
_faults.add(fault);
postMessage(fault.toString(), LOG_VERBOSE);
return recorded;
}

private void addGarbageFault(final String description, final long page, final int level, final int position) {
Expand Down Expand Up @@ -544,6 +573,16 @@ public long getMvvAntiValues() {
return _counters._mvvAntiValues;
}

/**
* @return Count of MVV versions whose length field still carries a
* leftover prune mark bit, left behind by a prune interrupted
* before the issue #286 fix. Such versions read normally but
* indicate the volume was corrupted; pruning rewrites them.
*/
public long getMarkedVersionCount() {
return _counters._markedVersionCount;
}

/**
* @return Count of pages for which an expected index pointer is missing
*/
Expand Down Expand Up @@ -1144,6 +1183,7 @@ private Buffer walkRight(final int level, final long toPage, final Key key, fina
}

private boolean verifyPage(final Buffer buffer, final long page, final int level, final Key key, final Tree tree) {
_currentPage = page;
if (buffer.getPageAddress() != page) {
addFault("Buffer contains wrong page " + buffer.getPageAddress(), page, level, 0);
return false;
Expand Down
51 changes: 50 additions & 1 deletion persistit/core/src/main/java/com/persistit/MVV.java
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,11 @@ else if (target[targetOffset] != TYPE_MVV_BYTE) {
* </p>
* <p>
* This method leaves the byte array unchanged if any of its checked
* Exceptions is thrown.
* Exceptions is thrown, with one exception: stale mark bits left on disk
* by a prune interrupted before the issue #286 fix are cleared up front
* regardless of outcome (issue #292). Mark bits are transient state
* private to this method and invisible to the read paths, so clearing
* them repairs the corruption without altering any version's value.
* </p>
* <p>
* This method adds {@link PrunedVersion} instances to the supplied list.
Expand Down Expand Up @@ -437,6 +441,20 @@ static int prune(final byte[] bytes, final int offset, final int length, final T
long lastVersionHandle = Long.MIN_VALUE;
long lastVersionTc = UNCOMMITTED;
long uncommittedTransactionTs = 0;
/*
* The passes below trust isMarked() while the marked counter only
* counts marks set by this run, so a stale mark left on disk by a
* prune interrupted before the issue #286 fix would win the
* primordial-conversion scan — promoting an obsolete version and
* silently dropping the current one with no PrunedVersion
* accounting (issue #292). Sweep the region clean before the
* first pass marks the real keepers.
*/
while (from + LENGTH_PER_VERSION <= offset + length) {
unmark(bytes, from);
from += getLength(bytes, from) + LENGTH_PER_VERSION;
}
from = offset + 1;
/*
* First pass - mark all the versions to keep. Keep every
* UNCOMMITTED version (there may be more than one created by the
Expand Down Expand Up @@ -625,6 +643,37 @@ static boolean verify(final byte[] bytes, final int offset, final int length) {
return true;
}

/**
* Count versions whose length field still carries the mark bit. Marks are
* transient state private to {@link #prune} and must never be observable
* outside it; a non-zero count on a stored MVV means the value was
* corrupted by a prune interrupted before the issue #286 fix. The length
* accessors strip the mark bit silently, so such versions read normally
* and only this scan can detect them (issue #290).
*
* @param bytes
* the byte array
* @param offset
* the index of the first byte of the MVV within the byte array
* @param length
* the count of bytes in the MVV
* @return the number of marked versions, 0 for a non-MVV value
*/
static int countMarkedVersions(final byte[] bytes, final int offset, final int length) {
if (!isArrayMVV(bytes, offset, length)) {
return 0;
}
int marked = 0;
int from = offset + 1;
while (from + LENGTH_PER_VERSION <= offset + length) {
if (isMarked(bytes, from)) {
marked++;
}
from += getLength(bytes, from) + LENGTH_PER_VERSION;
}
return marked;
}

static boolean verify(final TransactionIndex ti, final byte[] bytes, final int offset, final int length) {
if (!isArrayMVV(bytes, offset, length)) {
/*
Expand Down
Loading
Loading