sijie closed pull request #1319:  Issue 1316: A bookie with non-writable dirs 
should be able to start in readonly mode
URL: https://github.com/apache/bookkeeper/pull/1319
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/EntryLogger.java 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/EntryLogger.java
index 66efbaa5b..99b0f49ad 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/EntryLogger.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/EntryLogger.java
@@ -25,6 +25,7 @@
 import static 
org.apache.bookkeeper.bookie.TransactionalEntryLogCompactor.COMPACTING_SUFFIX;
 import static 
org.apache.bookkeeper.util.BookKeeperConstants.MAX_LOG_SIZE_LIMIT;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.MapMaker;
 import com.google.common.collect.Sets;
@@ -84,6 +85,9 @@
 public class EntryLogger {
     private static final Logger LOG = 
LoggerFactory.getLogger(EntryLogger.class);
 
+    @VisibleForTesting
+    static final int UNINITIALIZED_LOG_ID = -0xDEAD;
+
     static class BufferedLogChannel extends BufferedChannel {
         private final long logId;
         private final EntryLogMetadata entryLogMetadata;
@@ -285,7 +289,7 @@ public EntryLogger(ServerConfiguration conf,
         for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
             if (!dir.exists()) {
                 throw new FileNotFoundException(
-                        "Entry log directory does not exist");
+                        "Entry log directory '" + dir + "' does not exist");
             }
             long lastLogId = getLastLogId(dir);
             if (lastLogId > logId) {
@@ -398,7 +402,12 @@ synchronized long getLeastUnflushedLogId() {
     }
 
     synchronized long getCurrentLogId() {
-        return logChannel.getLogId();
+        BufferedLogChannel channel = logChannel;
+        if (null == channel) {
+            return UNINITIALIZED_LOG_ID;
+        } else {
+            return channel.getLogId();
+        }
     }
 
     /**
@@ -416,8 +425,10 @@ File getCurCompactionLogFile() {
     protected void initialize() throws IOException {
         // Register listener for disk full notifications.
         ledgerDirsManager.addLedgerDirsListener(getLedgerDirsListener());
-        // create a new log to write
-        createNewLog();
+
+        if (ledgerDirsManager.hasWritableLedgerDirs()) {
+            createNewLog();
+        }
     }
 
     private LedgerDirsListener getLedgerDirsListener() {
@@ -870,6 +881,12 @@ protected ByteBuf initialValue() throws Exception {
     };
 
     public synchronized long addEntry(long ledger, ByteBuf entry, boolean 
rollLog) throws IOException {
+        if (null == logChannel) {
+            // log channel can be null because the file is deferred to be 
created when no writable ledger directory
+            // is available.
+            createNewLog();
+        }
+
         int entrySize = entry.readableBytes() + 4; // Adding 4 bytes to 
prepend the size
         boolean reachEntryLogLimit =
             rollLog ? reachEntryLogLimit(entrySize) : 
readEntryLogHardLimit(entrySize);
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDirsManager.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDirsManager.java
index 5d2c11ff7..a615cfaf4 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDirsManager.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerDirsManager.java
@@ -53,7 +53,7 @@
     private final ConcurrentMap<File, Float> diskUsages =
             new ConcurrentHashMap<File, Float>();
     private final long entryLogSize;
-    private boolean forceGCAllowWhenNoSpace;
+    private long minUsableSizeForEntryLogCreation;
     private long minUsableSizeForIndexFileCreation;
 
     private final DiskChecker diskChecker;
@@ -69,9 +69,9 @@ public LedgerDirsManager(ServerConfiguration conf, File[] 
dirs, DiskChecker disk
         this.writableLedgerDirectories = new 
ArrayList<File>(ledgerDirectories);
         this.filledDirs = new ArrayList<File>();
         this.listeners = new ArrayList<LedgerDirsListener>();
-        this.forceGCAllowWhenNoSpace = conf.getIsForceGCAllowWhenNoSpace();
         this.entryLogSize = conf.getEntryLogSizeLimit();
         this.minUsableSizeForIndexFileCreation = 
conf.getMinUsableSizeForIndexFileCreation();
+        this.minUsableSizeForEntryLogCreation = 
conf.getMinUsableSizeForEntryLogCreation();
         for (File dir : ledgerDirectories) {
             diskUsages.put(dir, 0f);
             String statName = "dir_" + dir.getParent().replace('/', '_') + 
"_usage";
@@ -176,19 +176,10 @@ public boolean hasWritableLedgerDirs() {
             return writableLedgerDirectories;
         }
 
-        // If Force GC is not allowed under no space
-        if (!forceGCAllowWhenNoSpace) {
-            String errMsg = "All ledger directories are non writable and force 
GC is not enabled.";
-            NoWritableLedgerDirException e = new 
NoWritableLedgerDirException(errMsg);
-            LOG.error(errMsg, e);
-            throw e;
-        }
-
-        // We don't have writable Ledger Dirs.
-        // That means we must have turned readonly but the compaction
-        // must have started running and it needs to allocate
-        // a new log file to move forward with the compaction.
-        return getDirsAboveUsableThresholdSize((long) (this.entryLogSize * 
1.2));
+        // We don't have writable Ledger Dirs. But we are still okay to create 
new entry log files if we have enough
+        // disk spaces. This allows bookie can still function at readonly 
mode. Because compaction, journal replays
+        // can still write data to disks.
+        return 
getDirsAboveUsableThresholdSize(minUsableSizeForEntryLogCreation);
     }
 
     List<File> getDirsAboveUsableThresholdSize(long thresholdSize) throws 
NoWritableLedgerDirException {
@@ -202,7 +193,7 @@ public boolean hasWritableLedgerDirs() {
 
         if (!fullLedgerDirsToAccomodate.isEmpty()) {
             LOG.info("No writable ledger dirs below diskUsageThreshold. "
-                    + "But Dirs that can accomodate {} are: {}", 
thresholdSize, fullLedgerDirsToAccomodate);
+                    + "But Dirs that can accommodate {} are: {}", 
thresholdSize, fullLedgerDirsToAccomodate);
             return fullLedgerDirsToAccomodate;
         }
 
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
index cb398216b..904ca3ffe 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
@@ -162,6 +162,7 @@
     protected static final String BOOKIE_AUTH_PROVIDER_FACTORY_CLASS = 
"bookieAuthProviderFactoryClass";
 
     protected static final String MIN_USABLESIZE_FOR_INDEXFILE_CREATION = 
"minUsableSizeForIndexFileCreation";
+    protected static final String MIN_USABLESIZE_FOR_ENTRYLOG_CREATION = 
"minUsableSizeForEntryLogCreation";
     protected static final String MIN_USABLESIZE_FOR_HIGH_PRIORITY_WRITES = 
"minUsableSizeForHighPriorityWrites";
 
     protected static final String ALLOW_MULTIPLEDIRS_UNDER_SAME_DISKPARTITION =
@@ -2510,7 +2511,8 @@ public ServerConfiguration 
setEnableTaskExecutionStats(boolean enabled) {
      * Gets the minimum safe Usable size to be available in index directory 
for Bookie to create Index File while
      * replaying journal at the time of Bookie Start in Readonly Mode (in 
bytes).
      *
-     * @return
+     * @return minimum safe usable size to be available in index directory for 
bookie to create index files.
+     * @see #setMinUsableSizeForIndexFileCreation(long)
      */
     public long getMinUsableSizeForIndexFileCreation() {
         return this.getLong(MIN_USABLESIZE_FOR_INDEXFILE_CREATION, 100 * 1024 
* 1024L);
@@ -2520,23 +2522,52 @@ public long getMinUsableSizeForIndexFileCreation() {
      * Sets the minimum safe Usable size to be available in index directory 
for Bookie to create Index File while
      * replaying journal at the time of Bookie Start in Readonly Mode (in 
bytes).
      *
-     * @param minUsableSizeForIndexFileCreation
-     * @return
+     * <p>This parameter allows creating index files when there are enough 
disk spaces, even when the bookie
+     * is running at readonly mode because of the disk usage is exceeding 
{@link #getDiskUsageThreshold()}. Because
+     * compaction, journal replays can still write index files to disks when a 
bookie is readonly.
+     *
+     * @param minUsableSizeForIndexFileCreation min usable size for index file 
creation
+     * @return server configuration
      */
     public ServerConfiguration setMinUsableSizeForIndexFileCreation(long 
minUsableSizeForIndexFileCreation) {
         this.setProperty(MIN_USABLESIZE_FOR_INDEXFILE_CREATION, 
minUsableSizeForIndexFileCreation);
         return this;
     }
 
+    /**
+     * Gets the minimum safe usable size to be available in ledger directory 
for Bookie to create entry log files.
+     *
+     * @return minimum safe usable size to be available in ledger directory 
for entry log file creation.
+     * @see #setMinUsableSizeForEntryLogCreation(long)
+     */
+    public long getMinUsableSizeForEntryLogCreation() {
+        return this.getLong(MIN_USABLESIZE_FOR_ENTRYLOG_CREATION, (long) 1.2 * 
getEntryLogSizeLimit());
+    }
+
+    /**
+     * Sets the minimum safe usable size to be available in ledger directory 
for Bookie to create entry log files.
+     *
+     * <p>This parameter allows creating entry log files when there are enough 
disk spaces, even when the bookie
+     * is running at readonly mode because of the disk usage is exceeding 
{@link #getDiskUsageThreshold()}. Because
+     * compaction, journal replays can still write data to disks when a bookie 
is readonly.
+     *
+     * @param minUsableSizeForEntryLogCreation minimum safe usable size to be 
available in ledger directory
+     * @return server configuration
+     */
+    public ServerConfiguration setMinUsableSizeForEntryLogCreation(long 
minUsableSizeForEntryLogCreation) {
+        this.setProperty(MIN_USABLESIZE_FOR_ENTRYLOG_CREATION, 
minUsableSizeForEntryLogCreation);
+        return this;
+    }
+
     /**
      * Gets the minimum safe usable size to be available in ledger directory 
for Bookie to accept high priority writes.
      *
-     * <p>If not set, it is two times of {@link #getEntryLogSizeLimit()}.
+     * <p>If not set, it is the value of {@link 
#getMinUsableSizeForEntryLogCreation()}.
      *
      * @return the minimum safe usable size per ledger directory for bookie to 
accept high priority writes.
      */
     public long getMinUsableSizeForHighPriorityWrites() {
-        return this.getLong(MIN_USABLESIZE_FOR_HIGH_PRIORITY_WRITES, 2 * 
getEntryLogSizeLimit());
+        return this.getLong(MIN_USABLESIZE_FOR_HIGH_PRIORITY_WRITES, 
getMinUsableSizeForEntryLogCreation());
     }
 
     /**
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/BookieInitializationTest.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/BookieInitializationTest.java
index 96790ac06..714a1c733 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/BookieInitializationTest.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/BookieInitializationTest.java
@@ -94,6 +94,7 @@ public BookieInitializationTest() {
         String ledgersPath = "/" + "ledgers" + runtime.getMethodName();
         baseClientConf.setZkLedgersRootPath(ledgersPath);
         baseConf.setZkLedgersRootPath(ledgersPath);
+        baseConf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
     }
 
     @Override
@@ -556,6 +557,7 @@ public void 
testWithDiskFullReadOnlyDisabledOrForceGCAllowDisabled() throws Exce
         long usableSpace = tmpDir.getUsableSpace();
         long totalSpace = tmpDir.getTotalSpace();
         final ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration()
+                
.setLedgerStorageClass(InterleavedLedgerStorage.class.getName())
                 .setJournalDirName(tmpDir.getPath())
                 .setLedgerDirNames(new String[] { tmpDir.getPath() })
                 .setDiskCheckInterval(1000)
@@ -567,7 +569,7 @@ public void 
testWithDiskFullReadOnlyDisabledOrForceGCAllowDisabled() throws Exce
         // if isForceGCAllowWhenNoSpace or readOnlyModeEnabled is not set and 
Bookie is
         // started when Disk is full, then it will fail to start with 
NoWritableLedgerDirException
 
-        conf.setIsForceGCAllowWhenNoSpace(false)
+        conf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE)
             .setReadOnlyModeEnabled(false);
         try {
             new Bookie(conf);
@@ -576,7 +578,7 @@ public void 
testWithDiskFullReadOnlyDisabledOrForceGCAllowDisabled() throws Exce
             // expected
         }
 
-        conf.setIsForceGCAllowWhenNoSpace(true)
+        conf.setMinUsableSizeForEntryLogCreation(Long.MIN_VALUE)
             .setReadOnlyModeEnabled(false);
         try {
             new Bookie(conf);
@@ -585,13 +587,19 @@ public void 
testWithDiskFullReadOnlyDisabledOrForceGCAllowDisabled() throws Exce
             // expected
         }
 
-        conf.setIsForceGCAllowWhenNoSpace(false)
+        conf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE)
             .setReadOnlyModeEnabled(true);
+        Bookie bookie = null;
         try {
-            new Bookie(conf);
-            fail("NoWritableLedgerDirException expected");
+            // bookie is okay to start up when readonly mode is enabled 
because entry log file creation
+            // is deferred.
+            bookie = new Bookie(conf);
         } catch (NoWritableLedgerDirException e) {
-            // expected
+            fail("NoWritableLedgerDirException unexpected");
+        } finally {
+            if (null != bookie) {
+                bookie.shutdown();
+            }
         }
     }
 
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/EntryLogTest.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/EntryLogTest.java
index cc02e4e8c..b7f286cf8 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/EntryLogTest.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/EntryLogTest.java
@@ -44,6 +44,7 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import 
org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
 import org.apache.bookkeeper.conf.ServerConfiguration;
 import org.apache.bookkeeper.conf.TestBKConfiguration;
 import org.apache.bookkeeper.util.DiskChecker;
@@ -51,6 +52,7 @@
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,8 +71,33 @@ File createTempDir(String prefix, String suffix) throws 
IOException {
         return dir;
     }
 
+    private File rootDir;
+    private File curDir;
+    private ServerConfiguration conf;
+    private LedgerDirsManager dirsMgr;
+    private EntryLogger entryLogger;
+
+    @Before
+    public void setUp() throws Exception {
+        this.rootDir = createTempDir("bkTest", ".dir");
+        this.curDir = Bookie.getCurrentDirectory(rootDir);
+        Bookie.checkDirectoryStructure(curDir);
+        this.conf = TestBKConfiguration.newServerConfiguration();
+        this.dirsMgr = new LedgerDirsManager(
+            conf,
+            new File[] { rootDir },
+            new DiskChecker(
+                conf.getDiskUsageThreshold(),
+                conf.getDiskUsageWarnThreshold()));
+        this.entryLogger = new EntryLogger(conf, dirsMgr);
+    }
+
     @After
     public void tearDown() throws Exception {
+        if (null != this.entryLogger) {
+            entryLogger.shutdown();
+        }
+
         for (File dir : tempDirs) {
             FileUtils.deleteDirectory(dir);
         }
@@ -78,33 +105,70 @@ public void tearDown() throws Exception {
     }
 
     @Test
-    public void testCorruptEntryLog() throws Exception {
-        File tmpDir = createTempDir("bkTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
+    public void testDeferCreateNewLog() throws Exception {
+        entryLogger.shutdown();
+
+        // mark `curDir` as filled
+        this.conf.setMinUsableSizeForEntryLogCreation(1);
+        this.dirsMgr = new LedgerDirsManager(
+            conf,
+            new File[] { rootDir },
+            new DiskChecker(
+                conf.getDiskUsageThreshold(),
+                conf.getDiskUsageWarnThreshold()));
+        this.dirsMgr.addToFilledDirs(curDir);
+
+        entryLogger = new EntryLogger(conf, dirsMgr);
+        assertEquals(EntryLogger.UNINITIALIZED_LOG_ID, 
entryLogger.getCurrentLogId());
+
+        // add the first entry will trigger file creation
+        entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
+        assertEquals(2L, entryLogger.getCurrentLogId());
+    }
 
-        int gcWaitTime = 1000;
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setJournalDirName(tmpDir.toString());
+    @Test
+    public void testDeferCreateNewLogWithoutEnoughDiskSpaces() throws 
Exception {
+        entryLogger.shutdown();
+
+        // mark `curDir` as filled
+        this.conf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
+        this.dirsMgr = new LedgerDirsManager(
+            conf,
+            new File[] { rootDir },
+            new DiskChecker(
+                conf.getDiskUsageThreshold(),
+                conf.getDiskUsageWarnThreshold()));
+        this.dirsMgr.addToFilledDirs(curDir);
+
+        entryLogger = new EntryLogger(conf, dirsMgr);
+        assertEquals(EntryLogger.UNINITIALIZED_LOG_ID, 
entryLogger.getCurrentLogId());
+
+        // add the first entry will trigger file creation
+        try {
+            entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
+            fail("Should fail to append entry if there is no enough reserved 
space left");
+        } catch (NoWritableLedgerDirException e) {
+            assertEquals(EntryLogger.UNINITIALIZED_LOG_ID, 
entryLogger.getCurrentLogId());
+        }
+    }
 
-        conf.setGcWaitTime(gcWaitTime);
-        conf.setLedgerDirNames(new String[] {tmpDir.toString()});
-        Bookie bookie = new Bookie(conf);
+    @Test
+    public void testCorruptEntryLog() throws Exception {
         // create some entries
-        EntryLogger logger = ((InterleavedLedgerStorage) 
bookie.ledgerStorage).entryLogger;
-        logger.addEntry(1, generateEntry(1, 1).nioBuffer());
-        logger.addEntry(3, generateEntry(3, 1).nioBuffer());
-        logger.addEntry(2, generateEntry(2, 1).nioBuffer());
-        logger.flush();
+        entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
+        entryLogger.addEntry(3, generateEntry(3, 1).nioBuffer());
+        entryLogger.addEntry(2, generateEntry(2, 1).nioBuffer());
+        entryLogger.flush();
+        entryLogger.shutdown();
         // now lets truncate the file to corrupt the last entry, which 
simulates a partial write
         File f = new File(curDir, "0.log");
         RandomAccessFile raf = new RandomAccessFile(f, "rw");
         raf.setLength(raf.length() - 10);
         raf.close();
         // now see which ledgers are in the log
-        logger = new EntryLogger(conf, bookie.getLedgerDirsManager());
+        entryLogger = new EntryLogger(conf, dirsMgr);
 
-        EntryLogMetadata meta = logger.getEntryLogMetadata(0L);
+        EntryLogMetadata meta = entryLogger.getEntryLogMetadata(0L);
         LOG.info("Extracted Meta From Entry Log {}", meta);
         assertTrue(meta.getLedgersMap().containsKey(1L));
         assertFalse(meta.getLedgersMap().containsKey(2L));
@@ -126,14 +190,6 @@ private static String generateDataString(long ledger, long 
entry) {
 
     @Test
     public void testMissingLogId() throws Exception {
-        File tmpDir = createTempDir("entryLogTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
-
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setJournalDirName(tmpDir.toString());
-        conf.setLedgerDirNames(new String[] {tmpDir.toString()});
-        Bookie bookie = new Bookie(conf);
         // create some entries
         int numLogs = 3;
         int numEntries = 10;
@@ -141,12 +197,12 @@ public void testMissingLogId() throws Exception {
         for (int i = 0; i < numLogs; i++) {
             positions[i] = new long[numEntries];
 
-            EntryLogger logger = new EntryLogger(conf,
-                    bookie.getLedgerDirsManager());
+            EntryLogger logger = new EntryLogger(conf, dirsMgr);
             for (int j = 0; j < numEntries; j++) {
                 positions[i][j] = logger.addEntry(i, generateEntry(i, 
j).nioBuffer());
             }
             logger.flush();
+            logger.shutdown();
         }
         // delete last log id
         File lastLogId = new File(curDir, "lastId");
@@ -156,16 +212,15 @@ public void testMissingLogId() throws Exception {
         for (int i = numLogs; i < 2 * numLogs; i++) {
             positions[i] = new long[numEntries];
 
-            EntryLogger logger = new EntryLogger(conf,
-                    bookie.getLedgerDirsManager());
+            EntryLogger logger = new EntryLogger(conf, dirsMgr);
             for (int j = 0; j < numEntries; j++) {
                 positions[i][j] = logger.addEntry(i, generateEntry(i, 
j).nioBuffer());
             }
             logger.flush();
+            logger.shutdown();
         }
 
-        EntryLogger newLogger = new EntryLogger(conf,
-                bookie.getLedgerDirsManager());
+        EntryLogger newLogger = new EntryLogger(conf, dirsMgr);
         for (int i = 0; i < (2 * numLogs + 1); i++) {
             File logFile = new File(curDir, Long.toHexString(i) + ".log");
             assertTrue(logFile.exists());
@@ -186,22 +241,20 @@ public void testMissingLogId() throws Exception {
         }
     }
 
-    @Test
     /**
      * Test that EntryLogger Should fail with FNFE, if entry logger 
directories does not exist.
      */
+    @Test
     public void testEntryLoggerShouldThrowFNFEIfDirectoriesDoesNotExist()
             throws Exception {
         File tmpDir = createTempDir("bkTest", ".dir");
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setLedgerDirNames(new String[] { tmpDir.toString() });
         EntryLogger entryLogger = null;
         try {
-            entryLogger = new EntryLogger(conf, new LedgerDirsManager(conf, 
conf.getLedgerDirs(),
+            entryLogger = new EntryLogger(conf, new LedgerDirsManager(conf, 
new File[] { tmpDir },
                     new DiskChecker(conf.getDiskUsageThreshold(), 
conf.getDiskUsageWarnThreshold())));
             fail("Expecting FileNotFoundException");
         } catch (FileNotFoundException e) {
-            assertEquals("Entry log directory does not exist", e
+            assertEquals("Entry log directory '" + tmpDir +  "/current' does 
not exist", e
                     .getLocalizedMessage());
         } finally {
             if (entryLogger != null) {
@@ -243,32 +296,19 @@ public void testAddEntryFailureOnDiskFull() throws 
Exception {
     }
 
     /**
-     * Explicitely try to recover using the ledgers map index at the end of 
the entry log.
+     * Explicitly try to recover using the ledgers map index at the end of the 
entry log.
      */
     @Test
     public void testRecoverFromLedgersMap() throws Exception {
-        File tmpDir = createTempDir("bkTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
-
-        int gcWaitTime = 1000;
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setJournalDirName(tmpDir.toString());
-
-        conf.setGcWaitTime(gcWaitTime);
-        conf.setLedgerDirNames(new String[] {tmpDir.toString()});
-        Bookie bookie = new Bookie(conf);
-
         // create some entries
-        EntryLogger logger = ((InterleavedLedgerStorage) 
bookie.ledgerStorage).entryLogger;
-        logger.addEntry(1, generateEntry(1, 1).nioBuffer());
-        logger.addEntry(3, generateEntry(3, 1).nioBuffer());
-        logger.addEntry(2, generateEntry(2, 1).nioBuffer());
-        logger.addEntry(1, generateEntry(1, 2).nioBuffer());
-        logger.rollLog();
-        logger.flushRotatedLogs();
-
-        EntryLogMetadata meta = logger.extractEntryLogMetadataFromIndex(0L);
+        entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
+        entryLogger.addEntry(3, generateEntry(3, 1).nioBuffer());
+        entryLogger.addEntry(2, generateEntry(2, 1).nioBuffer());
+        entryLogger.addEntry(1, generateEntry(1, 2).nioBuffer());
+        entryLogger.rollLog();
+        entryLogger.flushRotatedLogs();
+
+        EntryLogMetadata meta = 
entryLogger.extractEntryLogMetadataFromIndex(0L);
         LOG.info("Extracted Meta From Entry Log {}", meta);
         assertEquals(60, meta.getLedgersMap().get(1L));
         assertEquals(30, meta.getLedgersMap().get(2L));
@@ -279,28 +319,17 @@ public void testRecoverFromLedgersMap() throws Exception {
     }
 
     /**
-     * Explicitely try to recover using the ledgers map index at the end of 
the entry log.
+     * Explicitly try to recover using the ledgers map index at the end of the 
entry log.
      */
     @Test
     public void testRecoverFromLedgersMapOnV0EntryLog() throws Exception {
-        File tmpDir = createTempDir("bkTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
-
-        int gcWaitTime = 1000;
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setGcWaitTime(gcWaitTime);
-        conf.setLedgerDirNames(new String[] { tmpDir.toString() });
-        conf.setJournalDirName(tmpDir.toString());
-        Bookie bookie = new Bookie(conf);
-
         // create some entries
-        EntryLogger logger = ((InterleavedLedgerStorage) 
bookie.ledgerStorage).entryLogger;
-        logger.addEntry(1, generateEntry(1, 1).nioBuffer());
-        logger.addEntry(3, generateEntry(3, 1).nioBuffer());
-        logger.addEntry(2, generateEntry(2, 1).nioBuffer());
-        logger.addEntry(1, generateEntry(1, 2).nioBuffer());
-        logger.rollLog();
+        entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
+        entryLogger.addEntry(3, generateEntry(3, 1).nioBuffer());
+        entryLogger.addEntry(2, generateEntry(2, 1).nioBuffer());
+        entryLogger.addEntry(1, generateEntry(1, 2).nioBuffer());
+        entryLogger.rollLog();
+        entryLogger.shutdown();
 
         // Rewrite the entry log header to be on V0 format
         File f = new File(curDir, "0.log");
@@ -311,17 +340,17 @@ public void testRecoverFromLedgersMapOnV0EntryLog() 
throws Exception {
         raf.close();
 
         // now see which ledgers are in the log
-        logger = new EntryLogger(conf, bookie.getLedgerDirsManager());
+        entryLogger = new EntryLogger(conf, dirsMgr);
 
         try {
-            logger.extractEntryLogMetadataFromIndex(0L);
+            entryLogger.extractEntryLogMetadataFromIndex(0L);
             fail("Should not be possible to recover from ledgers map index");
         } catch (IOException e) {
             // Ok
         }
 
         // Public method should succeed by falling back to scanning the file
-        EntryLogMetadata meta = logger.getEntryLogMetadata(0L);
+        EntryLogMetadata meta = entryLogger.getEntryLogMetadata(0L);
         LOG.info("Extracted Meta From Entry Log {}", meta);
         assertEquals(60, meta.getLedgersMap().get(1L));
         assertEquals(30, meta.getLedgersMap().get(2L));
@@ -337,37 +366,30 @@ public void testRecoverFromLedgersMapOnV0EntryLog() 
throws Exception {
      */
     @Test
     public void testPreAllocateLog() throws Exception {
-        File tmpDir = createTempDir("bkTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
+        entryLogger.shutdown();
 
         // enable pre-allocation case
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setLedgerDirNames(new String[] {tmpDir.toString()});
         conf.setEntryLogFilePreAllocationEnabled(true);
-        Bookie bookie = new Bookie(conf);
+
+        entryLogger = new EntryLogger(conf, dirsMgr);
         // create a logger whose initialization phase allocating a new entry 
log
-        EntryLogger logger = ((InterleavedLedgerStorage) 
bookie.ledgerStorage).entryLogger;
-        
assertNotNull(logger.getEntryLoggerAllocator().getPreallocationFuture());
+        
assertNotNull(entryLogger.getEntryLoggerAllocator().getPreallocationFuture());
 
-        logger.addEntry(1, generateEntry(1, 1).nioBuffer());
+        entryLogger.addEntry(1, generateEntry(1, 1).nioBuffer());
         // the Future<BufferedLogChannel> is not null all the time
-        
assertNotNull(logger.getEntryLoggerAllocator().getPreallocationFuture());
+        
assertNotNull(entryLogger.getEntryLoggerAllocator().getPreallocationFuture());
+        entryLogger.shutdown();
 
         // disable pre-allocation case
-        ServerConfiguration conf2 = 
TestBKConfiguration.newServerConfiguration();
-        conf2.setLedgerDirNames(new String[] {tmpDir.toString()});
-        conf2.setEntryLogFilePreAllocationEnabled(false);
-        Bookie bookie2 = new Bookie(conf2);
+        conf.setEntryLogFilePreAllocationEnabled(false);
         // create a logger
-        EntryLogger logger2 = ((InterleavedLedgerStorage) 
bookie2.ledgerStorage).entryLogger;
-        assertNull(logger2.getEntryLoggerAllocator().getPreallocationFuture());
+        entryLogger = new EntryLogger(conf, dirsMgr);
+        
assertNull(entryLogger.getEntryLoggerAllocator().getPreallocationFuture());
 
-        logger2.addEntry(2, generateEntry(1, 1).nioBuffer());
+        entryLogger.addEntry(2, generateEntry(1, 1).nioBuffer());
 
         // the Future<BufferedLogChannel> is null all the time
-        assertNull(logger2.getEntryLoggerAllocator().getPreallocationFuture());
-
+        
assertNull(entryLogger.getEntryLoggerAllocator().getPreallocationFuture());
     }
 
     /**
@@ -375,30 +397,18 @@ public void testPreAllocateLog() throws Exception {
      */
     @Test
     public void testGetEntryLogsSet() throws Exception {
-        File tmpDir = createTempDir("bkTest", ".dir");
-        File curDir = Bookie.getCurrentDirectory(tmpDir);
-        Bookie.checkDirectoryStructure(curDir);
-
-        int gcWaitTime = 1000;
-        ServerConfiguration conf = 
TestBKConfiguration.newServerConfiguration();
-        conf.setGcWaitTime(gcWaitTime);
-        conf.setLedgerDirNames(new String[] { tmpDir.toString() });
-        Bookie bookie = new Bookie(conf);
-
         // create some entries
-        EntryLogger logger = ((InterleavedLedgerStorage) 
bookie.ledgerStorage).entryLogger;
-
-        assertEquals(Sets.newHashSet(0L, 1L), logger.getEntryLogsSet());
+        assertEquals(Sets.newHashSet(0L, 1L), entryLogger.getEntryLogsSet());
 
-        logger.rollLog();
-        logger.flushRotatedLogs();
+        entryLogger.rollLog();
+        entryLogger.flushRotatedLogs();
 
-        assertEquals(Sets.newHashSet(0L, 1L, 2L), logger.getEntryLogsSet());
+        assertEquals(Sets.newHashSet(0L, 1L, 2L), 
entryLogger.getEntryLogsSet());
 
-        logger.rollLog();
-        logger.flushRotatedLogs();
+        entryLogger.rollLog();
+        entryLogger.flushRotatedLogs();
 
-        assertEquals(Sets.newHashSet(0L, 1L, 2L, 3L), 
logger.getEntryLogsSet());
+        assertEquals(Sets.newHashSet(0L, 1L, 2L, 3L), 
entryLogger.getEntryLogsSet());
     }
 
     static class LedgerStorageWriteTask implements Callable<Boolean> {
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/SingleBookieInitializationTest.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/SingleBookieInitializationTest.java
new file mode 100644
index 000000000..fe147cbbb
--- /dev/null
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/SingleBookieInitializationTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bookkeeper.bookie;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import org.apache.bookkeeper.bookie.BookieException.Code;
+import 
org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.conf.TestBKConfiguration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Test a single bookie at readonly mode.
+ */
+public class SingleBookieInitializationTest {
+
+    @Rule
+    public final TemporaryFolder testDir = new TemporaryFolder();
+
+    private File journalDir;
+    private File ledgerDir;
+    private ServerConfiguration conf;
+    private Bookie bookie;
+
+    @Before
+    public void setUp() throws Exception {
+        this.journalDir = testDir.newFolder("journal");
+        this.ledgerDir = testDir.newFolder("ledgers");
+
+        this.conf = TestBKConfiguration.newServerConfiguration();
+        this.conf.setJournalDirsName(new String[] { 
journalDir.getAbsolutePath() });
+        this.conf.setLedgerDirNames(new String[] { ledgerDir.getAbsolutePath() 
});
+        this.conf.setMetadataServiceUri(null);
+        this.conf.setZkServers(null);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (null != this.bookie) {
+            this.bookie.shutdown();
+        }
+    }
+
+    private static String generateDataString(long ledger, long entry) {
+        return ("ledger-" + ledger + "-" + entry);
+    }
+
+    private static ByteBuf generateEntry(long ledger, long entry) {
+        byte[] data = generateDataString(ledger, entry).getBytes();
+        ByteBuf bb = Unpooled.buffer(8 + 8 + data.length);
+        bb.writeLong(ledger);
+        bb.writeLong(entry);
+        bb.writeBytes(data);
+        return bb;
+    }
+
+    @Test
+    public void testInitBookieNoWritableDirsButHasEnoughSpaces() throws 
Exception {
+        float usage = 1.0f - ((float) ledgerDir.getUsableSpace()) / 
ledgerDir.getTotalSpace();
+        conf.setDiskUsageThreshold(usage / 2);
+        conf.setDiskUsageWarnThreshold(usage / 3);
+        conf.setMinUsableSizeForEntryLogCreation(Long.MIN_VALUE);
+        conf.setLedgerStorageClass(InterleavedLedgerStorage.class.getName());
+
+        bookie = new Bookie(conf);
+        bookie.start();
+
+        CompletableFuture<Integer> writeFuture = new CompletableFuture<>();
+        bookie.addEntry(
+            generateEntry(1L, 2L),
+            false,
+            (rc, ledgerId, entryId, addr, ctx) -> writeFuture.complete(rc),
+            null,
+            new byte[0]
+        );
+        assertEquals(Code.OK, writeFuture.get().intValue());
+    }
+
+    @Test
+    public void testInitBookieNoWritableDirsAndNoEnoughSpaces() throws 
Exception {
+        float usage = 1.0f - ((float) ledgerDir.getUsableSpace()) / 
ledgerDir.getTotalSpace();
+        conf.setDiskUsageThreshold(usage / 2);
+        conf.setDiskUsageWarnThreshold(usage / 3);
+        conf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
+        conf.setLedgerStorageClass(InterleavedLedgerStorage.class.getName());
+
+        bookie = new Bookie(conf);
+        bookie.start();
+
+        try {
+            bookie.addEntry(
+                generateEntry(1L, 2L),
+                false,
+                (rc, ledgerId, entryId, addr, ctx) -> {},
+                null,
+                new byte[0]
+            );
+            fail("Should fail on creating new entry log file"
+                + " since there is no enough disk space to accommodate 
writes");
+        } catch (IOException ioe) {
+            // expected
+            assertTrue(ioe.getCause() instanceof NoWritableLedgerDirException);
+        }
+    }
+
+}
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/TestLedgerDirsManager.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/TestLedgerDirsManager.java
index 1731b9b54..b8555ca99 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/TestLedgerDirsManager.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/TestLedgerDirsManager.java
@@ -100,6 +100,7 @@ public void setUp() throws Exception {
         conf.setDiskLowWaterMarkUsageThreshold(conf.getDiskUsageThreshold());
         conf.setDiskCheckInterval(diskCheckInterval);
         conf.setIsForceGCAllowWhenNoSpace(true);
+        conf.setMinUsableSizeForEntryLogCreation(Long.MIN_VALUE);
 
         executor = PowerMockito.mock(ScheduledExecutorService.class);
         executorController = new MockExecutorController()
@@ -165,7 +166,7 @@ public void testGetWritableDirForLog() throws Exception {
         List<File> writeDirs;
         try {
             dirsManager.addToFilledDirs(curDir);
-            writeDirs = dirsManager.getWritableLedgerDirs();
+            dirsManager.getWritableLedgerDirs();
             fail("Should not reach here due to there is no writable ledger 
dir.");
         } catch (NoWritableLedgerDirException nwlde) {
             // expected to fail with no writable ledger dir
@@ -179,6 +180,27 @@ public void testGetWritableDirForLog() throws Exception {
         }
     }
 
+    @Test
+    public void testGetWritableDirForLogNoEnoughDiskSpace() throws Exception {
+        conf.setMinUsableSizeForEntryLogCreation(curDir.getUsableSpace() + 
1024);
+        dirsManager = new LedgerDirsManager(conf, conf.getLedgerDirs(),
+            new DiskChecker(conf.getDiskUsageThreshold(), 
conf.getDiskUsageWarnThreshold()), statsLogger);
+        try {
+            dirsManager.addToFilledDirs(curDir);
+            dirsManager.getWritableLedgerDirs();
+            fail("Should not reach here due to there is no writable ledger 
dir.");
+        } catch (NoWritableLedgerDirException nwlde) {
+            // expected to fail with no writable ledger dir
+            // Now make sure we can get one for log
+            try {
+                dirsManager.getWritableLedgerDirsForNewLog();
+                fail("Should not reach here due to there is no enough disk 
space left");
+            } catch (NoWritableLedgerDirException e) {
+                // expected.
+            }
+        }
+    }
+
     @Test
     public void testLedgerDirsMonitorDuringTransition() throws Exception {
         testLedgerDirsMonitorDuringTransition(true);
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ReadOnlyBookieTest.java
 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ReadOnlyBookieTest.java
index a34bbcb5d..75aaa933f 100644
--- 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ReadOnlyBookieTest.java
+++ 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ReadOnlyBookieTest.java
@@ -46,6 +46,7 @@ public ReadOnlyBookieTest() {
         super(2);
         
baseConf.setLedgerStorageClass(InterleavedLedgerStorage.class.getName());
         baseConf.setEntryLogFilePreAllocationEnabled(false);
+        baseConf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
     }
 
     /**


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to