Scrub (recover) sstables even when -Index.db is missing

patch by mck; reviewed by stefania for CASSANDRA-9591
This commit is contained in:
Stefania Alborghetti 2015-06-15 16:49:03 +08:00 committed by Benedict Elliott Smith
parent 62714a9fed
commit 452d6a445a
5 changed files with 96 additions and 18 deletions

View File

@ -1,3 +1,7 @@
2.0.18
* Scrub (recover) sstables even when -Index.db is missing, (CASSANDRA-9591)
2.0.17
* Avoid NPE in AuthSuccess#decode (CASSANDRA-9727)
* Add listen_address to system.local (CASSANDRA-9603)

View File

@ -96,7 +96,16 @@ public class Scrubber implements Closeable
? new ScrubController(cfs)
: new CompactionController(cfs, Collections.singleton(sstable), CompactionManager.getDefaultGcBefore(cfs));
this.isCommutative = cfs.metadata.getDefaultValidator().isCommutative();
this.expectedBloomFilterSize = Math.max(cfs.metadata.getIndexInterval(), (int)(SSTableReader.getApproximateKeyCount(toScrub,cfs.metadata)));
boolean hasIndexFile = (new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX))).exists();
if (!hasIndexFile)
{
// if there's any corruption in the -Data.db then rows can't be skipped over. but it's worth a shot.
outputHandler.warn("Missing component: " + sstable.descriptor.filenameFor(Component.PRIMARY_INDEX));
}
this.expectedBloomFilterSize = Math.max(cfs.metadata.getIndexInterval(),
hasIndexFile ? (int)(SSTableReader.getApproximateKeyCount(toScrub,cfs.metadata)) : 0);
// loop through each row, deserializing to check for damage.
// we'll also loop through the index at the same time, using the position from the index to recover if the
@ -105,7 +114,11 @@ public class Scrubber implements Closeable
this.dataFile = isOffline
? sstable.openDataReader()
: sstable.openDataReader(CompactionManager.instance.getRateLimiter());
this.indexFile = RandomAccessReader.open(new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX)));
this.indexFile = hasIndexFile
? RandomAccessReader.open(new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX)))
: null;
this.scrubInfo = new ScrubInfo(dataFile, sstable);
this.currentRowPositionFromIndex = 0;
@ -117,7 +130,8 @@ public class Scrubber implements Closeable
outputHandler.output(String.format("Scrubbing %s (%s bytes)", sstable, dataFile.length()));
try
{
nextIndexKey = ByteBufferUtil.readWithShortLength(indexFile);
nextIndexKey = indexAvailable() ? ByteBufferUtil.readWithShortLength(indexFile) : null;
if (indexAvailable())
{
// throw away variable so we don't have a side effect in the assert
long firstRowPositionFromIndex = RowIndexEntry.serializer.deserialize(indexFile, sstable.descriptor.version).position;
@ -181,7 +195,7 @@ public class Scrubber implements Closeable
outputHandler.debug(String.format("Index doublecheck: row %s is %s bytes", ByteBufferUtil.bytesToHex(currentIndexKey), dataSizeFromIndex));
}
assert currentIndexKey != null || indexFile.isEOF();
assert currentIndexKey != null || !indexAvailable();
writer.mark();
try
@ -198,10 +212,10 @@ public class Scrubber implements Closeable
if (dataSize > dataFile.length())
throw new IOError(new IOException("Impossible row size (greater than file length): " + dataSize));
if (dataStart != dataStartFromIndex)
if (indexFile != null && dataStart != dataStartFromIndex)
outputHandler.warn(String.format("Data file row position %d differs from index file row position %d", dataStart, dataStartFromIndex));
if (dataSize != dataSizeFromIndex)
if (indexFile != null && dataSize != dataSizeFromIndex)
outputHandler.warn(String.format("Data file row size %d differs from index file row size %d", dataSize, dataSizeFromIndex));
SSTableIdentityIterator atoms = new SSTableIdentityIterator(sstable, dataFile, key, dataSize, validateColumns);
@ -317,8 +331,9 @@ public class Scrubber implements Closeable
currentRowPositionFromIndex = nextRowPositionFromIndex;
try
{
nextIndexKey = indexFile.isEOF() ? null : ByteBufferUtil.readWithShortLength(indexFile);
nextRowPositionFromIndex = indexFile.isEOF()
nextIndexKey = !indexAvailable() ? null : ByteBufferUtil.readWithShortLength(indexFile);
nextRowPositionFromIndex = !indexAvailable()
? dataFile.length()
: RowIndexEntry.serializer.deserialize(indexFile, sstable.descriptor.version).position;
}
@ -330,6 +345,11 @@ public class Scrubber implements Closeable
}
}
private boolean indexAvailable()
{
return indexFile != null && !indexFile.isEOF();
}
private void seekToNextRow()
{
while(nextRowPositionFromIndex < dataFile.length())

View File

@ -157,7 +157,7 @@ public class SSTableReader extends SSTable implements Closeable
public static SSTableReader openForBatch(Descriptor descriptor, Set<Component> components, CFMetaData metadata, IPartitioner partitioner) throws IOException
{
SSTableMetadata sstableMetadata = openMetadata(descriptor, components, partitioner);
SSTableMetadata sstableMetadata = openMetadata(descriptor, components, partitioner, true);
SSTableReader sstable = new SSTableReader(descriptor,
components,
metadata,
@ -191,7 +191,7 @@ public class SSTableReader extends SSTable implements Closeable
boolean validate) throws IOException
{
long start = System.nanoTime();
SSTableMetadata sstableMetadata = openMetadata(descriptor, components, partitioner);
SSTableMetadata sstableMetadata = openMetadata(descriptor, components, partitioner, validate);
SSTableReader sstable = new SSTableReader(descriptor,
components,
@ -213,12 +213,17 @@ public class SSTableReader extends SSTable implements Closeable
return sstable;
}
private static SSTableMetadata openMetadata(Descriptor descriptor, Set<Component> components, IPartitioner partitioner) throws IOException
private static SSTableMetadata openMetadata(Descriptor descriptor,
Set<Component> components,
IPartitioner partitioner,
boolean primaryIndexRequired) throws IOException
{
assert partitioner != null;
// Minimum components without which we can't do anything
assert components.contains(Component.DATA) : "Data component is missing for sstable" + descriptor;
assert components.contains(Component.PRIMARY_INDEX) : "Primary index component is missing for sstable " + descriptor;
assert !primaryIndexRequired || components.contains(Component.PRIMARY_INDEX)
: "Primary index component is missing for sstable " + descriptor;
logger.info("Opening {} ({} bytes)", descriptor, new File(descriptor.filenameFor(COMPONENT_DATA)).length());
@ -382,11 +387,17 @@ public class SSTableReader extends SSTable implements Closeable
readMeterSyncFuture.cancel(false);
// Force finalizing mmapping if necessary
ifile.cleanup();
if (null != ifile)
ifile.cleanup();
dfile.cleanup();
// close the BF so it can be opened later.
bf.close();
indexSummary.close();
if (null != bf)
bf.close();
if (null != indexSummary)
indexSummary.close();
}
public void setTrackedBy(DataTracker tracker)
@ -406,6 +417,12 @@ public class SSTableReader extends SSTable implements Closeable
load(false, true);
bf = FilterFactory.AlwaysPresent;
}
else if (!components.contains(Component.PRIMARY_INDEX))
{
// avoid any reading of the missing primary index component.
// this should only happen during StandaloneScrubber
load(false, false);
}
else if (!components.contains(Component.FILTER))
{
// bf is enabled, but filter component is missing.
@ -454,7 +471,9 @@ public class SSTableReader extends SSTable implements Closeable
if (recreateBloomFilter || !summaryLoaded)
buildSummary(recreateBloomFilter, ibuilder, dbuilder, summaryLoaded);
ifile = ibuilder.complete(descriptor.filenameFor(Component.PRIMARY_INDEX));
if (components.contains(Component.PRIMARY_INDEX))
ifile = ibuilder.complete(descriptor.filenameFor(Component.PRIMARY_INDEX));
dfile = dbuilder.complete(descriptor.filenameFor(Component.DATA));
if (saveSummaryIfCreated && (recreateBloomFilter || !summaryLoaded)) // save summary information to disk
saveSummary(this, ibuilder, dbuilder);
@ -462,6 +481,9 @@ public class SSTableReader extends SSTable implements Closeable
private void buildSummary(boolean recreateBloomFilter, SegmentedFile.Builder ibuilder, SegmentedFile.Builder dbuilder, boolean summaryLoaded) throws IOException
{
if (!components.contains(Component.PRIMARY_INDEX))
return;
// we read the positions in a BRAF so we don't have to worry about an entry spanning a mmap boundary.
RandomAccessReader primaryIndex = RandomAccessReader.open(new File(descriptor.filenameFor(Component.PRIMARY_INDEX)));
@ -965,6 +987,9 @@ public class SSTableReader extends SSTable implements Closeable
}
}
if (ifile == null)
return null;
// scan the on-disk index, starting at the nearest sampled position.
// The check against IndexInterval is to be exit the loop in the EQ case when the key looked for is not present
// (bloom filter false positive). But note that for non-EQ cases, we might need to check the first key of the
@ -1063,6 +1088,9 @@ public class SSTableReader extends SSTable implements Closeable
if (sampledPosition == -1)
sampledPosition = 0;
if (ifile == null)
return null;
Iterator<FileDataInput> segments = ifile.iterator(sampledPosition);
while (segments.hasNext())
{
@ -1480,7 +1508,8 @@ public class SSTableReader extends SSTable implements Closeable
private void dropPageCache()
{
dropPageCache(dfile.path);
dropPageCache(ifile.path);
if (null != ifile)
dropPageCache(ifile.path);
}
private void dropPageCache(String filePath)

View File

@ -79,7 +79,7 @@ public class StandaloneScrubber
for (Map.Entry<Descriptor, Set<Component>> entry : lister.list().entrySet())
{
Set<Component> components = entry.getValue();
if (!components.contains(Component.DATA) || !components.contains(Component.PRIMARY_INDEX))
if (!components.contains(Component.DATA))
continue;
try

View File

@ -269,6 +269,31 @@ public class ScrubTest extends SchemaLoader
assertEquals(10, rows.size());
}
@Test
public void testScrubNoIndex() throws IOException, ExecutionException, InterruptedException, ConfigurationException
{
CompactionManager.instance.disableAutoCompaction();
Keyspace keyspace = Keyspace.open(KEYSPACE);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF);
cfs.clearUnsafe();
List<Row> rows;
// insert data and verify we get it back w/ range query
fillCF(cfs, 10);
rows = cfs.getRangeSlice(Util.range("", ""), null, new IdentityQueryFilter(), 1000);
assertEquals(10, rows.size());
for (SSTableReader sstable : cfs.getSSTables())
new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX)).delete();
CompactionManager.instance.performScrub(cfs, false, true);
// check data is still there
rows = cfs.getRangeSlice(Util.range("", ""), null, new IdentityQueryFilter(), 1000);
assertEquals(10, rows.size());
}
@Test
public void testScrubOutOfOrder() throws Exception
{