|
|
@@ -22,6 +22,7 @@ package org.elasticsearch.index.translog;
|
|
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
|
|
import org.apache.lucene.codecs.CodecUtil;
|
|
|
import org.apache.lucene.index.Term;
|
|
|
+import org.apache.lucene.mockfile.FilterFileChannel;
|
|
|
import org.apache.lucene.store.AlreadyClosedException;
|
|
|
import org.apache.lucene.store.ByteArrayDataOutput;
|
|
|
import org.apache.lucene.util.IOUtils;
|
|
|
@@ -110,13 +111,16 @@ public class TranslogTests extends ESTestCase {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- protected Translog create(Path path) throws IOException {
|
|
|
+ private Translog create(Path path) throws IOException {
|
|
|
+ return new Translog(getTranslogConfig(path));
|
|
|
+ }
|
|
|
+
|
|
|
+ protected TranslogConfig getTranslogConfig(Path path) {
|
|
|
Settings build = Settings.settingsBuilder()
|
|
|
.put(TranslogConfig.INDEX_TRANSLOG_FS_TYPE, TranslogWriter.Type.SIMPLE.name())
|
|
|
.put(IndexMetaData.SETTING_VERSION_CREATED, org.elasticsearch.Version.CURRENT)
|
|
|
.build();
|
|
|
- TranslogConfig translogConfig = new TranslogConfig(shardId, path, IndexSettingsModule.newIndexSettings(shardId.index(), build), Translog.Durabilty.REQUEST, BigArrays.NON_RECYCLING_INSTANCE, null);
|
|
|
- return new Translog(translogConfig);
|
|
|
+ return new TranslogConfig(shardId, path, IndexSettingsModule.newIndexSettings(shardId.index(), build), Translog.Durabilty.REQUEST, BigArrays.NON_RECYCLING_INSTANCE, null);
|
|
|
}
|
|
|
|
|
|
protected void addToTranslogAndList(Translog translog, ArrayList<Translog.Operation> list, Translog.Operation op) {
|
|
|
@@ -1279,4 +1283,108 @@ public class TranslogTests extends ESTestCase {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public void testFailFlush() throws IOException {
|
|
|
+ Path tempDir = createTempDir();
|
|
|
+ final AtomicBoolean failWrite = new AtomicBoolean();
|
|
|
+ final AtomicBoolean simulateDiskFull = new AtomicBoolean();
|
|
|
+ TranslogConfig config = getTranslogConfig(tempDir);
|
|
|
+ Translog translog = new Translog(config) {
|
|
|
+ @Override
|
|
|
+ TranslogWriter.ChannelFactory getChannelFactory() {
|
|
|
+ final TranslogWriter.ChannelFactory factory = super.getChannelFactory();
|
|
|
+
|
|
|
+ return new TranslogWriter.ChannelFactory() {
|
|
|
+ @Override
|
|
|
+ public FileChannel open(Path file) throws IOException {
|
|
|
+ FileChannel channel = factory.open(file);
|
|
|
+ return new FilterFileChannel(channel) {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int write(ByteBuffer src) throws IOException {
|
|
|
+ if (failWrite.get()) {
|
|
|
+ throw new IOException("boom");
|
|
|
+ }
|
|
|
+ if (simulateDiskFull.get()) {
|
|
|
+ if (src.limit() > 1) {
|
|
|
+ final int pos = src.position();
|
|
|
+ final int limit = src.limit();
|
|
|
+ src.limit(limit / 2);
|
|
|
+ super.write(src);
|
|
|
+ src.position(pos);
|
|
|
+ src.limit(limit);
|
|
|
+ throw new IOException("no space left on device");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return super.write(src);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ List<Translog.Location> locations = new ArrayList<>();
|
|
|
+ int opsSynced = 0;
|
|
|
+ int opsAdded = 0;
|
|
|
+ boolean failed = false;
|
|
|
+ boolean syncFailed = true;
|
|
|
+ while(failed == false) {
|
|
|
+ try {
|
|
|
+ locations.add(translog.add(new Translog.Index("test", "" + opsSynced, Integer.toString(opsSynced).getBytes(Charset.forName("UTF-8")))));
|
|
|
+ opsAdded++;
|
|
|
+ translog.sync();
|
|
|
+ opsSynced++;
|
|
|
+ } catch (IOException ex) {
|
|
|
+ failed = true;
|
|
|
+ assertEquals("no space left on device", ex.getMessage());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ failed = true;
|
|
|
+ assertTrue(ex.toString(), ex.getMessage().startsWith("Failed to write operation"));
|
|
|
+ }
|
|
|
+ simulateDiskFull.set(randomBoolean());
|
|
|
+ }
|
|
|
+ simulateDiskFull.set(false);
|
|
|
+ if (randomBoolean()) {
|
|
|
+ try {
|
|
|
+ locations.add(translog.add(new Translog.Index("test", "" + opsSynced, Integer.toString(opsSynced).getBytes(Charset.forName("UTF-8")))));
|
|
|
+ opsSynced++;
|
|
|
+ } catch (AlreadyClosedException ex) {
|
|
|
+ assertNotNull(ex.getCause());
|
|
|
+ assertEquals(ex.getCause().getMessage(), "no space left on device");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ Translog.TranslogGeneration translogGeneration = translog.getGeneration();
|
|
|
+ try {
|
|
|
+ translog.newSnapshot();
|
|
|
+ fail("already closed");
|
|
|
+ } catch (AlreadyClosedException ex) {
|
|
|
+ // all is well
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ translog.close();
|
|
|
+ if (opsAdded != opsSynced) {
|
|
|
+ fail("already closed");
|
|
|
+ }
|
|
|
+ } catch (AlreadyClosedException ex) {
|
|
|
+ assertNotNull(ex.getCause());
|
|
|
+ }
|
|
|
+ config.setTranslogGeneration(translogGeneration);
|
|
|
+ try (Translog tlog = new Translog(config)){
|
|
|
+ assertEquals("lastCommitted must be 1 less than current", translogGeneration.translogFileGeneration + 1, tlog.currentFileGeneration());
|
|
|
+ assertFalse(tlog.syncNeeded());
|
|
|
+
|
|
|
+ try (Translog.Snapshot snapshot = tlog.newSnapshot()) {
|
|
|
+ assertEquals(opsSynced, snapshot.estimatedTotalOperations());
|
|
|
+ for (int i = 0; i < opsSynced; i++) {
|
|
|
+ assertEquals("expected operation" + i + " to be in the previous translog but wasn't", tlog.currentFileGeneration() - 1, locations.get(i).generation);
|
|
|
+ Translog.Operation next = snapshot.next();
|
|
|
+ assertNotNull("operation " + i + " must be non-null", next);
|
|
|
+ assertEquals(i, Integer.parseInt(next.getSource().source.toUtf8()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|