diff --git a/hbase-procedure/pom.xml b/hbase-procedure/pom.xml index 73bc8660200..3fedda8e193 100644 --- a/hbase-procedure/pom.xml +++ b/hbase-procedure/pom.xml @@ -100,6 +100,11 @@ org.apache.hbase hbase-metrics-api + + org.mockito + mockito-core + test + diff --git a/hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/store/wal/WALProcedureStore.java b/hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/store/wal/WALProcedureStore.java index 8581d825d5e..c5680cfa9b8 100644 --- a/hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/store/wal/WALProcedureStore.java +++ b/hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/store/wal/WALProcedureStore.java @@ -359,21 +359,20 @@ public class WALProcedureStore extends ProcedureStoreBase { lock.lock(); try { LOG.trace("Starting WAL Procedure Store lease recovery"); - FileStatus[] oldLogs = getLogFiles(); while (isRunning()) { + FileStatus[] oldLogs = getLogFiles(); // Get Log-MaxID and recover lease on old logs try { flushLogId = initOldLogs(oldLogs); } catch (FileNotFoundException e) { LOG.warn("Someone else is active and deleted logs. retrying.", e); - oldLogs = getLogFiles(); continue; } // Create new state-log if (!rollWriter(flushLogId + 1)) { // someone else has already created this log - LOG.debug("Someone else has already created log " + flushLogId); + LOG.debug("Someone else has already created log {}. Retrying.", flushLogId); continue; } @@ -1002,7 +1001,8 @@ public class WALProcedureStore extends ProcedureStoreBase { return true; } - private boolean rollWriter(final long logId) throws IOException { + @VisibleForTesting + boolean rollWriter(final long logId) throws IOException { assert logId > flushLogId : "logId=" + logId + " flushLogId=" + flushLogId; assert lock.isHeldByCurrentThread() : "expected to be the lock owner. " + lock.isLocked(); @@ -1072,7 +1072,10 @@ public class WALProcedureStore extends ProcedureStoreBase { } private void closeCurrentLogStream() { - if (stream == null) return; + if (stream == null || logs.isEmpty()) { + return; + } + try { ProcedureWALFile log = logs.getLast(); log.setProcIds(storeTracker.getUpdatedMinProcId(), storeTracker.getUpdatedMaxProcId()); diff --git a/hbase-procedure/src/test/java/org/apache/hadoop/hbase/procedure2/store/wal/TestWALProcedureStore.java b/hbase-procedure/src/test/java/org/apache/hadoop/hbase/procedure2/store/wal/TestWALProcedureStore.java index 1929c0c7361..64cf211161e 100644 --- a/hbase-procedure/src/test/java/org/apache/hadoop/hbase/procedure2/store/wal/TestWALProcedureStore.java +++ b/hbase-procedure/src/test/java/org/apache/hadoop/hbase/procedure2/store/wal/TestWALProcedureStore.java @@ -54,6 +54,9 @@ import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -759,6 +762,40 @@ public class TestWALProcedureStore { assertEquals(0, loader.getCorruptedCount()); } + @Test + public void testLogFileAleadExists() throws IOException { + final boolean[] tested = {false}; + WALProcedureStore mStore = Mockito.spy(procStore); + + Answer ans = new Answer() { + @Override + public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable { + long logId = ((Long) invocationOnMock.getArgument(0)).longValue(); + switch ((int) logId) { + case 2: + // Create a file so that real rollWriter() runs into file exists condition + Path logFilePath = mStore.getLogFilePath(logId); + mStore.getFileSystem().create(logFilePath); + break; + case 3: + // Success only when we retry with logId 3 + tested[0] = true; + default: + break; + } + return (Boolean) invocationOnMock.callRealMethod(); + } + }; + + // First time Store has one log file, next id will be 2 + Mockito.doAnswer(ans).when(mStore).rollWriter(2); + // next time its 3 + Mockito.doAnswer(ans).when(mStore).rollWriter(3); + + mStore.recoverLease(); + assertTrue(tested[0]); + } + @Test public void testLoadChildren() throws Exception { TestProcedure a = new TestProcedure(1, 0);