mirror of https://github.com/apache/cassandra
Scrub (recover) sstables even when -Index.db is missing
patch by mck; reviewed by stefania for CASSANDRA-9591
This commit is contained in:
parent
62714a9fed
commit
452d6a445a
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue