diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorReloadTask.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorReloadTask.java
new file mode 100644
index 000000000000..0b826de333d6
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorReloadTask.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+
+/**
+ * This interface is used to perform whatever task is necessary for reloading coprocessors on
+ * HMaster, HRegionServer, and HRegion. Since the steps required to reload coprocessors varies for
+ * each of these types, this interface helps with code flexibility by allowing a lamda function to
+ * be provided for the {@link #reload(Configuration) reload()} method.
+ *
+ * See {@link org.apache.hadoop.hbase.util.CoprocessorConfigurationUtil#maybeUpdateCoprocessors
+ * CoprocessorConfigurationUtil.maybeUpdateCoprocessors()} and its usage in
+ * {@link org.apache.hadoop.hbase.conf.ConfigurationObserver#onConfigurationChange
+ * onConfigurationChange()} with HMaster, HRegionServer, and HRegion for an idea of how this
+ * interface is helpful.
+ */
+@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
+@InterfaceStability.Evolving
+@FunctionalInterface
+public interface CoprocessorReloadTask {
+ void reload(Configuration conf);
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
index 17355df9af1e..8fd133f64f68 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
@@ -18,6 +18,8 @@
package org.apache.hadoop.hbase.master;
import static org.apache.hadoop.hbase.HConstants.DEFAULT_HBASE_SPLIT_COORDINATED_BY_ZK;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_DEFAULT;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY;
import static org.apache.hadoop.hbase.HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS;
import static org.apache.hadoop.hbase.HConstants.HBASE_SPLIT_WAL_COORDINATED_BY_ZK;
import static org.apache.hadoop.hbase.master.cleaner.HFileCleaner.CUSTOM_POOL_SIZE;
@@ -498,6 +500,8 @@ public class HMaster extends HBaseServerBase implements Maste
public static final String WARMUP_BEFORE_MOVE = "hbase.master.warmup.before.move";
private static final boolean DEFAULT_WARMUP_BEFORE_MOVE = true;
+ private volatile boolean isGlobalReadOnlyEnabled;
+
/**
* Use RSProcedureDispatcher instance to initiate master -> rs remote procedure execution. Use
* this config to extend RSProcedureDispatcher (mainly for testing purpose).
@@ -583,6 +587,8 @@ public HMaster(final Configuration conf) throws IOException {
getChoreService().scheduleChore(clusterStatusPublisherChore);
}
}
+ this.isGlobalReadOnlyEnabled =
+ conf.getBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, HBASE_GLOBAL_READONLY_ENABLED_DEFAULT);
this.activeMasterManager = createActiveMasterManager(zooKeeper, serverName, this);
cachedClusterId = new CachedClusterId(this, conf);
this.regionServerTracker = new RegionServerTracker(zooKeeper, this);
@@ -1090,8 +1096,7 @@ private void finishActiveMasterInitialization() throws IOException, InterruptedE
if (!maintenanceMode) {
startupTaskGroup.addTask("Initializing master coprocessors");
setQuotasObserver(conf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(
- ConfigurationUtil.isReadOnlyModeEnabled(conf), conf,
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf,
CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY);
AbstractReadOnlyController.manageActiveClusterIdFile(
ConfigurationUtil.isReadOnlyModeEnabled(conf), this.getMasterFileSystem());
@@ -4501,21 +4506,16 @@ public void onConfigurationChange(Configuration newConf) {
// append the quotas observer back to the master coprocessor key
setQuotasObserver(newConf);
- boolean readOnlyMode = ConfigurationUtil.isReadOnlyModeEnabled(newConf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, newConf,
- CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY);
+ boolean originalIsReadOnlyEnabled = this.isGlobalReadOnlyEnabled;
- // update region server coprocessor if the configuration has changed.
- if (
- CoprocessorConfigurationUtil.checkConfigurationChange(this.cpHost, newConf,
- CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY) && !maintenanceMode
- ) {
- LOG.info("Update the master coprocessor(s) because the configuration has changed");
- initializeCoprocessorHost(newConf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, this.conf,
- CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY);
- AbstractReadOnlyController.manageActiveClusterIdFile(
- ConfigurationUtil.isReadOnlyModeEnabled(newConf), this.getMasterFileSystem());
+ CoprocessorConfigurationUtil.maybeUpdateCoprocessors(newConf, this.isGlobalReadOnlyEnabled,
+ this.cpHost, CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, this.maintenanceMode,
+ this.toString(), val -> this.isGlobalReadOnlyEnabled = val,
+ conf -> initializeCoprocessorHost(newConf));
+
+ if (this.isGlobalReadOnlyEnabled != originalIsReadOnlyEnabled) {
+ AbstractReadOnlyController.manageActiveClusterIdFile(this.isGlobalReadOnlyEnabled,
+ this.getMasterFileSystem());
}
}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
index 13eb59793595..5d1246645a4f 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
@@ -17,6 +17,8 @@
*/
package org.apache.hadoop.hbase.regionserver;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_DEFAULT;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY;
import static org.apache.hadoop.hbase.HConstants.REPLICATION_SCOPE_LOCAL;
import static org.apache.hadoop.hbase.regionserver.HStoreFile.MAJOR_COMPACTION_KEY;
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.REGION_NAMES_KEY;
@@ -178,7 +180,6 @@
import org.apache.hadoop.hbase.util.CancelableProgressable;
import org.apache.hadoop.hbase.util.ClassSize;
import org.apache.hadoop.hbase.util.CommonFSUtils;
-import org.apache.hadoop.hbase.util.ConfigurationUtil;
import org.apache.hadoop.hbase.util.CoprocessorConfigurationUtil;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.FSUtils;
@@ -391,6 +392,8 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
private Path regionWalDir;
private FileSystem walFS;
+ private volatile boolean isGlobalReadOnlyEnabled;
+
// set to true if the region is restored from snapshot for reading by ClientSideRegionScanner
private boolean isRestoredRegion = false;
@@ -941,8 +944,9 @@ public HRegion(final HRegionFileSystem fs, final WAL wal, final Configuration co
decorateRegionConfiguration(conf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(
- ConfigurationUtil.isReadOnlyModeEnabled(conf), this.conf,
+ this.isGlobalReadOnlyEnabled =
+ conf.getBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, HBASE_GLOBAL_READONLY_ENABLED_DEFAULT);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(this.conf,
CoprocessorHost.REGION_COPROCESSOR_CONF_KEY);
if (rsServices != null) {
@@ -8515,7 +8519,7 @@ public boolean registerService(Service instance) {
ServiceDescriptor serviceDesc = instance.getDescriptorForType();
String serviceName = CoprocessorRpcUtils.getServiceName(serviceDesc);
if (coprocessorServiceHandlers.containsKey(serviceName)) {
- LOG.error("Coprocessor service {} already registered, rejecting request from {} in region {}",
+ LOG.warn("Coprocessor service {} already registered, rejecting request from {} in region {}",
serviceName, instance, this);
return false;
}
@@ -8986,25 +8990,15 @@ IOException throwOnInterrupt(Throwable t) {
* {@inheritDoc}
*/
@Override
- public void onConfigurationChange(Configuration conf) {
- this.storeHotnessProtector.update(conf);
-
- boolean readOnlyMode = ConfigurationUtil.isReadOnlyModeEnabled(conf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, conf,
- CoprocessorHost.REGION_COPROCESSOR_CONF_KEY);
-
- // update coprocessorHost if the configuration has changed.
- if (
- CoprocessorConfigurationUtil.checkConfigurationChange(this.coprocessorHost, conf,
- CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
- CoprocessorHost.USER_REGION_COPROCESSOR_CONF_KEY)
- ) {
- LOG.info("Update the system coprocessors because the configuration has changed");
- decorateRegionConfiguration(conf);
- this.coprocessorHost = new RegionCoprocessorHost(this, rsServices, conf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, this.conf,
- CoprocessorHost.REGION_COPROCESSOR_CONF_KEY);
- }
+ public void onConfigurationChange(Configuration newConf) {
+ this.storeHotnessProtector.update(newConf);
+
+ CoprocessorConfigurationUtil.maybeUpdateCoprocessors(newConf, this.isGlobalReadOnlyEnabled,
+ this.coprocessorHost, CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, false, this.toString(),
+ val -> this.isGlobalReadOnlyEnabled = val, conf -> {
+ decorateRegionConfiguration(conf);
+ this.coprocessorHost = new RegionCoprocessorHost(this, rsServices, newConf);
+ });
}
/**
@@ -9160,4 +9154,10 @@ public void addWriteRequestsCount(long writeRequestsCount) {
boolean isReadsEnabled() {
return this.writestate.readsEnabled;
}
+
+ @RestrictedApi(explanation = "Should only be called in tests", link = "",
+ allowedOnPath = ".*/src/test/.*")
+ public ConfigurationManager getConfigurationManager() {
+ return configurationManager;
+ }
}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
index 657cb01f38db..8cd6f738b339 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
@@ -20,6 +20,8 @@
import static org.apache.hadoop.hbase.HConstants.DEFAULT_HBASE_SPLIT_COORDINATED_BY_ZK;
import static org.apache.hadoop.hbase.HConstants.DEFAULT_HBASE_SPLIT_WAL_MAX_SPLITTER;
import static org.apache.hadoop.hbase.HConstants.DEFAULT_SLOW_LOG_SYS_TABLE_CHORE_DURATION;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_DEFAULT;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY;
import static org.apache.hadoop.hbase.HConstants.HBASE_SPLIT_WAL_COORDINATED_BY_ZK;
import static org.apache.hadoop.hbase.HConstants.HBASE_SPLIT_WAL_MAX_SPLITTER;
import static org.apache.hadoop.hbase.master.waleventtracker.WALEventTrackerTableCreator.WAL_EVENT_TRACKER_ENABLED_DEFAULT;
@@ -161,7 +163,6 @@
import org.apache.hadoop.hbase.trace.TraceUtil;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.CompressionTest;
-import org.apache.hadoop.hbase.util.ConfigurationUtil;
import org.apache.hadoop.hbase.util.CoprocessorConfigurationUtil;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.FSUtils;
@@ -318,6 +319,7 @@ public class HRegionServer extends HBaseServerBase
private LeaseManager leaseManager;
private volatile boolean dataFsOk;
+ private volatile boolean isGlobalReadOnlyEnabled;
static final String ABORT_TIMEOUT = "hbase.regionserver.abort.timeout";
// Default abort timeout is 1200 seconds for safe
@@ -546,6 +548,9 @@ public HRegionServer(final Configuration conf) throws IOException {
uncaughtExceptionHandler =
(t, e) -> abort("Uncaught exception in executorService thread " + t.getName(), e);
+ this.isGlobalReadOnlyEnabled =
+ conf.getBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, HBASE_GLOBAL_READONLY_ENABLED_DEFAULT);
+
// If no master in cluster, skip trying to track one or look for a cluster status.
if (!this.masterless) {
masterAddressTracker = new MasterAddressTracker(getZooKeeper(), this);
@@ -827,9 +832,9 @@ public void run() {
if (!isStopped() && !isAborted()) {
installShutdownHook();
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(
- ConfigurationUtil.isReadOnlyModeEnabled(conf), conf,
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf,
CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY);
+
// Initialize the RegionServerCoprocessorHost now that our ephemeral
// node was created, in case any coprocessors want to use ZooKeeper
this.rsHost = new RegionServerCoprocessorHost(this, this.conf);
@@ -3488,20 +3493,10 @@ public void onConfigurationChange(Configuration newConf) {
LOG.warn("Failed to initialize SuperUsers on reloading of the configuration");
}
- boolean readOnlyMode = ConfigurationUtil.isReadOnlyModeEnabled(newConf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, newConf,
- CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY);
-
- // update region server coprocessor if the configuration has changed.
- if (
- CoprocessorConfigurationUtil.checkConfigurationChange(this.rsHost, newConf,
- CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY)
- ) {
- LOG.info("Update region server coprocessors because the configuration has changed");
- this.rsHost = new RegionServerCoprocessorHost(this, newConf);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(readOnlyMode, this.conf,
- CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY);
- }
+ CoprocessorConfigurationUtil.maybeUpdateCoprocessors(newConf, this.isGlobalReadOnlyEnabled,
+ this.rsHost, CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, false, this.toString(),
+ val -> this.isGlobalReadOnlyEnabled = val,
+ conf -> this.rsHost = new RegionServerCoprocessorHost(this, newConf));
}
@Override
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/util/CoprocessorConfigurationUtil.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/util/CoprocessorConfigurationUtil.java
index bbd1e6755d54..46eb69571057 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/util/CoprocessorConfigurationUtil.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/util/CoprocessorConfigurationUtil.java
@@ -22,15 +22,19 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorReloadTask;
import org.apache.hadoop.hbase.security.access.BulkLoadReadOnlyController;
import org.apache.hadoop.hbase.security.access.EndpointReadOnlyController;
import org.apache.hadoop.hbase.security.access.MasterReadOnlyController;
import org.apache.hadoop.hbase.security.access.RegionReadOnlyController;
import org.apache.hadoop.hbase.security.access.RegionServerReadOnlyController;
import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
import org.apache.hbase.thirdparty.com.google.common.base.Strings;
@@ -40,6 +44,7 @@
*/
@InterfaceAudience.Private
public final class CoprocessorConfigurationUtil {
+ private static final Logger LOG = LoggerFactory.getLogger(CoprocessorConfigurationUtil.class);
private CoprocessorConfigurationUtil() {
}
@@ -175,16 +180,65 @@ private static List getReadOnlyCoprocessors(String configurationKey) {
};
}
- public static void syncReadOnlyConfigurations(boolean readOnlyMode, Configuration conf,
- String configurationKey) {
- conf.setBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, readOnlyMode);
+ /**
+ * This method adds or removes relevant ReadOnlyController coprocessors to the provided
+ * configuration based on whether read-only mode is enabled.
+ * @param conf The up-to-date configuration used to determine how to handle
+ * coprocessors
+ * @param coprocessorConfKey The configuration key name
+ */
+ public static void syncReadOnlyConfigurations(Configuration conf, String coprocessorConfKey) {
+ boolean isReadOnlyModeEnabled = conf.getBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY,
+ HConstants.HBASE_GLOBAL_READONLY_ENABLED_DEFAULT);
- List cpList = getReadOnlyCoprocessors(configurationKey);
- // If readonly is true then add the coprocessor of master
- if (readOnlyMode) {
- CoprocessorConfigurationUtil.addCoprocessors(conf, configurationKey, cpList);
+ List cpList = getReadOnlyCoprocessors(coprocessorConfKey);
+ if (isReadOnlyModeEnabled) {
+ CoprocessorConfigurationUtil.addCoprocessors(conf, coprocessorConfKey, cpList);
} else {
- CoprocessorConfigurationUtil.removeCoprocessors(conf, configurationKey, cpList);
+ CoprocessorConfigurationUtil.removeCoprocessors(conf, coprocessorConfKey, cpList);
+ }
+ }
+
+ /**
+ * This method updates the coprocessors on the master, region server, or region if a change has
+ * been detected. Detected changes include changes in coprocessors or changes in read-only mode
+ * configuration. If a change is detected, then new coprocessors are loaded using the provided
+ * reload method. The new value for the read-only config variable is updated as well.
+ * @param newConf an updated configuration
+ * @param originalIsReadOnlyEnabled the original value for
+ * {@value HConstants#HBASE_GLOBAL_READONLY_ENABLED_KEY}
+ * @param coprocessorHost the coprocessor host for HMaster, HRegionServer, or HRegion
+ * @param coprocessorConfKey configuration key used for setting master, region server, or
+ * region coprocessors
+ * @param isMaintenanceMode whether maintenance mode is active (mainly for HMaster)
+ * @param instance string value of the instance calling this method (mainly helps
+ * with tracking region logging)
+ * @param stateSetter lambda function that sets the read-only instance variable with
+ * an updated value from the config
+ * @param reloadTask lambda function that reloads coprocessors on the master,
+ * region server, or region
+ */
+ public static void maybeUpdateCoprocessors(Configuration newConf,
+ boolean originalIsReadOnlyEnabled, CoprocessorHost, ?> coprocessorHost,
+ String coprocessorConfKey, boolean isMaintenanceMode, String instance,
+ Consumer stateSetter, CoprocessorReloadTask reloadTask) {
+
+ boolean maybeUpdatedReadOnlyMode = ConfigurationUtil.isReadOnlyModeEnabled(newConf);
+ boolean hasReadOnlyModeChanged = originalIsReadOnlyEnabled != maybeUpdatedReadOnlyMode;
+ boolean hasCoprocessorConfigChanged = CoprocessorConfigurationUtil
+ .checkConfigurationChange(coprocessorHost, newConf, coprocessorConfKey);
+
+ // update region server coprocessor if the configuration has changed.
+ if ((hasCoprocessorConfigChanged || hasReadOnlyModeChanged) && !isMaintenanceMode) {
+ LOG.info("Updating coprocessors for {} because the configuration has changed", instance);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(newConf, coprocessorConfKey);
+ reloadTask.reload(newConf);
+ }
+
+ if (hasReadOnlyModeChanged) {
+ stateSetter.accept(maybeUpdatedReadOnlyMode);
+ LOG.info("Config {} has been dynamically changed to {} for {}",
+ HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, maybeUpdatedReadOnlyMode, instance);
}
}
}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestReadOnlyControllerCoprocessorLoading.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestReadOnlyControllerCoprocessorLoading.java
index 4846e84bcd62..c9692fb751e5 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestReadOnlyControllerCoprocessorLoading.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestReadOnlyControllerCoprocessorLoading.java
@@ -107,8 +107,8 @@ private void createTable() throws Exception {
region = regions.get(0);
}
- private void setReadOnlyMode(boolean isReadOnlyEnabled) {
- // Create a new configuration to micic client server behavior
+ private Configuration setReadOnlyMode(boolean isReadOnlyEnabled) {
+ // Create a new configuration to mimic client server behavior
// otherwise the existing conf object is shared with the cluster
// and can cause side effects on other tests if not reset properly.
// This way we can ensure that only the coprocessor loading is tested
@@ -119,9 +119,10 @@ private void setReadOnlyMode(boolean isReadOnlyEnabled) {
newConf.setBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, isReadOnlyEnabled);
master.getConfigurationManager().notifyAllObservers(newConf);
regionServer.getConfigurationManager().notifyAllObservers(newConf);
+ return newConf;
}
- private void verifyMasterReadOnlyControllerLoading(boolean isReadOnlyEnabled) throws Exception {
+ private void verifyMasterReadOnlyControllerLoading(boolean isReadOnlyEnabled) {
MasterCoprocessorHost masterCPHost = master.getMasterCoprocessorHost();
if (isReadOnlyEnabled) {
assertNotNull(
@@ -136,8 +137,7 @@ private void verifyMasterReadOnlyControllerLoading(boolean isReadOnlyEnabled) th
}
}
- private void verifyRegionServerReadOnlyControllerLoading(boolean isReadOnlyEnabled)
- throws Exception {
+ private void verifyRegionServerReadOnlyControllerLoading(boolean isReadOnlyEnabled) {
RegionServerCoprocessorHost rsCPHost = regionServer.getRegionServerCoprocessorHost();
if (isReadOnlyEnabled) {
assertNotNull(
@@ -152,7 +152,7 @@ private void verifyRegionServerReadOnlyControllerLoading(boolean isReadOnlyEnabl
}
}
- private void verifyRegionReadOnlyControllerLoading(boolean isReadOnlyEnabled) throws Exception {
+ private void verifyRegionReadOnlyControllerLoading(boolean isReadOnlyEnabled) {
RegionCoprocessorHost regionCPHost = region.getCoprocessorHost();
if (isReadOnlyEnabled) {
@@ -220,8 +220,10 @@ public void testReadOnlyControllerLoadedWhenEnabledDynamically() throws Exceptio
public void testReadOnlyControllerUnloadedWhenDisabledDynamically() throws Exception {
setupMiniCluster(initialReadOnlyMode);
boolean isReadOnlyEnabled = false;
- setReadOnlyMode(isReadOnlyEnabled);
+ Configuration newConf = setReadOnlyMode(isReadOnlyEnabled);
createTable();
+ // The newly created table's region has a stale conf that needs to be updated
+ region.onConfigurationChange(newConf);
verifyMasterReadOnlyControllerLoading(isReadOnlyEnabled);
verifyRegionServerReadOnlyControllerLoading(isReadOnlyEnabled);
verifyRegionReadOnlyControllerLoading(isReadOnlyEnabled);
@@ -232,8 +234,10 @@ public void testReadOnlyControllerLoadUnloadedWhenMultipleReadOnlyToggle() throw
setupMiniCluster(initialReadOnlyMode);
// Ensure region exists before validation
- setReadOnlyMode(false);
+ Configuration newConf = setReadOnlyMode(false);
createTable();
+ // The newly created table's region has a stale conf that needs to be updated
+ region.onConfigurationChange(newConf);
verifyReadOnlyState(false);
// Define toggle sequence
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/util/TestCoprocessorConfigurationUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/util/TestCoprocessorConfigurationUtil.java
index 16b68388ed57..273be065e015 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/util/TestCoprocessorConfigurationUtil.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/util/TestCoprocessorConfigurationUtil.java
@@ -17,9 +17,9 @@
*/
package org.apache.hadoop.hbase.util;
+import static org.apache.hadoop.hbase.HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -27,7 +27,6 @@
import java.util.Arrays;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
import org.apache.hadoop.hbase.security.access.BulkLoadReadOnlyController;
import org.apache.hadoop.hbase.security.access.EndpointReadOnlyController;
@@ -125,9 +124,9 @@ public void testRemoveCoprocessorsIdempotentWhenNotPresent() {
assertArrayEquals(new String[] { "cp1" }, conf.getStrings(key));
}
- private void assertEnable(String key, List expected) {
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(true, conf, key);
- assertTrue(conf.getBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, false));
+ private void assertEnableReadOnlyMode(String key, List expected) {
+ conf.setBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, true);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
String[] result = conf.getStrings(key);
assertNotNull(result);
assertEquals(expected.size(), result.length);
@@ -136,34 +135,33 @@ private void assertEnable(String key, List expected) {
@Test
public void testSyncReadOnlyConfigurationsReadOnlyEnableAllKeys() {
- assertEnable(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+ assertEnableReadOnlyMode(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
List.of(MasterReadOnlyController.class.getName()));
- assertEnable(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
+ assertEnableReadOnlyMode(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
List.of(RegionServerReadOnlyController.class.getName()));
- assertEnable(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
+ assertEnableReadOnlyMode(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
List.of(RegionReadOnlyController.class.getName(), BulkLoadReadOnlyController.class.getName(),
EndpointReadOnlyController.class.getName()));
}
- private void assertDisable(String key, List initialCoprocs) {
+ private void assertDisableReadOnlyMode(String key, List initialCoprocs) {
conf.setStrings(key, initialCoprocs.toArray(new String[0]));
- conf.setBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, true);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(false, conf, key);
- assertFalse(conf.getBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, true));
+ conf.setBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, false);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
String[] result = conf.getStrings(key);
assertTrue(result == null || result.length == 0);
}
@Test
public void testSyncReadOnlyConfigurationsReadOnlyDisableAllKeys() {
- assertDisable(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+ assertDisableReadOnlyMode(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
List.of(MasterReadOnlyController.class.getName()));
- assertDisable(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
+ assertDisableReadOnlyMode(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
List.of(RegionServerReadOnlyController.class.getName()));
- assertDisable(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
+ assertDisableReadOnlyMode(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
List.of(RegionReadOnlyController.class.getName(), BulkLoadReadOnlyController.class.getName(),
EndpointReadOnlyController.class.getName()));
}
@@ -172,7 +170,8 @@ public void testSyncReadOnlyConfigurationsReadOnlyDisableAllKeys() {
public void testSyncReadOnlyConfigurationsReadOnlyEnablePreservesExistingCoprocessors() {
String key = CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY;
conf.setStrings(key, "existingCp");
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(true, conf, key);
+ conf.setBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, true);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
List result = Arrays.asList(conf.getStrings(key));
assertTrue(result.contains("existingCp"));
assertTrue(result.contains(MasterReadOnlyController.class.getName()));
@@ -184,7 +183,7 @@ public void testSyncReadOnlyConfigurationsReadOnlyDisableRemovesOnlyReadOnlyCopr
String key = CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY;
String existingCp = "org.example.OtherCP";
conf.setStrings(key, existingCp, MasterReadOnlyController.class.getName());
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(false, conf, key);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
String[] cps = conf.getStrings(key);
assertNotNull(cps);
assertEquals(1, cps.length);
@@ -194,9 +193,10 @@ public void testSyncReadOnlyConfigurationsReadOnlyDisableRemovesOnlyReadOnlyCopr
@Test
public void testSyncReadOnlyConfigurationsIsIdempotent() {
Configuration conf = new Configuration(false);
+ conf.setBoolean(HBASE_GLOBAL_READONLY_ENABLED_KEY, true);
String key = CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY;
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(true, conf, key);
- CoprocessorConfigurationUtil.syncReadOnlyConfigurations(true, conf, key);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
+ CoprocessorConfigurationUtil.syncReadOnlyConfigurations(conf, key);
String[] cps = conf.getStrings(key);
assertNotNull(cps);
assertEquals(1, cps.length);