You are viewing a plain text version of this content. The canonical link for it is here.
Posted to hdfs-commits@hadoop.apache.org by su...@apache.org on 2012/06/29 01:00:12 UTC
svn commit: r1355189 - in
/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs: ./
src/main/java/org/apache/hadoop/hdfs/server/namenode/
src/test/java/org/apache/hadoop/hdfs/server/namenode/
Author: suresh
Date: Thu Jun 28 23:00:10 2012
New Revision: 1355189
URL: http://svn.apache.org/viewvc?rev=1355189&view=rev
Log:
HDFS-3510. Editlog pre-allocation is performed prior to writing edits to avoid partial edits case disk out of space. Contributed by Collin McCabe.
Modified:
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditLogFileOutputStream.java
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditsDoubleBuffer.java
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogFileOutputStream.java
hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestNameNodeRecovery.java
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt Thu Jun 28 23:00:10 2012
@@ -99,6 +99,9 @@ Trunk (unreleased changes)
HDFS-3571. Allow EditLogFileInputStream to read from a remote URL (todd)
+ HDFS-3510. Editlog pre-allocation is performed prior to writing edits
+ to avoid partial edits case disk out of space.(Collin McCabe via suresh)
+
OPTIMIZATIONS
BUG FIXES
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditLogFileOutputStream.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditLogFileOutputStream.java?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditLogFileOutputStream.java (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditLogFileOutputStream.java Thu Jun 28 23:00:10 2012
@@ -41,13 +41,13 @@ import com.google.common.annotations.Vis
@InterfaceAudience.Private
public class EditLogFileOutputStream extends EditLogOutputStream {
private static Log LOG = LogFactory.getLog(EditLogFileOutputStream.class);
- public static final int PREALLOCATION_LENGTH = 1024 * 1024;
+ public static final int MIN_PREALLOCATION_LENGTH = 1024 * 1024;
private File file;
private FileOutputStream fp; // file stream for storing edit logs
private FileChannel fc; // channel of the file stream for sync
private EditsDoubleBuffer doubleBuf;
- static ByteBuffer fill = ByteBuffer.allocateDirect(PREALLOCATION_LENGTH);
+ static ByteBuffer fill = ByteBuffer.allocateDirect(MIN_PREALLOCATION_LENGTH);
static {
fill.position(0);
@@ -132,7 +132,7 @@ public class EditLogFileOutputStream ext
doubleBuf = null;
}
- // remove the last INVALID marker from transaction log.
+ // remove any preallocated padding bytes from the transaction log.
if (fc != null && fc.isOpen()) {
fc.truncate(fc.position());
fc.close();
@@ -166,7 +166,6 @@ public class EditLogFileOutputStream ext
*/
@Override
public void setReadyToFlush() throws IOException {
- doubleBuf.getCurrentBuf().write(FSEditLogOpCodes.OP_INVALID.getOpCode()); // insert eof marker
doubleBuf.setReadyToFlush();
}
@@ -183,10 +182,9 @@ public class EditLogFileOutputStream ext
LOG.info("Nothing to flush");
return;
}
+ preallocate(); // preallocate file if necessay
doubleBuf.flushTo(fp);
fc.force(false); // metadata updates not needed
- fc.position(fc.position() - 1); // skip back the end-of-file marker
- preallocate(); // preallocate file if necessary
}
/**
@@ -197,20 +195,27 @@ public class EditLogFileOutputStream ext
return doubleBuf.shouldForceSync();
}
- // allocate a big chunk of data
private void preallocate() throws IOException {
long position = fc.position();
- if (position + 4096 >= fc.size()) {
- if(FSNamesystem.LOG.isDebugEnabled()) {
- FSNamesystem.LOG.debug("Preallocating Edit log, current size "
- + fc.size());
- }
+ long size = fc.size();
+ int bufSize = doubleBuf.getReadyBuf().getLength();
+ long need = bufSize - (size - position);
+ if (need <= 0) {
+ return;
+ }
+ long oldSize = size;
+ long total = 0;
+ long fillCapacity = fill.capacity();
+ while (need > 0) {
fill.position(0);
- IOUtils.writeFully(fc, fill, position);
- if(FSNamesystem.LOG.isDebugEnabled()) {
- FSNamesystem.LOG.debug("Edit log size is now " + fc.size() +
- " written " + fill.capacity() + " bytes " + " at offset " + position);
- }
+ IOUtils.writeFully(fc, fill, size);
+ need -= fillCapacity;
+ size += fillCapacity;
+ total += fillCapacity;
+ }
+ if(FSNamesystem.LOG.isDebugEnabled()) {
+ FSNamesystem.LOG.debug("Preallocated " + total + " bytes at the end of " +
+ "the edit log (offset " + oldSize + ")");
}
}
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditsDoubleBuffer.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditsDoubleBuffer.java?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditsDoubleBuffer.java (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EditsDoubleBuffer.java Thu Jun 28 23:00:10 2012
@@ -89,6 +89,10 @@ class EditsDoubleBuffer {
return bufCurrent.size() >= initBufferSize;
}
+ DataOutputBuffer getReadyBuf() {
+ return bufReady;
+ }
+
DataOutputBuffer getCurrentBuf() {
return bufCurrent;
}
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java Thu Jun 28 23:00:10 2012
@@ -2278,25 +2278,7 @@ public abstract class FSEditLogOp {
public FSEditLogOp readOp(boolean skipBrokenEdits) throws IOException {
while (true) {
try {
- limiter.setLimit(MAX_OP_SIZE);
- in.mark(MAX_OP_SIZE);
return decodeOp();
- } catch (GarbageAfterTerminatorException e) {
- in.reset();
- if (!skipBrokenEdits) {
- throw e;
- }
- // If we saw a terminator opcode followed by a long region of 0x00 or
- // 0xff, we want to skip over that region, because there's nothing
- // interesting there.
- long numSkip = e.getNumAfterTerminator();
- try {
- IOUtils.skipFully(in, numSkip);
- } catch (Throwable t) {
- FSImage.LOG.error("Failed to skip " + numSkip + " bytes of " +
- "garbage after an OP_INVALID.", t);
- return null;
- }
} catch (IOException e) {
in.reset();
if (!skipBrokenEdits) {
@@ -2325,7 +2307,6 @@ public abstract class FSEditLogOp {
}
private void verifyTerminator() throws IOException {
- long off = 0;
/** The end of the edit log should contain only 0x00 or 0xff bytes.
* If it contains other bytes, the log itself may be corrupt.
* It is important to check this; if we don't, a stray OP_INVALID byte
@@ -2333,21 +2314,49 @@ public abstract class FSEditLogOp {
* know that we had lost data.
*/
byte[] buf = new byte[4096];
+ limiter.clearLimit();
+ int numRead = -1, idx = 0;
while (true) {
- int numRead = in.read(buf);
- if (numRead == -1) {
- return;
- }
- for (int i = 0; i < numRead; i++, off++) {
- if ((buf[i] != (byte)0) && (buf[i] != (byte)-1)) {
- throw new GarbageAfterTerminatorException("Read garbage after " +
- "the terminator!", off);
+ try {
+ numRead = -1;
+ idx = 0;
+ numRead = in.read(buf);
+ if (numRead == -1) {
+ return;
+ }
+ while (idx < numRead) {
+ if ((buf[idx] != (byte)0) && (buf[idx] != (byte)-1)) {
+ throw new IOException("Read extra bytes after " +
+ "the terminator!");
+ }
+ idx++;
+ }
+ } finally {
+ // After reading each group of bytes, we reposition the mark one
+ // byte before the next group. Similarly, if there is an error, we
+ // want to reposition the mark one byte before the error
+ if (numRead != -1) {
+ in.reset();
+ IOUtils.skipFully(in, idx);
+ in.mark(buf.length + 1);
+ IOUtils.skipFully(in, 1);
}
}
}
}
+ /**
+ * Read an opcode from the input stream.
+ *
+ * @return the opcode, or null on EOF.
+ *
+ * If an exception is thrown, the stream's mark will be set to the first
+ * problematic byte. This usually means the beginning of the opcode.
+ */
private FSEditLogOp decodeOp() throws IOException {
+ limiter.setLimit(MAX_OP_SIZE);
+ in.mark(MAX_OP_SIZE);
+
if (checksum != null) {
checksum.reset();
}
@@ -2534,35 +2543,4 @@ public abstract class FSEditLogOp {
short mode = Short.valueOf(st.getValue("MODE"));
return new PermissionStatus(username, groupname, new FsPermission(mode));
}
-
- /**
- * Exception indicating that we found an OP_INVALID followed by some
- * garbage. An OP_INVALID should signify the end of the file... if there
- * is additional content after that, then the edit log is corrupt.
- */
- static class GarbageAfterTerminatorException extends IOException {
- private static final long serialVersionUID = 1L;
- private final long numAfterTerminator;
-
- public GarbageAfterTerminatorException(String str,
- long numAfterTerminator) {
- super(str);
- this.numAfterTerminator = numAfterTerminator;
- }
-
- /**
- * Get the number of bytes after the terminator at which the garbage
- * appeared.
- *
- * So if you had an OP_INVALID followed immediately by another valid opcode,
- * this would be 0.
- * If you had an OP_INVALID followed by some padding bytes, followed by a
- * stray byte at the end, this would be the number of padding bytes.
- *
- * @return numAfterTerminator
- */
- public long getNumAfterTerminator() {
- return numAfterTerminator;
- }
- }
}
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogFileOutputStream.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogFileOutputStream.java?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogFileOutputStream.java (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogFileOutputStream.java Thu Jun 28 23:00:10 2012
@@ -20,105 +20,74 @@ package org.apache.hadoop.hdfs.server.na
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
-import java.nio.channels.FileChannel;
import org.apache.hadoop.util.StringUtils;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.DU;
-import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.fs.permission.FsPermission;
-import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
-import org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.EditLogValidation;
-import org.apache.hadoop.hdfs.HdfsConfiguration;
-import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
+/**
+ * Test the EditLogFileOutputStream
+ */
public class TestEditLogFileOutputStream {
- private final static int HEADER_LEN = 17;
+ private final static File TEST_DIR =
+ new File(System.getProperty("test.build.data", "/tmp"));
private static final File TEST_EDITS =
- new File(System.getProperty("test.build.data","/tmp"),
- "editLogStream.dat");
+ new File(TEST_DIR, "testEditLogFileOutput.log");
+ final static int MIN_PREALLOCATION_LENGTH =
+ EditLogFileOutputStream.MIN_PREALLOCATION_LENGTH;
@Before
+ @After
public void deleteEditsFile() {
- TEST_EDITS.delete();
+ if (TEST_EDITS.exists()) TEST_EDITS.delete();
}
- @Test
- public void testConstants() {
- // Each call to FSEditLogOp#Reader#readOp can read at most MAX_OP_SIZE bytes
- // before getting an exception. So we don't want to preallocate a longer
- // region than MAX_OP_SIZE, because then we'd get an IOException when reading
- // through the padding at the end of the file.
- assertTrue(EditLogFileOutputStream.PREALLOCATION_LENGTH <
- FSEditLogOp.MAX_OP_SIZE);
- }
-
- @Test
- public void testPreallocation() throws IOException {
- Configuration conf = new HdfsConfiguration();
- MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0)
- .build();
-
- final long START_TXID = 1;
- StorageDirectory sd = cluster.getNameNode().getFSImage()
- .getStorage().getStorageDir(0);
- File editLog = NNStorage.getInProgressEditsFile(sd, START_TXID);
-
- EditLogValidation validation = EditLogFileInputStream.validateEditLog(editLog);
- assertEquals("Edit log should contain a header as valid length",
- HEADER_LEN, validation.getValidLength());
- assertEquals(validation.getEndTxId(), START_TXID);
- assertEquals("Edit log should have 1MB pre-allocated, plus 4 bytes " +
- "for the version number",
- EditLogFileOutputStream.PREALLOCATION_LENGTH + 4, editLog.length());
-
-
- cluster.getFileSystem().mkdirs(new Path("/tmp"),
- new FsPermission((short)777));
-
- long oldLength = validation.getValidLength();
- validation = EditLogFileInputStream.validateEditLog(editLog);
- assertTrue("Edit log should have more valid data after writing a txn " +
- "(was: " + oldLength + " now: " + validation.getValidLength() + ")",
- validation.getValidLength() > oldLength);
- assertEquals(1, validation.getEndTxId() - START_TXID);
-
- assertEquals("Edit log should be 1MB long, plus 4 bytes for the version number",
- EditLogFileOutputStream.PREALLOCATION_LENGTH + 4, editLog.length());
- // 256 blocks for the 1MB of preallocation space
- assertTrue("Edit log disk space used should be at least 257 blocks",
- 256 * 4096 <= new DU(editLog, conf).getUsed());
+ static void flushAndCheckLength(EditLogFileOutputStream elos,
+ long expectedLength) throws IOException {
+ elos.setReadyToFlush();
+ elos.flushAndSync();
+ assertEquals(expectedLength, elos.getFile().length());
}
+ /**
+ * Tests writing to the EditLogFileOutputStream. Due to preallocation, the
+ * length of the edit log will usually be longer than its valid contents.
+ */
@Test
- public void testClose() throws IOException {
- String errorMessage = "TESTING: fc.truncate() threw IOE";
-
- File testDir = new File(System.getProperty("test.build.data", "/tmp"));
- assertTrue("could not create test directory", testDir.exists() || testDir.mkdirs());
- File f = new File(testDir, "edits");
- assertTrue("could not create test file", f.createNewFile());
- EditLogFileOutputStream elos = new EditLogFileOutputStream(f, 0);
-
- FileChannel mockFc = Mockito.spy(elos.getFileChannelForTesting());
- Mockito.doThrow(new IOException(errorMessage)).when(mockFc).truncate(Mockito.anyLong());
- elos.setFileChannelForTesting(mockFc);
-
+ public void testRawWrites() throws IOException {
+ EditLogFileOutputStream elos = new EditLogFileOutputStream(TEST_EDITS, 0);
try {
- elos.close();
- fail("elos.close() succeeded, but should have thrown");
- } catch (IOException e) {
- assertEquals("wrong IOE thrown from elos.close()", e.getMessage(), errorMessage);
+ byte[] small = new byte[] {1,2,3,4,5,8,7};
+ elos.create();
+ // The first (small) write we make extends the file by 1 MB due to
+ // preallocation.
+ elos.writeRaw(small, 0, small.length);
+ flushAndCheckLength(elos, MIN_PREALLOCATION_LENGTH);
+ // The next small write we make goes into the area that was already
+ // preallocated.
+ elos.writeRaw(small, 0, small.length);
+ flushAndCheckLength(elos, MIN_PREALLOCATION_LENGTH);
+ // Now we write enough bytes so that we exceed the minimum preallocated
+ // length.
+ final int BIG_WRITE_LENGTH = 3 * MIN_PREALLOCATION_LENGTH;
+ byte[] buf = new byte[4096];
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = 0;
+ }
+ int total = BIG_WRITE_LENGTH;
+ while (total > 0) {
+ int toWrite = (total > buf.length) ? buf.length : total;
+ elos.writeRaw(buf, 0, toWrite);
+ total -= toWrite;
+ }
+ flushAndCheckLength(elos, 4 * MIN_PREALLOCATION_LENGTH);
+ } finally {
+ if (elos != null) elos.close();
}
-
- assertEquals("fc was not nulled when elos.close() failed", elos.getFileChannelForTesting(), null);
}
/**
@@ -164,5 +133,4 @@ public class TestEditLogFileOutputStream
editLogStream.abort();
editLogStream.abort();
}
-
}
Modified: hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestNameNodeRecovery.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestNameNodeRecovery.java?rev=1355189&r1=1355188&r2=1355189&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestNameNodeRecovery.java (original)
+++ hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestNameNodeRecovery.java Thu Jun 28 23:00:10 2012
@@ -135,6 +135,9 @@ public class TestNameNodeRecovery {
}
}
+ /**
+ * A test scenario for the edit log
+ */
private interface EditLogTestSetup {
/**
* Set up the edit log.
@@ -156,10 +159,50 @@ public class TestNameNodeRecovery {
abstract public Set<Long> getValidTxIds();
}
- private class EltsTestEmptyLog implements EditLogTestSetup {
+ static void padEditLog(EditLogOutputStream elos, int paddingLength)
+ throws IOException {
+ if (paddingLength <= 0) {
+ return;
+ }
+ byte buf[] = new byte[4096];
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = (byte)-1;
+ }
+ int pad = paddingLength;
+ while (pad > 0) {
+ int toWrite = pad > buf.length ? buf.length : pad;
+ elos.writeRaw(buf, 0, toWrite);
+ pad -= toWrite;
+ }
+ }
+
+ static void addDeleteOpcode(EditLogOutputStream elos,
+ OpInstanceCache cache) throws IOException {
+ DeleteOp op = DeleteOp.getInstance(cache);
+ op.setTransactionId(0x0);
+ op.setPath("/foo");
+ op.setTimestamp(0);
+ elos.write(op);
+ }
+
+ /**
+ * Test the scenario where we have an empty edit log.
+ *
+ * This class is also useful in testing whether we can correctly handle
+ * various amounts of padding bytes at the end of the log. We should be
+ * able to handle any amount of padding (including no padding) without
+ * throwing an exception.
+ */
+ private static class EltsTestEmptyLog implements EditLogTestSetup {
+ private int paddingLength;
+
+ public EltsTestEmptyLog(int paddingLength) {
+ this.paddingLength = paddingLength;
+ }
+
public void addTransactionsToLog(EditLogOutputStream elos,
OpInstanceCache cache) throws IOException {
- // do nothing
+ padEditLog(elos, paddingLength);
}
public long getLastValidTxId() {
@@ -174,10 +217,65 @@ public class TestNameNodeRecovery {
/** Test an empty edit log */
@Test(timeout=180000)
public void testEmptyLog() throws IOException {
- runEditLogTest(new EltsTestEmptyLog());
+ runEditLogTest(new EltsTestEmptyLog(0));
+ }
+
+ /** Test an empty edit log with padding */
+ @Test(timeout=180000)
+ public void testEmptyPaddedLog() throws IOException {
+ runEditLogTest(new EltsTestEmptyLog(
+ EditLogFileOutputStream.MIN_PREALLOCATION_LENGTH));
}
- private class EltsTestGarbageInEditLog implements EditLogTestSetup {
+ /** Test an empty edit log with extra-long padding */
+ @Test(timeout=180000)
+ public void testEmptyExtraPaddedLog() throws IOException {
+ runEditLogTest(new EltsTestEmptyLog(
+ 3 * EditLogFileOutputStream.MIN_PREALLOCATION_LENGTH));
+ }
+
+ /**
+ * Test the scenario where an edit log contains some padding (0xff) bytes
+ * followed by valid opcode data.
+ *
+ * These edit logs are corrupt, but all the opcodes should be recoverable
+ * with recovery mode.
+ */
+ private static class EltsTestOpcodesAfterPadding implements EditLogTestSetup {
+ private int paddingLength;
+
+ public EltsTestOpcodesAfterPadding(int paddingLength) {
+ this.paddingLength = paddingLength;
+ }
+
+ public void addTransactionsToLog(EditLogOutputStream elos,
+ OpInstanceCache cache) throws IOException {
+ padEditLog(elos, paddingLength);
+ addDeleteOpcode(elos, cache);
+ }
+
+ public long getLastValidTxId() {
+ return 0;
+ }
+
+ public Set<Long> getValidTxIds() {
+ return Sets.newHashSet(0L);
+ }
+ }
+
+ @Test(timeout=180000)
+ public void testOpcodesAfterPadding() throws IOException {
+ runEditLogTest(new EltsTestOpcodesAfterPadding(
+ EditLogFileOutputStream.MIN_PREALLOCATION_LENGTH));
+ }
+
+ @Test(timeout=180000)
+ public void testOpcodesAfterExtraPadding() throws IOException {
+ runEditLogTest(new EltsTestOpcodesAfterPadding(
+ 3 * EditLogFileOutputStream.MIN_PREALLOCATION_LENGTH));
+ }
+
+ private static class EltsTestGarbageInEditLog implements EditLogTestSetup {
final private long BAD_TXID = 4;
final private long MAX_TXID = 10;