From 7d5d1fe752e3065f6e4e021557310c72eab14653 Mon Sep 17 00:00:00 2001 From: Todd Lipcon Date: Tue, 3 May 2011 19:07:54 +0000 Subject: [PATCH] HBASE-3837 Show regions in transition on the master web page git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1099199 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 1 + .../master/AssignmentManagerStatusTmpl.jamon | 42 +++++++++++ .../hbase/tmpl/master/MasterStatusTmpl.jamon | 3 + .../hbase/master/AssignmentManager.java | 74 ++++++++++++------- .../hbase/master/TestMasterFailover.java | 4 +- .../hbase/master/TestMasterStatusServlet.java | 24 +++++- 6 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 src/main/jamon/org/apache/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon diff --git a/CHANGES.txt b/CHANGES.txt index 24e011768f9..86d0ec6881f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -236,6 +236,7 @@ Release 0.91.0 - Unreleased (Prakash Khemani) HBASE-3836 Add facility to track currently progressing actions and workflows. (todd) + HBASE-3837 Show regions in transition on the master web page (todd) Release 0.90.3 - Unreleased diff --git a/src/main/jamon/org/apache/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon b/src/main/jamon/org/apache/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon new file mode 100644 index 00000000000..f5279043567 --- /dev/null +++ b/src/main/jamon/org/apache/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon @@ -0,0 +1,42 @@ +<%doc> +Copyright 2011 The Apache Software Foundation + +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. + +<%import> +org.apache.hadoop.hbase.master.AssignmentManager; +org.apache.hadoop.hbase.master.AssignmentManager.RegionState; +java.util.Map; + +<%args> +AssignmentManager assignmentManager; + +<%java> +Map rit = assignmentManager.getRegionsInTransition(); + + +

Regions in Transition

+<%if rit.isEmpty() %> +No regions in transition. +<%else> + + + <%for Map.Entry entry : rit.entrySet() %> + + +
RegionState
<% entry.getKey() %><% entry.getValue() %>
+ \ No newline at end of file diff --git a/src/main/jamon/org/apache/hbase/tmpl/master/MasterStatusTmpl.jamon b/src/main/jamon/org/apache/hbase/tmpl/master/MasterStatusTmpl.jamon index 4b1b4f44529..47067926fad 100644 --- a/src/main/jamon/org/apache/hbase/tmpl/master/MasterStatusTmpl.jamon +++ b/src/main/jamon/org/apache/hbase/tmpl/master/MasterStatusTmpl.jamon @@ -97,6 +97,9 @@ org.apache.hadoop.hbase.HTableDescriptor; <%if (servers != null) %> <& regionServers &> + +<& AssignmentManagerStatusTmpl; assignmentManager=master.getAssignmentManager()&> + diff --git a/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index 57af9f4356a..5837beb85b2 100644 --- a/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -332,20 +332,23 @@ public class AssignmentManager extends ZooKeeperListener { // Just insert region into RIT. // If this never updates the timeout will trigger new assignment regionsInTransition.put(encodedRegionName, new RegionState( - regionInfo, RegionState.State.CLOSING, data.getStamp())); + regionInfo, RegionState.State.CLOSING, + data.getStamp(), data.getOrigin())); break; case RS_ZK_REGION_CLOSED: // Region is closed, insert into RIT and handle it regionsInTransition.put(encodedRegionName, new RegionState( - regionInfo, RegionState.State.CLOSED, data.getStamp())); + regionInfo, RegionState.State.CLOSED, + data.getStamp(), data.getOrigin())); new ClosedRegionHandler(master, this, regionInfo).process(); break; case M_ZK_REGION_OFFLINE: // Region is offline, insert into RIT and handle it like a closed regionsInTransition.put(encodedRegionName, new RegionState( - regionInfo, RegionState.State.OFFLINE, data.getStamp())); + regionInfo, RegionState.State.OFFLINE, + data.getStamp(), data.getOrigin())); new ClosedRegionHandler(master, this, regionInfo).process(); break; @@ -353,13 +356,15 @@ public class AssignmentManager extends ZooKeeperListener { // Just insert region into RIT // If this never updates the timeout will trigger new assignment regionsInTransition.put(encodedRegionName, new RegionState( - regionInfo, RegionState.State.OPENING, data.getStamp())); + regionInfo, RegionState.State.OPENING, + data.getStamp(), data.getOrigin())); break; case RS_ZK_REGION_OPENED: // Region is opened, insert into RIT and handle it regionsInTransition.put(encodedRegionName, new RegionState( - regionInfo, RegionState.State.OPENING, data.getStamp())); + regionInfo, RegionState.State.OPENING, + data.getStamp(), data.getOrigin())); ServerName sn = data.getOrigin() == null? null: data.getOrigin(); // hsi could be null if this server is no longer online. If @@ -422,7 +427,7 @@ public class AssignmentManager extends ZooKeeperListener { case RS_ZK_REGION_SPLITTING: if (!isInStateForSplitting(regionState)) break; - addSplittingToRIT(sn.toString(), encodedName); + addSplittingToRIT(sn, encodedName); break; case RS_ZK_REGION_SPLIT: @@ -433,7 +438,7 @@ public class AssignmentManager extends ZooKeeperListener { LOG.info("Received SPLIT for region " + prettyPrintedRegionName + " from server " + sn + " but region was not first in SPLITTING state; continuing"); - addSplittingToRIT(sn.toString(), encodedName); + addSplittingToRIT(sn, encodedName); } // Check it has daughters. byte [] payload = data.getPayload(); @@ -468,7 +473,8 @@ public class AssignmentManager extends ZooKeeperListener { return; } // Transition to CLOSING (or update stamp if already CLOSING) - regionState.update(RegionState.State.CLOSING, data.getStamp()); + regionState.update(RegionState.State.CLOSING, + data.getStamp(), data.getOrigin()); break; case RS_ZK_REGION_CLOSED: @@ -484,7 +490,8 @@ public class AssignmentManager extends ZooKeeperListener { // Handle CLOSED by assigning elsewhere or stopping if a disable // If we got here all is good. Need to update RegionState -- else // what follows will fail because not in expected state. - regionState.update(RegionState.State.CLOSED, data.getStamp()); + regionState.update(RegionState.State.CLOSED, + data.getStamp(), data.getOrigin()); this.executorService.submit(new ClosedRegionHandler(master, this, regionState.getRegion())); break; @@ -502,7 +509,8 @@ public class AssignmentManager extends ZooKeeperListener { return; } // Transition to OPENING (or update stamp if already OPENING) - regionState.update(RegionState.State.OPENING, data.getStamp()); + regionState.update(RegionState.State.OPENING, + data.getStamp(), data.getOrigin()); break; case RS_ZK_REGION_OPENED: @@ -517,7 +525,8 @@ public class AssignmentManager extends ZooKeeperListener { return; } // Handle OPENED by removing from transition and deleted zk node - regionState.update(RegionState.State.OPEN, data.getStamp()); + regionState.update(RegionState.State.OPEN, + data.getStamp(), data.getOrigin()); this.executorService.submit( new OpenedRegionHandler(master, this, regionState.getRegion(), data.getOrigin())); @@ -564,12 +573,13 @@ public class AssignmentManager extends ZooKeeperListener { * @return The SPLITTING RegionState we added to RIT for the passed region * encodedName */ - private RegionState addSplittingToRIT(final String serverName, + private RegionState addSplittingToRIT(final ServerName serverName, final String encodedName) { RegionState regionState = null; synchronized (this.regions) { regionState = findHRegionInfoThenAddToRIT(serverName, encodedName); - regionState.update(RegionState.State.SPLITTING); + regionState.update(RegionState.State.SPLITTING, + System.currentTimeMillis(), serverName); } return regionState; } @@ -580,7 +590,7 @@ public class AssignmentManager extends ZooKeeperListener { * @param encodedName * @return The instance of RegionState that was added to RIT or null if error. */ - private RegionState findHRegionInfoThenAddToRIT(final String serverName, + private RegionState findHRegionInfoThenAddToRIT(final ServerName serverName, final String encodedName) { HRegionInfo hri = findHRegionInfo(serverName, encodedName); if (hri == null) { @@ -598,9 +608,8 @@ public class AssignmentManager extends ZooKeeperListener { * @param encodedName * @return Found HRegionInfo or null. */ - private HRegionInfo findHRegionInfo(final String serverName, + private HRegionInfo findHRegionInfo(final ServerName sn, final String encodedName) { - ServerName sn = new ServerName(serverName); if (!this.serverManager.isServerOnline(sn)) return null; List hris = this.servers.get(sn); HRegionInfo foundHri = null; @@ -824,7 +833,7 @@ public class AssignmentManager extends ZooKeeperListener { } if (rs == null) continue; synchronized (rs) { - rs.update(rs.getState()); + rs.updateTimestampToNow(); } } } @@ -1028,7 +1037,7 @@ public class AssignmentManager extends ZooKeeperListener { // Async exists to set a watcher so we'll get triggered when // unassigned node changes. this.zkw.getZooKeeper().exists(path, this.zkw, - new ExistsUnassignedAsyncCallback(this.counter), ctx); + new ExistsUnassignedAsyncCallback(this.counter, destination), ctx); } } @@ -1039,9 +1048,11 @@ public class AssignmentManager extends ZooKeeperListener { static class ExistsUnassignedAsyncCallback implements AsyncCallback.StatCallback { private final Log LOG = LogFactory.getLog(ExistsUnassignedAsyncCallback.class); private final AtomicInteger counter; + private ServerName destination; - ExistsUnassignedAsyncCallback(final AtomicInteger counter) { + ExistsUnassignedAsyncCallback(final AtomicInteger counter, ServerName destination) { this.counter = counter; + this.destination = destination; } @Override @@ -1059,7 +1070,7 @@ public class AssignmentManager extends ZooKeeperListener { // yet sent out the actual open but putting this state change after the // call to open risks our writing PENDING_OPEN after state has been moved // to OPENING by the regionserver. - state.update(RegionState.State.PENDING_OPEN); + state.update(RegionState.State.PENDING_OPEN, System.currentTimeMillis(), destination); this.counter.addAndGet(1); } } @@ -1113,7 +1124,8 @@ public class AssignmentManager extends ZooKeeperListener { LOG.debug("Assigning region " + state.getRegion().getRegionNameAsString() + " to " + plan.getDestination().toString()); // Transition RegionState to PENDING_OPEN - state.update(RegionState.State.PENDING_OPEN); + state.update(RegionState.State.PENDING_OPEN, System.currentTimeMillis(), + plan.getDestination()); // Send OPEN RPC. This can fail if the server on other end is is not up. serverManager.sendRegionOpen(plan.getDestination(), state.getRegion()); break; @@ -2151,27 +2163,35 @@ public class AssignmentManager extends ZooKeeperListener { private State state; private long stamp; + private ServerName serverName; public RegionState() {} RegionState(HRegionInfo region, State state) { - this(region, state, System.currentTimeMillis()); + this(region, state, System.currentTimeMillis(), null); } - RegionState(HRegionInfo region, State state, long stamp) { + RegionState(HRegionInfo region, State state, long stamp, ServerName serverName) { this.region = region; this.state = state; this.stamp = stamp; + this.serverName = serverName; } - public void update(State state, long stamp) { + public void update(State state, long stamp, ServerName serverName) { this.state = state; this.stamp = stamp; + this.serverName = serverName; } public void update(State state) { this.state = state; this.stamp = System.currentTimeMillis(); + this.serverName = null; + } + + public void updateTimestampToNow() { + this.stamp = System.currentTimeMillis(); } public State getState() { @@ -2224,8 +2244,10 @@ public class AssignmentManager extends ZooKeeperListener { @Override public String toString() { - return region.getRegionNameAsString() + " state=" + state + - ", ts=" + stamp; + return region.getRegionNameAsString() + + " state=" + state + + ", ts=" + stamp + + ", server=" + serverName; } @Override diff --git a/src/test/java/org/apache/hadoop/hbase/master/TestMasterFailover.java b/src/test/java/org/apache/hadoop/hbase/master/TestMasterFailover.java index 6b95eee2989..2022767dfc0 100644 --- a/src/test/java/org/apache/hadoop/hbase/master/TestMasterFailover.java +++ b/src/test/java/org/apache/hadoop/hbase/master/TestMasterFailover.java @@ -808,13 +808,13 @@ public class TestMasterFailover { region = enabledRegions.remove(0); regionsThatShouldBeOnline.add(region); master.assignmentManager.regionsInTransition.put(region.getEncodedName(), - new RegionState(region, RegionState.State.PENDING_OPEN, 0)); + new RegionState(region, RegionState.State.PENDING_OPEN, 0, null)); ZKAssign.createNodeOffline(zkw, region, master.getServerName()); // PENDING_OPEN and disabled region = disabledRegions.remove(0); regionsThatShouldBeOffline.add(region); master.assignmentManager.regionsInTransition.put(region.getEncodedName(), - new RegionState(region, RegionState.State.PENDING_OPEN, 0)); + new RegionState(region, RegionState.State.PENDING_OPEN, 0, null)); ZKAssign.createNodeOffline(zkw, region, master.getServerName()); // This test is bad. It puts up a PENDING_CLOSE but doesn't say what // server we were PENDING_CLOSE against -- i.e. an entry in diff --git a/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java b/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java index d2697c8aeef..1fef78834d4 100644 --- a/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java +++ b/src/test/java/org/apache/hadoop/hbase/master/TestMasterStatusServlet.java @@ -22,14 +22,18 @@ package org.apache.hadoop.hbase.master; import java.io.IOException; import java.io.StringWriter; import java.util.List; +import java.util.NavigableMap; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.master.AssignmentManager.RegionState; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.hbase.tmpl.master.MasterStatusTmpl; import org.junit.Before; @@ -37,6 +41,7 @@ import org.junit.Test; import org.mockito.Mockito; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; /** * Tests for the master status page and its template. @@ -47,13 +52,19 @@ public class TestMasterStatusServlet { private Configuration conf; private HBaseAdmin admin; + static final ServerName FAKE_HOST = + new ServerName("fakehost", 12345, 1234567890); + static final HTableDescriptor FAKE_TABLE = + new HTableDescriptor("mytable"); + static final HRegionInfo FAKE_REGION = + new HRegionInfo(FAKE_TABLE, Bytes.toBytes("a"), Bytes.toBytes("b")); + @Before public void setupBasicMocks() { conf = HBaseConfiguration.create(); master = Mockito.mock(HMaster.class); - Mockito.doReturn(new ServerName("fakehost", 12345, 1234567890)) - .when(master).getServerName(); + Mockito.doReturn(FAKE_HOST).when(master).getServerName(); Mockito.doReturn(conf).when(master).getConfiguration(); // Fake serverManager @@ -61,6 +72,15 @@ public class TestMasterStatusServlet { Mockito.doReturn(1.0).when(serverManager).getAverageLoad(); Mockito.doReturn(serverManager).when(master).getServerManager(); + // Fake AssignmentManager and RIT + AssignmentManager am = Mockito.mock(AssignmentManager.class); + NavigableMap regionsInTransition = + Maps.newTreeMap(); + regionsInTransition.put("r1", + new RegionState(FAKE_REGION, RegionState.State.CLOSING, 12345L, FAKE_HOST)); + Mockito.doReturn(regionsInTransition).when(am).getRegionsInTransition(); + Mockito.doReturn(am).when(master).getAssignmentManager(); + // Fake ZKW ZooKeeperWatcher zkw = Mockito.mock(ZooKeeperWatcher.class); Mockito.doReturn("fakequorum").when(zkw).getQuorum();