001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023import static org.mockito.Mockito.mock; 024import static org.mockito.Mockito.verifyNoInteractions; 025import static org.mockito.Mockito.when; 026 027import java.io.IOException; 028import java.io.OutputStream; 029import java.nio.file.Files; 030import java.nio.file.Path; 031import java.util.HashMap; 032import java.util.Map; 033import java.util.Set; 034import java.util.function.Consumer; 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.hbase.CompatibilityFactory; 037import org.apache.hadoop.hbase.TableDescriptors; 038import org.apache.hadoop.hbase.client.MasterSwitchType; 039import org.apache.hadoop.hbase.master.snapshot.SnapshotCleanupStateStore; 040import org.apache.hadoop.hbase.metrics.MetricRegistries; 041import org.apache.hadoop.hbase.quotas.MasterQuotaManager; 042import org.apache.hadoop.hbase.quotas.QuotaUtil; 043import org.apache.hadoop.hbase.quotas.RpcThrottleStateStore; 044import org.apache.hadoop.hbase.test.MetricsAssertHelper; 045import org.apache.hadoop.hbase.testclassification.MasterTests; 046import org.apache.hadoop.hbase.testclassification.SmallTests; 047import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; 048import org.junit.jupiter.api.AfterEach; 049import org.junit.jupiter.api.BeforeEach; 050import org.junit.jupiter.api.Tag; 051import org.junit.jupiter.api.Test; 052import org.junit.jupiter.api.io.TempDir; 053import org.junit.jupiter.params.ParameterizedTest; 054import org.junit.jupiter.params.provider.ValueSource; 055 056@Tag(MasterTests.TAG) 057@Tag(SmallTests.TAG) 058public class TestMasterSwitchMetrics { 059 private static final MetricsAssertHelper METRICS_HELPER = 060 CompatibilityFactory.getInstance(MetricsAssertHelper.class); 061 062 private HMaster master; 063 private Configuration conf; 064 private RpcThrottleStateStore rpcThrottleStateStore; 065 private MetricsMasterWrapperImpl wrapper; 066 private MetricsMasterSourceImpl source; 067 068 @BeforeEach 069 public void setUp() throws IOException { 070 master = mock(HMaster.class); 071 conf = new Configuration(false); 072 conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); 073 when(master.getConfiguration()).thenReturn(conf); 074 when(master.isActiveMaster()).thenReturn(true); 075 when(master.isInitialized()).thenReturn(true); 076 when(master.getMasterWalManager()).thenReturn(mock(MasterWalManager.class)); 077 TableDescriptors tableDescriptors = mock(TableDescriptors.class); 078 when(tableDescriptors.exists(QuotaUtil.QUOTA_TABLE_NAME)).thenReturn(true); 079 when(master.getTableDescriptors()).thenReturn(tableDescriptors); 080 when(master.getClusterSchema()).thenReturn(mock(ClusterSchema.class)); 081 MasterQuotaManager quotaManager = new MasterQuotaManager(master); 082 quotaManager.start(); 083 when(master.getMasterQuotaManager()).thenReturn(quotaManager); 084 master.snapshotCleanupStateStore = mock(SnapshotCleanupStateStore.class); 085 rpcThrottleStateStore = mock(RpcThrottleStateStore.class); 086 when(master.getRpcThrottleStateStore()).thenReturn(rpcThrottleStateStore); 087 wrapper = new MetricsMasterWrapperImpl(master); 088 source = new MetricsMasterSourceImpl(wrapper); 089 } 090 091 @AfterEach 092 public void tearDown() { 093 DefaultMetricsSystem.instance().unregisterSource(MetricsMasterSource.METRICS_JMX_CONTEXT); 094 MetricRegistries.global().remove(source.getMetricRegistryInfo()); 095 } 096 097 @Test 098 public void testEachSwitchChangesItsGauge() { 099 Map<String, Consumer<Boolean>> switches = new HashMap<>(); 100 switches.put(MetricsMasterSource.BALANCER_ENABLED_NAME, 101 enabled -> when(master.isBalancerOn()).thenReturn(enabled)); 102 switches.put(MetricsMasterSource.NORMALIZER_ENABLED_NAME, 103 enabled -> when(master.isNormalizerOn()).thenReturn(enabled)); 104 switches.put(MetricsMasterSource.SPLIT_ENABLED_NAME, 105 enabled -> when(master.isSplitOrMergeEnabled(MasterSwitchType.SPLIT)).thenReturn(enabled)); 106 switches.put(MetricsMasterSource.MERGE_ENABLED_NAME, 107 enabled -> when(master.isSplitOrMergeEnabled(MasterSwitchType.MERGE)).thenReturn(enabled)); 108 switches.put(MetricsMasterSource.CLEANER_CHORE_ENABLED_NAME, 109 enabled -> when(master.isCleanerChoreEnabled()).thenReturn(enabled)); 110 switches.put(MetricsMasterSource.CATALOG_JANITOR_ENABLED_NAME, 111 enabled -> when(master.isCatalogJanitorEnabled()).thenReturn(enabled)); 112 switches.put(MetricsMasterSource.SNAPSHOT_CLEANUP_ENABLED_NAME, 113 enabled -> when(master.snapshotCleanupStateStore.get()).thenReturn(enabled)); 114 switches.put(MetricsMasterSource.REPLICATION_PEER_MODIFICATION_ENABLED_NAME, 115 enabled -> when(master.isReplicationPeerModificationEnabled()).thenReturn(enabled)); 116 switches.put(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, 117 enabled -> when(rpcThrottleStateStore.get()).thenReturn(enabled)); 118 Map<String, Integer> expected = new HashMap<>(); 119 switches.keySet().forEach(name -> expected.put(name, 0)); 120 for (Map.Entry<String, Consumer<Boolean>> entry : switches.entrySet()) { 121 for (boolean enabled : new boolean[] { true, false }) { 122 entry.getValue().accept(enabled); 123 expected.put(entry.getKey(), enabled ? 1 : 0); 124 assertEquals(expected, wrapper.getSwitchStates()); 125 expected 126 .forEach((name, state) -> METRICS_HELPER.assertGauge(name, state.longValue(), source)); 127 } 128 } 129 } 130 131 @Test 132 public void testOnlyActiveInitializedMasterExposesSwitches() { 133 Set<String> metricNames = wrapper.getSwitchStates().keySet(); 134 when(master.isInitialized()).thenReturn(false); 135 assertTrue(wrapper.getSwitchStates().isEmpty()); 136 metricNames.forEach(name -> assertFalse(METRICS_HELPER.checkGaugeExists(name, source))); 137 when(master.isInitialized()).thenReturn(true); 138 when(master.isActiveMaster()).thenReturn(false); 139 assertTrue(wrapper.getSwitchStates().isEmpty()); 140 metricNames.forEach(name -> assertFalse(METRICS_HELPER.checkGaugeExists(name, source))); 141 } 142 143 @Test 144 public void testRpcThrottleOmittedUntilQuotaManagerReady() { 145 MasterQuotaManager quotaManager = master.getMasterQuotaManager(); 146 Map<String, Integer> expected = new HashMap<>(wrapper.getSwitchStates()); 147 expected.remove(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME); 148 when(master.getMasterQuotaManager()).thenReturn(null); 149 assertEquals(expected, wrapper.getSwitchStates()); 150 assertFalse( 151 METRICS_HELPER.checkGaugeExists(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, source)); 152 expected.forEach((name, state) -> METRICS_HELPER.assertGauge(name, state.longValue(), source)); 153 154 when(master.getMasterQuotaManager()).thenReturn(quotaManager); 155 when(rpcThrottleStateStore.get()).thenReturn(true); 156 expected.put(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, 1); 157 assertEquals(expected, wrapper.getSwitchStates()); 158 METRICS_HELPER.assertGauge(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, 1L, source); 159 } 160 161 @Test 162 public void testRpcThrottleDisabledWithoutQuotaSupport() { 163 conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, false); 164 when(master.getMasterQuotaManager()).thenReturn(null); 165 when(rpcThrottleStateStore.get()).thenReturn(true); 166 assertEquals(0, wrapper.getSwitchStates().get(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME)); 167 METRICS_HELPER.assertGauge(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, 0L, source); 168 verifyNoInteractions(rpcThrottleStateStore); 169 } 170 171 @ParameterizedTest 172 @ValueSource(booleans = { false, true }) 173 public void testRpcThrottleUnaffectedByConfigurationReload(boolean quotaEnabled, 174 @TempDir Path tempDir) throws IOException { 175 Path site = tempDir.resolve("hbase-site.xml"); 176 writeQuotaConfiguration(site, quotaEnabled); 177 conf = new Configuration(false); 178 conf.addResource(new org.apache.hadoop.fs.Path(site.toUri())); 179 when(master.getConfiguration()).thenReturn(conf); 180 MasterQuotaManager quotaManager = new MasterQuotaManager(master); 181 quotaManager.start(); 182 when(master.getMasterQuotaManager()).thenReturn(quotaManager); 183 when(rpcThrottleStateStore.get()).thenReturn(true); 184 long expected = quotaEnabled ? 1L : 0L; 185 assertEquals(quotaEnabled, quotaManager.isRpcThrottleEnabled()); 186 METRICS_HELPER.assertGauge(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, expected, source); 187 188 writeQuotaConfiguration(site, !quotaEnabled); 189 conf.reloadConfiguration(); 190 assertEquals(!quotaEnabled, QuotaUtil.isQuotaEnabled(conf)); 191 assertEquals(quotaEnabled, quotaManager.isRpcThrottleEnabled()); 192 METRICS_HELPER.assertGauge(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, expected, source); 193 } 194 195 private static void writeQuotaConfiguration(Path path, boolean quotaEnabled) throws IOException { 196 Configuration config = new Configuration(false); 197 config.setBoolean(QuotaUtil.QUOTA_CONF_KEY, quotaEnabled); 198 try (OutputStream out = Files.newOutputStream(path)) { 199 config.writeXml(out); 200 } 201 } 202}