You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by si...@apache.org on 2018/04/06 23:39:36 UTC
[bookkeeper] branch master updated: ISSUE #1316: A bookie with
non-writable dirs should be able to start in readonly mode
This is an automated email from the ASF dual-hosted git repository.
sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git
The following commit(s) were added to refs/heads/master by this push:
new ea1a75c ISSUE #1316: A bookie with non-writable dirs should be able to start in readonly mode
ea1a75c is described below
commit ea1a75c0622a93abbd16fd71820f3c0cb5fc3ab0
Author: Sijie Guo <si...@apache.org>
AuthorDate: Fri Apr 6 16:39:28 2018 -0700
ISSUE #1316: A bookie with non-writable dirs should be able to start in readonly mode
Descriptions of the changes in this PR:
*Problem*
Bookie failed to start when it doesn't have non-writable dirs. Issue is reported at #1316
*Solution*
- Introduce a setting `minUsageSizeForEntryLogCreation` to allow creating entry logs even when there is no writable dirs. This setting is replacing `getIsForceGCAllowWhenNoSpace` for creating new log, since entry log files are created not only for gc/compaction. It can also happen on startup and also journal replays.
- Defer creating entry log files until first write happens, when there is no writable ledger dirs.
- add bunch of tests for EntryLog and Bookie initialization.
Master Issue: #1316
Related Issue: #190
Author: Sijie Guo <si...@apache.org>
Reviewers: Charan Reddy Guttapalem <re...@gmail.com>
This closes #1319 from sijie/allow_bookie_to_start_with_no_writable_bookies, closes #1316
---
.../org/apache/bookkeeper/bookie/EntryLogger.java | 25 ++-
.../bookkeeper/bookie/LedgerDirsManager.java | 23 +-
.../bookkeeper/conf/ServerConfiguration.java | 41 +++-
.../bookie/BookieInitializationTest.java | 20 +-
.../org/apache/bookkeeper/bookie/EntryLogTest.java | 244 +++++++++++----------
.../bookie/SingleBookieInitializationTest.java | 134 +++++++++++
.../bookkeeper/bookie/TestLedgerDirsManager.java | 24 +-
.../apache/bookkeeper/test/ReadOnlyBookieTest.java | 1 +
8 files changed, 363 insertions(+), 149 deletions(-)
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 66efbaa..99b0f49 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 com.google.common.base.Charsets.UTF_8;
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 @@ import org.slf4j.LoggerFactory;
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 class EntryLogger {
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 @@ public class EntryLogger {
}
synchronized long getCurrentLogId() {
- return logChannel.getLogId();
+ BufferedLogChannel channel = logChannel;
+ if (null == channel) {
+ return UNINITIALIZED_LOG_ID;
+ } else {
+ return channel.getLogId();
+ }
}
/**
@@ -416,8 +425,10 @@ public class EntryLogger {
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 @@ public class EntryLogger {
};
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 5d2c11f..a615cfa 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 @@ public class LedgerDirsManager {
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 class LedgerDirsManager {
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 class LedgerDirsManager {
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 class LedgerDirsManager {
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 cb39821..904ca3f 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 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
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 class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
* 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,8 +2522,12 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
* 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);
@@ -2529,14 +2535,39 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
}
/**
+ * 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 96790ac..714a1c7 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 class BookieInitializationTest extends BookKeeperClusterTestCase {
String ledgersPath = "/" + "ledgers" + runtime.getMethodName();
baseClientConf.setZkLedgersRootPath(ledgersPath);
baseConf.setZkLedgersRootPath(ledgersPath);
+ baseConf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
}
@Override
@@ -556,6 +557,7 @@ public class BookieInitializationTest extends BookKeeperClusterTestCase {
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 class BookieInitializationTest extends BookKeeperClusterTestCase {
// 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 class BookieInitializationTest extends BookKeeperClusterTestCase {
// expected
}
- conf.setIsForceGCAllowWhenNoSpace(true)
+ conf.setMinUsableSizeForEntryLogCreation(Long.MIN_VALUE)
.setReadOnlyModeEnabled(false);
try {
new Bookie(conf);
@@ -585,13 +587,19 @@ public class BookieInitializationTest extends BookKeeperClusterTestCase {
// 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 cc02e4e..b7f286c 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.Callable;
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.bookkeeper.util.IOUtils;
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 @@ public class EntryLogTest {
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 class EntryLogTest {
}
@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 @@ public class EntryLogTest {
@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 class EntryLogTest {
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 class EntryLogTest {
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 class EntryLogTest {
}
}
- @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 class EntryLogTest {
}
/**
- * 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 class EntryLogTest {
}
/**
- * 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 class EntryLogTest {
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 class EntryLogTest {
*/
@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 class EntryLogTest {
*/
@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 0000000..fe147cb
--- /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 1731b9b..b8555ca 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 class TestLedgerDirsManager {
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 class TestLedgerDirsManager {
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
@@ -180,6 +181,27 @@ public class TestLedgerDirsManager {
}
@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 a34bbcb..75aaa93 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 class ReadOnlyBookieTest extends BookKeeperClusterTestCase {
super(2);
baseConf.setLedgerStorageClass(InterleavedLedgerStorage.class.getName());
baseConf.setEntryLogFilePreAllocationEnabled(false);
+ baseConf.setMinUsableSizeForEntryLogCreation(Long.MAX_VALUE);
}
/**
--
To stop receiving notification emails like this one, please contact
sijie@apache.org.