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;
019
020import java.io.IOException;
021import java.util.EnumSet;
022import java.util.List;
023import java.util.Optional;
024import java.util.concurrent.CompletableFuture;
025import java.util.concurrent.atomic.AtomicInteger;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.ClusterMetrics.Option;
028import org.apache.hadoop.hbase.Waiter.Predicate;
029import org.apache.hadoop.hbase.client.Admin;
030import org.apache.hadoop.hbase.client.AsyncAdmin;
031import org.apache.hadoop.hbase.client.AsyncConnection;
032import org.apache.hadoop.hbase.client.ConnectionFactory;
033import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
034import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
035import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
036import org.apache.hadoop.hbase.coprocessor.MasterObserver;
037import org.apache.hadoop.hbase.coprocessor.ObserverContext;
038import org.apache.hadoop.hbase.master.HMaster;
039import org.apache.hadoop.hbase.regionserver.HRegionServer;
040import org.apache.hadoop.hbase.testclassification.SmallTests;
041import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread;
042import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
043import org.junit.AfterClass;
044import org.junit.Assert;
045import org.junit.BeforeClass;
046import org.junit.ClassRule;
047import org.junit.Test;
048import org.junit.experimental.categories.Category;
049
050@Category(SmallTests.class)
051public class TestClientClusterMetrics {
052
053  @ClassRule
054  public static final HBaseClassTestRule CLASS_RULE =
055      HBaseClassTestRule.forClass(TestClientClusterMetrics.class);
056
057  private static HBaseTestingUtility UTIL;
058  private static Admin ADMIN;
059  private final static int SLAVES = 5;
060  private final static int MASTERS = 3;
061  private static MiniHBaseCluster CLUSTER;
062  private static HRegionServer DEAD;
063
064  @BeforeClass
065  public static void setUpBeforeClass() throws Exception {
066    Configuration conf = HBaseConfiguration.create();
067    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyObserver.class.getName());
068    UTIL = new HBaseTestingUtility(conf);
069    UTIL.startMiniCluster(MASTERS, SLAVES);
070    CLUSTER = UTIL.getHBaseCluster();
071    CLUSTER.waitForActiveAndReadyMaster();
072    ADMIN = UTIL.getAdmin();
073    // Kill one region server
074    List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads();
075    RegionServerThread rst = rsts.get(rsts.size() - 1);
076    DEAD = rst.getRegionServer();
077    DEAD.stop("Test dead servers metrics");
078    while (rst.isAlive()) {
079      Thread.sleep(500);
080    }
081  }
082
083  @Test
084  public void testDefaults() throws Exception {
085    ClusterMetrics origin = ADMIN.getClusterMetrics();
086    ClusterMetrics defaults = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class));
087    Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion());
088    Assert.assertEquals(origin.getClusterId(), defaults.getClusterId());
089    Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0);
090    Assert.assertEquals(origin.getBackupMasterNames().size(),
091        defaults.getBackupMasterNames().size());
092    Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size());
093    Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount());
094    Assert.assertEquals(origin.getLiveServerMetrics().size(),
095        defaults.getLiveServerMetrics().size());
096    Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort());
097  }
098
099  @Test
100  public void testAsyncClient() throws Exception {
101    try (AsyncConnection asyncConnect = ConnectionFactory.createAsyncConnection(
102      UTIL.getConfiguration()).get()) {
103      AsyncAdmin asyncAdmin = asyncConnect.getAdmin();
104      CompletableFuture<ClusterMetrics> originFuture =
105        asyncAdmin.getClusterMetrics();
106      CompletableFuture<ClusterMetrics> defaultsFuture =
107        asyncAdmin.getClusterMetrics(EnumSet.allOf(Option.class));
108      ClusterMetrics origin = originFuture.get();
109      ClusterMetrics defaults = defaultsFuture.get();
110      Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion());
111      Assert.assertEquals(origin.getClusterId(), defaults.getClusterId());
112      Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion());
113      Assert.assertEquals(origin.getClusterId(), defaults.getClusterId());
114      Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0);
115      Assert.assertEquals(origin.getBackupMasterNames().size(),
116        defaults.getBackupMasterNames().size());
117      Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size());
118      Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount());
119      Assert.assertEquals(origin.getLiveServerMetrics().size(),
120        defaults.getLiveServerMetrics().size());
121      Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort());
122    }
123  }
124
125  @Test
126  public void testLiveAndDeadServersStatus() throws Exception {
127    // Count the number of live regionservers
128    List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads();
129    int numRs = 0;
130    int len = regionserverThreads.size();
131    for (int i = 0; i < len; i++) {
132      if (regionserverThreads.get(i).isAlive()) {
133        numRs++;
134      }
135    }
136    // Depending on the (random) order of unit execution we may run this unit before the
137    // minicluster is fully up and recovered from the RS shutdown done during test init.
138    Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() {
139      @Override
140      public boolean evaluate() throws Exception {
141        ClusterMetrics metrics = ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS));
142        Assert.assertNotNull(metrics);
143        return metrics.getRegionCount() > 0;
144      }
145    });
146    // Retrieve live servers and dead servers info.
147    EnumSet<Option> options = EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS);
148    ClusterMetrics metrics = ADMIN.getClusterMetrics(options);
149    Assert.assertNotNull(metrics);
150    // exclude a dead region server
151    Assert.assertEquals(SLAVES -1, numRs);
152    // live servers = nums of regionservers
153    // By default, HMaster don't carry any regions so it won't report its load.
154    // Hence, it won't be in the server list.
155    Assert.assertEquals(numRs, metrics.getLiveServerMetrics().size());
156    Assert.assertTrue(metrics.getRegionCount() > 0);
157    Assert.assertNotNull(metrics.getDeadServerNames());
158    Assert.assertEquals(1, metrics.getDeadServerNames().size());
159    ServerName deadServerName = metrics.getDeadServerNames().iterator().next();
160    Assert.assertEquals(DEAD.getServerName(), deadServerName);
161  }
162
163  @Test
164  public void testMasterAndBackupMastersStatus() throws Exception {
165    // get all the master threads
166    List<MasterThread> masterThreads = CLUSTER.getMasterThreads();
167    int numActive = 0;
168    int activeIndex = 0;
169    ServerName activeName = null;
170    HMaster active = null;
171    for (int i = 0; i < masterThreads.size(); i++) {
172      if (masterThreads.get(i).getMaster().isActiveMaster()) {
173        numActive++;
174        activeIndex = i;
175        active = masterThreads.get(activeIndex).getMaster();
176        activeName = active.getServerName();
177      }
178    }
179    Assert.assertNotNull(active);
180    Assert.assertEquals(1, numActive);
181    Assert.assertEquals(MASTERS, masterThreads.size());
182    // Retrieve master and backup masters infos only.
183    EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS);
184    ClusterMetrics metrics = ADMIN.getClusterMetrics(options);
185    Assert.assertTrue(metrics.getMasterName().equals(activeName));
186    Assert.assertEquals(MASTERS - 1, metrics.getBackupMasterNames().size());
187  }
188
189  @Test
190  public void testOtherStatusInfos() throws Exception {
191    EnumSet<Option> options =
192        EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION,
193                   Option.CLUSTER_ID, Option.BALANCER_ON);
194    ClusterMetrics metrics = ADMIN.getClusterMetrics(options);
195    Assert.assertEquals(1, metrics.getMasterCoprocessorNames().size());
196    Assert.assertNotNull(metrics.getHBaseVersion());
197    Assert.assertNotNull(metrics.getClusterId());
198    Assert.assertTrue(metrics.getAverageLoad() == 0.0);
199    Assert.assertNotNull(metrics.getBalancerOn());
200  }
201
202  @AfterClass
203  public static void tearDownAfterClass() throws Exception {
204    if (ADMIN != null) {
205      ADMIN.close();
206    }
207    UTIL.shutdownMiniCluster();
208  }
209
210  @Test
211  public void testObserver() throws IOException {
212    int preCount = MyObserver.PRE_COUNT.get();
213    int postCount = MyObserver.POST_COUNT.get();
214    Assert.assertTrue(ADMIN.getClusterMetrics().getMasterCoprocessorNames().stream()
215        .anyMatch(s -> s.equals(MyObserver.class.getSimpleName())));
216    Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get());
217    Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get());
218  }
219
220  public static class MyObserver implements MasterCoprocessor, MasterObserver {
221    private static final AtomicInteger PRE_COUNT = new AtomicInteger(0);
222    private static final AtomicInteger POST_COUNT = new AtomicInteger(0);
223
224    @Override public Optional<MasterObserver> getMasterObserver() {
225      return Optional.of(this);
226    }
227
228    @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx)
229        throws IOException {
230      PRE_COUNT.incrementAndGet();
231    }
232
233    @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx,
234        ClusterMetrics metrics) throws IOException {
235      POST_COUNT.incrementAndGet();
236    }
237  }
238}