You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by iv...@apache.org on 2015/01/12 15:19:19 UTC
bookkeeper git commit: BOOKKEEPER-832: Allow starting bookie in
ReadOnly mode (zhaijia via ivank)
Repository: bookkeeper
Updated Branches:
refs/heads/master 264995cf2 -> 4050e7965
BOOKKEEPER-832: Allow starting bookie in ReadOnly mode (zhaijia via ivank)
Project: http://git-wip-us.apache.org/repos/asf/bookkeeper/repo
Commit: http://git-wip-us.apache.org/repos/asf/bookkeeper/commit/4050e796
Tree: http://git-wip-us.apache.org/repos/asf/bookkeeper/tree/4050e796
Diff: http://git-wip-us.apache.org/repos/asf/bookkeeper/diff/4050e796
Branch: refs/heads/master
Commit: 4050e79654704ebc0224cc024980e297fdc56473
Parents: 264995c
Author: Ivan Kelly <iv...@apache.org>
Authored: Mon Jan 12 15:18:55 2015 +0100
Committer: Ivan Kelly <iv...@apache.org>
Committed: Mon Jan 12 15:18:55 2015 +0100
----------------------------------------------------------------------
CHANGES.txt | 2 +
.../org/apache/bookkeeper/bookie/Bookie.java | 10 +-
.../bookkeeper/bookie/ReadOnlyBookie.java | 120 +++++++++++++++++++
.../bookkeeper/conf/ServerConfiguration.java | 25 ++++
.../apache/bookkeeper/proto/BookieServer.java | 11 +-
.../test/BookKeeperClusterTestCase.java | 8 +-
.../test/ForceReadOnlyBookieTest.java | 95 +++++++++++++++
7 files changed, 263 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a3eca3e..ed31203 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -42,6 +42,8 @@ Trunk (unreleased changes)
BOOKKEEPER-803: Guide for making a replicated log out of ledgers (ivank)
+ BOOKKEEPER-832: Allow starting bookie in ReadOnly mode (zhaijia via ivank)
+
bookkeeper-client:
BOOKKEEPER-810: Allow to configure TCP connect timeout (Charles Xie via sijie)
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
----------------------------------------------------------------------
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
index 54a3c9f..24dd466 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
@@ -106,7 +106,7 @@ public class Bookie extends BookieCriticalThread {
static final long METAENTRY_ID_FENCE_KEY = -0x2000;
// ZK registration path for this bookie
- private final String bookieRegistrationPath;
+ protected final String bookieRegistrationPath;
private final LedgerDirsManager ledgerDirsManager;
private LedgerDirsManager indexDirsManager;
@@ -127,10 +127,10 @@ public class Bookie extends BookieCriticalThread {
final ConcurrentMap<Long, byte[]> masterKeyCache = new ConcurrentHashMap<Long, byte[]>();
- final private String zkBookieRegPath;
- final private String zkBookieReadOnlyPath;
+ final protected String zkBookieRegPath;
+ final protected String zkBookieReadOnlyPath;
- final private AtomicBoolean readOnly = new AtomicBoolean(false);
+ final protected AtomicBoolean readOnly = new AtomicBoolean(false);
// Expose Stats
private final Counter writeBytes;
@@ -795,7 +795,7 @@ public class Bookie extends BookieCriticalThread {
// exit here as this is a fatal error.
throw new IOException(ke);
} catch (InterruptedException ie) {
- LOG.error("ZK exception registering ephemeral Znode for Bookie!",
+ LOG.error("Interrupted exception registering ephemeral Znode for Bookie!",
ie);
// Throw an IOException back up. This will cause the Bookie
// constructor to error out. Alternatively, we could do a System
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ReadOnlyBookie.java
----------------------------------------------------------------------
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ReadOnlyBookie.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ReadOnlyBookie.java
new file mode 100644
index 0000000..d354fb3
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ReadOnlyBookie.java
@@ -0,0 +1,120 @@
+/**
+ *
+ * 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 java.io.IOException;
+
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.stats.StatsLogger;
+import org.apache.bookkeeper.util.BookKeeperConstants;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.NodeExistsException;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Implements a read only bookie.
+ *
+ * ReadOnlyBookie is force started as readonly, and will not change to writable.
+ *
+ */
+public class ReadOnlyBookie extends Bookie {
+
+ private final static Logger LOG = LoggerFactory.getLogger(ReadOnlyBookie.class);
+
+ public ReadOnlyBookie(ServerConfiguration conf, StatsLogger statsLogger)
+ throws IOException, KeeperException, InterruptedException, BookieException {
+ super(conf, statsLogger);
+ if (conf.isReadOnlyModeEnabled()) {
+ readOnly.set(true);
+ } else {
+ String err = "Try to init ReadOnly Bookie, while ReadOnly mode is not enabled";
+ LOG.error(err);
+ throw new IOException(err);
+ }
+ LOG.info("successed call ReadOnlyBookie constructor");
+ }
+
+ /**
+ * Register as a read only bookie
+ */
+ @Override
+ protected void registerBookie(ServerConfiguration conf) throws IOException {
+ if (null == zk) {
+ // zookeeper instance is null, means not register itself to zk
+ return;
+ }
+
+ // ZK node for this ReadOnly Bookie.
+ try{
+ if (null == zk.exists(this.bookieRegistrationPath
+ + BookKeeperConstants.READONLY, false)) {
+ try {
+ zk.create(this.bookieRegistrationPath
+ + BookKeeperConstants.READONLY + "/", new byte[0],
+ Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ LOG.debug("successed create ReadOnlyBookie parent zk node");
+ } catch (NodeExistsException e) {
+ // this node is just now created by someone.
+ }
+ }
+
+ if (!checkRegNodeAndWaitExpired(zkBookieReadOnlyPath)) {
+ // Create the ZK node for this RO Bookie.
+ zk.create(zkBookieReadOnlyPath, new byte[0], Ids.OPEN_ACL_UNSAFE,
+ CreateMode.EPHEMERAL);
+ LOG.debug("successed create ReadOnlyBookie zk node");
+ }
+ } catch (KeeperException ke) {
+ LOG.error("ZK exception registering Znode for ReadOnly Bookie!", ke);
+ // Throw an IOException back up. This will cause the Bookie
+ // constructor to error out. Alternatively, we could do a System
+ // exit here as this is a fatal error.
+ throw new IOException(ke);
+ } catch (InterruptedException ie) {
+ LOG.error("Interruptted exception registering Znode for ReadOnly Bookie!",
+ ie);
+ // Throw an IOException back up. This will cause the Bookie
+ // constructor to error out. Alternatively, we could do a System
+ // exit here as this is a fatal error.
+ throw new IOException(ie);
+ }
+ }
+
+ @VisibleForTesting
+ @Override
+ public void transitionToWritableMode() {
+ LOG.info("Skip transition to writable mode for readonly bookie");
+ }
+
+
+ @VisibleForTesting
+ @Override
+ public void transitionToReadOnlyMode() {
+ LOG.warn("Skip transition to readonly mode for readonly bookie");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
----------------------------------------------------------------------
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 03b3be4..a06e770 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
@@ -82,6 +82,8 @@ public class ServerConfiguration extends AbstractConfiguration {
protected final static String OPEN_LEDGER_REREPLICATION_GRACE_PERIOD = "openLedgerRereplicationGracePeriod";
//ReadOnly mode support on all disk full
protected final static String READ_ONLY_MODE_ENABLED = "readOnlyModeEnabled";
+ //Whether the bookie is force started in ReadOnly mode
+ protected final static String FORCE_READ_ONLY_BOOKIE = "forceReadOnlyBookie";
//Disk utilization
protected final static String DISK_USAGE_THRESHOLD = "diskUsageThreshold";
protected final static String DISK_USAGE_WARN_THRESHOLD = "diskUsageWarnThreshold";
@@ -1218,6 +1220,29 @@ public class ServerConfiguration extends AbstractConfiguration {
}
/**
+ * Sets that whether force start a bookie in readonly mode
+ *
+ * @param enabled
+ * - true if need to start a bookie in read only mode. Otherwise
+ * false.
+ * @return ServerConfiguration
+ */
+ public ServerConfiguration setForceReadOnlyBookie(boolean enabled) {
+ setProperty(FORCE_READ_ONLY_BOOKIE, enabled);
+ return this;
+ }
+
+ /**
+ * Get whether the Bookie is force started in read only mode or not
+ *
+ * @return true - if need to start a bookie in read only mode. Otherwise
+ * false.
+ */
+ public boolean isForceReadOnlyBookie() {
+ return getBoolean(FORCE_READ_ONLY_BOOKIE, false);
+ }
+
+ /**
* Get the maximum number of entries which can be compacted without flushing.
* Default is 100,000.
*
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
----------------------------------------------------------------------
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
index bf5f438..8e8349a 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieServer.java
@@ -26,6 +26,7 @@ import java.net.MalformedURLException;
import java.net.UnknownHostException;
import org.apache.bookkeeper.bookie.Bookie;
+import org.apache.bookkeeper.bookie.ReadOnlyBookie;
import org.apache.bookkeeper.bookie.BookieCriticalThread;
import org.apache.bookkeeper.bookie.BookieException;
import org.apache.bookkeeper.bookie.ExitCode;
@@ -104,7 +105,9 @@ public class BookieServer {
protected Bookie newBookie(ServerConfiguration conf)
throws IOException, KeeperException, InterruptedException, BookieException {
- return new Bookie(conf, statsLogger.scope(BOOKIE_SCOPE));
+ return conf.isForceReadOnlyBookie() ?
+ new ReadOnlyBookie(conf, statsLogger.scope(BOOKIE_SCOPE)) :
+ new Bookie(conf, statsLogger.scope(BOOKIE_SCOPE));
}
public void start() throws IOException, UnavailableException {
@@ -265,6 +268,8 @@ public class BookieServer {
bkOpts.addOption("c", "conf", true, "Configuration for Bookie Server");
bkOpts.addOption("withAutoRecovery", false,
"Start Autorecovery service Bookie server");
+ bkOpts.addOption("readOnly", false,
+ "Force Start a ReadOnly Bookie server");
bkOpts.addOption("h", "help", false, "Print help message");
}
@@ -318,6 +323,10 @@ public class BookieServer {
conf.setAutoRecoveryDaemonEnabled(true);
}
+ if (cmdLine.hasOption("readOnly")) {
+ conf.setForceReadOnlyBookie(true);
+ }
+
if (leftArgs.length < 4) {
throw new IllegalArgumentException();
}
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/BookKeeperClusterTestCase.java
----------------------------------------------------------------------
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/BookKeeperClusterTestCase.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/BookKeeperClusterTestCase.java
index fbb79b0..0223a66 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/BookKeeperClusterTestCase.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/BookKeeperClusterTestCase.java
@@ -413,8 +413,12 @@ public abstract class BookKeeperClusterTestCase {
if (conf.getUseHostNameAsBookieID()) {
host = InetAddress.getLocalHost().getCanonicalHostName();
}
- while (bkc.getZkHandle().exists(
- "/ledgers/available/" + host + ":" + port, false) == null) {
+
+ while ( (!conf.isForceReadOnlyBookie() && (bkc.getZkHandle().exists(
+ "/ledgers/available/" + host + ":" + port, false) == null)) ||
+ ( conf.isForceReadOnlyBookie() && ((bkc.getZkHandle().exists(
+ "/ledgers/available/readonly/" + host + ":" + port, false) == null)))
+ ) {
Thread.sleep(500);
}
http://git-wip-us.apache.org/repos/asf/bookkeeper/blob/4050e796/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ForceReadOnlyBookieTest.java
----------------------------------------------------------------------
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ForceReadOnlyBookieTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ForceReadOnlyBookieTest.java
new file mode 100644
index 0000000..914c7e2
--- /dev/null
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/test/ForceReadOnlyBookieTest.java
@@ -0,0 +1,95 @@
+/**
+ *
+ * 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.test;
+
+import java.io.File;
+import java.util.Enumeration;
+
+import org.apache.bookkeeper.bookie.Bookie;
+import org.apache.bookkeeper.bookie.LedgerDirsManager;
+import org.apache.bookkeeper.client.BookKeeper.DigestType;
+import org.apache.bookkeeper.client.LedgerEntry;
+import org.apache.bookkeeper.client.LedgerHandle;
+import org.junit.Test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import static org.junit.Assert.*;
+
+/**
+ * Test to verify force start readonly bookie
+ */
+public class ForceReadOnlyBookieTest extends BookKeeperClusterTestCase {
+
+ private final static Logger LOG = LoggerFactory.getLogger(ForceReadOnlyBookieTest.class);
+ public ForceReadOnlyBookieTest() {
+ super(2);
+ baseConf.setSortedLedgerStorageEnabled(false);
+ baseConf.setEntryLogFilePreAllocationEnabled(false);
+ }
+
+ /**
+ * Check force start readonly bookie
+ */
+ @Test(timeout = 60000)
+ public void testBookieForceStartAsReadOnly() throws Exception {
+ // create ledger, add entries
+ LedgerHandle ledger = bkc.createLedger(2, 2, DigestType.MAC,
+ "".getBytes());
+ for (int i = 0; i < 10; i++) {
+ ledger.addEntry("data".getBytes());
+ }
+ ledger.close();
+ LOG.info("successed prepare");
+
+ // start bookie 1 as readonly
+ bsConfs.get(1).setReadOnlyModeEnabled(true);
+ bsConfs.get(1).setForceReadOnlyBookie(true);
+ restartBookies();
+ Bookie bookie = bs.get(1).getBookie();
+
+ assertTrue("Bookie should be running and in readonly mode",
+ bookie.isRunning() && bookie.isReadOnly());
+ LOG.info("successed force start ReadOnlyBookie");
+
+ // Check new bookie with readonly mode enabled.
+ File[] ledgerDirs = bsConfs.get(1).getLedgerDirs();
+ assertEquals("Only one ledger dir should be present", 1, ledgerDirs.length);
+
+ // kill the writable bookie
+ killBookie(0);
+ // read entry from read only bookie
+ Enumeration<LedgerEntry> readEntries = ledger.readEntries(0, 9);
+ while (readEntries.hasMoreElements()) {
+ LedgerEntry entry = readEntries.nextElement();
+ assertEquals("Entry should contain correct data", "data",
+ new String(entry.getEntry()));
+ }
+ LOG.info("successed read entry from ReadOnlyBookie");
+
+ // test will not transfer to Writable mode.
+ LedgerDirsManager ledgerDirsManager = bookie.getLedgerDirsManager();
+ ledgerDirsManager.addToWritableDirs(new File(ledgerDirs[0], "current"), true);
+ assertTrue("Bookie should be running and in readonly mode",
+ bookie.isRunning() && bookie.isReadOnly());
+ LOG.info("successed: bookie still readonly");
+ }
+}