[TEST] Fix CorruptedFileTest to always corrupt the latest delete generation if a .del file is picked
This commit is contained in:
parent
35a52cd04a
commit
a84777e990
|
@ -23,6 +23,7 @@ import com.google.common.base.Charsets;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import org.apache.lucene.codecs.CodecUtil;
|
import org.apache.lucene.codecs.CodecUtil;
|
||||||
import org.apache.lucene.index.CheckIndex;
|
import org.apache.lucene.index.CheckIndex;
|
||||||
|
import org.apache.lucene.index.IndexFileNames;
|
||||||
import org.apache.lucene.index.MergePolicy;
|
import org.apache.lucene.index.MergePolicy;
|
||||||
import org.apache.lucene.index.NoMergePolicy;
|
import org.apache.lucene.index.NoMergePolicy;
|
||||||
import org.apache.lucene.store.*;
|
import org.apache.lucene.store.*;
|
||||||
|
@ -68,10 +69,7 @@ import org.elasticsearch.transport.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -388,62 +386,91 @@ public class CorruptedFileTest extends ElasticsearchIntegrationTest {
|
||||||
assertTrue(shardRouting.assignedToNode());
|
assertTrue(shardRouting.assignedToNode());
|
||||||
String nodeId = shardRouting.currentNodeId();
|
String nodeId = shardRouting.currentNodeId();
|
||||||
NodesStatsResponse nodeStatses = client().admin().cluster().prepareNodesStats(nodeId).setFs(true).get();
|
NodesStatsResponse nodeStatses = client().admin().cluster().prepareNodesStats(nodeId).setFs(true).get();
|
||||||
File fileToCorrupt = null;
|
Set<File> files = new TreeSet<>(); // treeset makes sure iteration order is deterministic
|
||||||
for (FsStats.Info info : nodeStatses.getNodes()[0].getFs()) {
|
for (FsStats.Info info : nodeStatses.getNodes()[0].getFs()) {
|
||||||
String path = info.getPath();
|
String path = info.getPath();
|
||||||
final String relativeDataLocationPath = "indices/test/" + Integer.toString(shardRouting.getId()) + "/index";
|
final String relativeDataLocationPath = "indices/test/" + Integer.toString(shardRouting.getId()) + "/index";
|
||||||
File file = new File(path, relativeDataLocationPath);
|
File file = new File(path, relativeDataLocationPath);
|
||||||
final File[] files = file.listFiles(new FileFilter() {
|
files.addAll(Arrays.asList(file.listFiles(new FileFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File pathname) {
|
public boolean accept(File pathname) {
|
||||||
return pathname.isFile() && !"write.lock".equals(pathname.getName())
|
return pathname.isFile() && !"write.lock".equals(pathname.getName());
|
||||||
&& !pathname.getName().endsWith(".del"); // temporary fix - del files might be generational and we corrupt an old generation
|
|
||||||
// TODO(simonw): fix this method to select the oldest del gen if we pick a del file
|
|
||||||
}
|
}
|
||||||
});
|
})));
|
||||||
if (files.length > 1) {
|
}
|
||||||
fileToCorrupt = RandomPicks.randomFrom(getRandom(), files);
|
pruneOldDeleteGenerations(files);
|
||||||
try (Directory dir = FSDirectory.open(file)) {
|
File fileToCorrupt = null;
|
||||||
long checksumBeforeCorruption;
|
if (!files.isEmpty()) {
|
||||||
try (IndexInput input = dir.openInput(fileToCorrupt.getName(), IOContext.DEFAULT)) {
|
fileToCorrupt = RandomPicks.randomFrom(getRandom(), files);
|
||||||
checksumBeforeCorruption = CodecUtil.retrieveChecksum(input);
|
try (Directory dir = FSDirectory.open(fileToCorrupt.getParentFile())) {
|
||||||
}
|
long checksumBeforeCorruption;
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(fileToCorrupt, "rw")) {
|
try (IndexInput input = dir.openInput(fileToCorrupt.getName(), IOContext.DEFAULT)) {
|
||||||
raf.seek(randomIntBetween(0, (int)Math.min(Integer.MAX_VALUE, raf.length()-1)));
|
checksumBeforeCorruption = CodecUtil.retrieveChecksum(input);
|
||||||
long filePointer = raf.getFilePointer();
|
|
||||||
byte b = raf.readByte();
|
|
||||||
raf.seek(filePointer);
|
|
||||||
raf.writeByte(~b);
|
|
||||||
raf.getFD().sync();
|
|
||||||
logger.info("Corrupting file for shard {} -- flipping at position {} from {} to {} file: {}", shardRouting, filePointer, Integer.toHexString(b), Integer.toHexString(~b), fileToCorrupt.getName());
|
|
||||||
}
|
|
||||||
long checksumAfterCorruption;
|
|
||||||
long actualChecksumAfterCorruption;
|
|
||||||
try (ChecksumIndexInput input = dir.openChecksumInput(fileToCorrupt.getName(), IOContext.DEFAULT)) {
|
|
||||||
assertThat(input.getFilePointer(), is(0l));
|
|
||||||
input.seek(input.length() - 8); // one long is the checksum... 8 bytes
|
|
||||||
checksumAfterCorruption = input.getChecksum();
|
|
||||||
actualChecksumAfterCorruption = input.readLong();
|
|
||||||
}
|
|
||||||
// we need to add assumptions here that the checksums actually really don't match there is a small chance to get collisions
|
|
||||||
// in the checksum which is ok though....
|
|
||||||
StringBuilder msg = new StringBuilder();
|
|
||||||
msg.append("Checksum before: [").append(checksumBeforeCorruption).append("]");
|
|
||||||
msg.append(" after: [").append(checksumAfterCorruption).append("]");
|
|
||||||
msg.append(" checksum value after corruption: ").append(actualChecksumAfterCorruption).append("]");
|
|
||||||
msg.append(" file: ").append(fileToCorrupt.getName()).append(" length: ").append(dir.fileLength(fileToCorrupt.getName()));
|
|
||||||
logger.info(msg.toString());
|
|
||||||
assumeTrue("Checksum collision - " + msg.toString(),
|
|
||||||
checksumAfterCorruption != checksumBeforeCorruption // collision
|
|
||||||
|| actualChecksumAfterCorruption != checksumBeforeCorruption); // checksum corrupted
|
|
||||||
}
|
}
|
||||||
break;
|
try (RandomAccessFile raf = new RandomAccessFile(fileToCorrupt, "rw")) {
|
||||||
|
raf.seek(randomIntBetween(0, (int)Math.min(Integer.MAX_VALUE, raf.length()-1)));
|
||||||
|
long filePointer = raf.getFilePointer();
|
||||||
|
byte b = raf.readByte();
|
||||||
|
raf.seek(filePointer);
|
||||||
|
raf.writeByte(~b);
|
||||||
|
raf.getFD().sync();
|
||||||
|
logger.info("Corrupting file for shard {} -- flipping at position {} from {} to {} file: {}", shardRouting, filePointer, Integer.toHexString(b), Integer.toHexString(~b), fileToCorrupt.getName());
|
||||||
|
}
|
||||||
|
long checksumAfterCorruption;
|
||||||
|
long actualChecksumAfterCorruption;
|
||||||
|
try (ChecksumIndexInput input = dir.openChecksumInput(fileToCorrupt.getName(), IOContext.DEFAULT)) {
|
||||||
|
assertThat(input.getFilePointer(), is(0l));
|
||||||
|
input.seek(input.length() - 8); // one long is the checksum... 8 bytes
|
||||||
|
checksumAfterCorruption = input.getChecksum();
|
||||||
|
actualChecksumAfterCorruption = input.readLong();
|
||||||
|
}
|
||||||
|
// we need to add assumptions here that the checksums actually really don't match there is a small chance to get collisions
|
||||||
|
// in the checksum which is ok though....
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.append("Checksum before: [").append(checksumBeforeCorruption).append("]");
|
||||||
|
msg.append(" after: [").append(checksumAfterCorruption).append("]");
|
||||||
|
msg.append(" checksum value after corruption: ").append(actualChecksumAfterCorruption).append("]");
|
||||||
|
msg.append(" file: ").append(fileToCorrupt.getName()).append(" length: ").append(dir.fileLength(fileToCorrupt.getName()));
|
||||||
|
logger.info(msg.toString());
|
||||||
|
assumeTrue("Checksum collision - " + msg.toString(),
|
||||||
|
checksumAfterCorruption != checksumBeforeCorruption // collision
|
||||||
|
|| actualChecksumAfterCorruption != checksumBeforeCorruption); // checksum corrupted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThat("no file corrupted", fileToCorrupt, notNullValue());
|
assertThat("no file corrupted", fileToCorrupt, notNullValue());
|
||||||
return shardRouting;
|
return shardRouting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prunes the list of index files such that only the latest del generation files are contained.
|
||||||
|
*/
|
||||||
|
private void pruneOldDeleteGenerations(Set<File> files) {
|
||||||
|
final TreeSet<File> delFiles = new TreeSet<>();
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.getName().endsWith(".del")) {
|
||||||
|
delFiles.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File last = null;
|
||||||
|
for (File current : delFiles) {
|
||||||
|
if (last != null) {
|
||||||
|
final String newSegmentName = IndexFileNames.parseSegmentName(current.getName());
|
||||||
|
final String oldSegmentName = IndexFileNames.parseSegmentName(last.getName());
|
||||||
|
if (newSegmentName.equals(oldSegmentName)) {
|
||||||
|
int oldGen = Integer.parseInt(IndexFileNames.stripExtension(IndexFileNames.stripSegmentName(last.getName())).replace("_", ""), Character.MAX_RADIX);
|
||||||
|
int newGen = Integer.parseInt(IndexFileNames.stripExtension(IndexFileNames.stripSegmentName(current.getName())).replace("_", ""), Character.MAX_RADIX);
|
||||||
|
if (newGen > oldGen) {
|
||||||
|
files.remove(last);
|
||||||
|
} else {
|
||||||
|
files.remove(current);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<File> listShardFiles(ShardRouting routing) {
|
public List<File> listShardFiles(ShardRouting routing) {
|
||||||
NodesStatsResponse nodeStatses = client().admin().cluster().prepareNodesStats(routing.currentNodeId()).setFs(true).get();
|
NodesStatsResponse nodeStatses = client().admin().cluster().prepareNodesStats(routing.currentNodeId()).setFs(true).get();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue