View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.master;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.TreeMap;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.HRegionInfo;
36  import org.apache.hadoop.hbase.HTableDescriptor;
37  import org.apache.hadoop.hbase.MetaTableAccessor;
38  import org.apache.hadoop.hbase.RegionTransition;
39  import org.apache.hadoop.hbase.ServerLoad;
40  import org.apache.hadoop.hbase.ServerName;
41  import org.apache.hadoop.hbase.TableName;
42  import org.apache.hadoop.hbase.TableStateManager;
43  import org.apache.hadoop.hbase.classification.InterfaceAudience;
44  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
45  import org.apache.hadoop.hbase.master.RegionState.State;
46  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
47  import org.apache.hadoop.hbase.util.Bytes;
48  import org.apache.hadoop.hbase.util.ConfigUtil;
49  import org.apache.hadoop.hbase.util.FSUtils;
50  import org.apache.hadoop.hbase.util.Pair;
51  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
52  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
53  import org.apache.zookeeper.KeeperException;
54  
55  import com.google.common.annotations.VisibleForTesting;
56  import com.google.common.base.Preconditions;
57  
58  /**
59   * Region state accountant. It holds the states of all regions in the memory.
60   * In normal scenario, it should match the meta table and the true region states.
61   *
62   * This map is used by AssignmentManager to track region states.
63   */
64  @InterfaceAudience.Private
65  public class RegionStates {
66    private static final Log LOG = LogFactory.getLog(RegionStates.class);
67  
68    /**
69     * Regions currently in transition.
70     */
71    final HashMap<String, RegionState> regionsInTransition =
72      new HashMap<String, RegionState>();
73  
74    /**
75     * Region encoded name to state map.
76     * All the regions should be in this map.
77     */
78    private final Map<String, RegionState> regionStates =
79      new HashMap<String, RegionState>();
80  
81    /**
82     * Holds mapping of table -> region state
83     */
84    private final Map<TableName, Map<String, RegionState>> regionStatesTableIndex =
85        new HashMap<TableName, Map<String, RegionState>>();
86  
87    /**
88     * Server to regions assignment map.
89     * Contains the set of regions currently assigned to a given server.
90     */
91    private final Map<ServerName, Set<HRegionInfo>> serverHoldings =
92      new HashMap<ServerName, Set<HRegionInfo>>();
93  
94    /**
95     * Maintains the mapping from the default region to the replica regions.
96     */
97    private final Map<HRegionInfo, Set<HRegionInfo>> defaultReplicaToOtherReplicas =
98      new HashMap<HRegionInfo, Set<HRegionInfo>>();
99  
100   /**
101    * Region to server assignment map.
102    * Contains the server a given region is currently assigned to.
103    */
104   private final TreeMap<HRegionInfo, ServerName> regionAssignments =
105     new TreeMap<HRegionInfo, ServerName>();
106 
107   /**
108    * Encoded region name to server assignment map for re-assignment
109    * purpose. Contains the server a given region is last known assigned
110    * to, which has not completed log splitting, so not assignable.
111    * If a region is currently assigned, this server info in this
112    * map should be the same as that in regionAssignments.
113    * However the info in regionAssignments is cleared when the region
114    * is offline while the info in lastAssignments is cleared when
115    * the region is closed or the server is dead and processed.
116    */
117   private final HashMap<String, ServerName> lastAssignments =
118     new HashMap<String, ServerName>();
119 
120   /**
121    * Encoded region name to server assignment map for the
122    * purpose to clean up serverHoldings when a region is online
123    * on a new server. When the region is offline from the previous
124    * server, we cleaned up regionAssignments so that it has the
125    * latest assignment map. But we didn't clean up serverHoldings
126    * to match the meta. We need this map to find out the old server
127    * whose serverHoldings needs cleanup, given a moved region.
128    */
129   private final HashMap<String, ServerName> oldAssignments =
130     new HashMap<String, ServerName>();
131 
132   /**
133    * Map a host port pair string to the latest start code
134    * of a region server which is known to be dead. It is dead
135    * to us, but server manager may not know it yet.
136    */
137   private final HashMap<String, Long> deadServers =
138     new HashMap<String, Long>();
139 
140   /**
141    * Map a dead servers to the time when log split is done.
142    * Since log splitting is not ordered, we have to remember
143    * all processed instances. The map is cleaned up based
144    * on a configured time. By default, we assume a dead
145    * server should be done with log splitting in two hours.
146    */
147   private final HashMap<ServerName, Long> processedServers =
148     new HashMap<ServerName, Long>();
149   private long lastProcessedServerCleanTime;
150 
151   private final TableStateManager tableStateManager;
152   private final RegionStateStore regionStateStore;
153   private final ServerManager serverManager;
154   private final MasterServices server;
155   private final boolean useZK; // Is it ZK based assignment?
156 
157   // The maximum time to keep a log split info in region states map
158   static final String LOG_SPLIT_TIME = "hbase.master.maximum.logsplit.keeptime";
159   static final long DEFAULT_LOG_SPLIT_TIME = 7200000L; // 2 hours
160 
161   RegionStates(final MasterServices master, final TableStateManager tableStateManager,
162       final ServerManager serverManager, final RegionStateStore regionStateStore) {
163     this.tableStateManager = tableStateManager;
164     this.regionStateStore = regionStateStore;
165     this.serverManager = serverManager;
166     this.server = master;
167     this.useZK = ConfigUtil.useZKForAssignment(server.getConfiguration());
168   }
169 
170   /**
171    * @return a copy of the region assignment map
172    */
173   public synchronized Map<HRegionInfo, ServerName> getRegionAssignments() {
174     return new TreeMap<HRegionInfo, ServerName>(regionAssignments);
175   }
176 
177   /**
178    * Return the replicas (including default) for the regions grouped by ServerName
179    * @param regions
180    * @return a pair containing the groupings as a map
181    */
182   synchronized Map<ServerName, List<HRegionInfo>> getRegionAssignments(
183     Collection<HRegionInfo> regions) {
184     Map<ServerName, List<HRegionInfo>> map = new HashMap<ServerName, List<HRegionInfo>>();
185     for (HRegionInfo region : regions) {
186       HRegionInfo defaultReplica = RegionReplicaUtil.getRegionInfoForDefaultReplica(region);
187       Set<HRegionInfo> allReplicas = defaultReplicaToOtherReplicas.get(defaultReplica);
188       if (allReplicas != null) {
189         for (HRegionInfo hri : allReplicas) {
190           ServerName server = regionAssignments.get(hri);
191           if (server != null) {
192             List<HRegionInfo> regionsOnServer = map.get(server);
193             if (regionsOnServer == null) {
194               regionsOnServer = new ArrayList<HRegionInfo>(1);
195               map.put(server, regionsOnServer);
196             }
197             regionsOnServer.add(hri);
198           }
199         }
200       }
201     }
202     return map;
203   }
204 
205   public synchronized ServerName getRegionServerOfRegion(HRegionInfo hri) {
206     return regionAssignments.get(hri);
207   }
208 
209   /**
210    * Get regions in transition and their states
211    */
212   @SuppressWarnings("unchecked")
213   public synchronized Map<String, RegionState> getRegionsInTransition() {
214     return (Map<String, RegionState>)regionsInTransition.clone();
215   }
216 
217   /**
218    * @return True if specified region in transition.
219    */
220   public synchronized boolean isRegionInTransition(final HRegionInfo hri) {
221     return regionsInTransition.containsKey(hri.getEncodedName());
222   }
223 
224   /**
225    * @return True if specified region in transition.
226    */
227   public synchronized boolean isRegionInTransition(final String encodedName) {
228     return regionsInTransition.containsKey(encodedName);
229   }
230 
231   /**
232    * @return True if any region in transition.
233    */
234   public synchronized boolean isRegionsInTransition() {
235     return !regionsInTransition.isEmpty();
236   }
237 
238   /**
239    * @return True if hbase:meta table region is in transition.
240    */
241   public synchronized boolean isMetaRegionInTransition() {
242     for (RegionState state : regionsInTransition.values()) {
243       if (state.getRegion().isMetaRegion()) return true;
244     }
245     return false;
246   }
247 
248   /**
249    * @return True if specified region assigned, and not in transition.
250    */
251   public synchronized boolean isRegionOnline(final HRegionInfo hri) {
252     return !isRegionInTransition(hri) && regionAssignments.containsKey(hri);
253   }
254 
255   /**
256    * @return True if specified region offline/closed, but not in transition.
257    * If the region is not in the map, it is offline to us too.
258    */
259   public synchronized boolean isRegionOffline(final HRegionInfo hri) {
260     return getRegionState(hri) == null || (!isRegionInTransition(hri)
261       && isRegionInState(hri, State.OFFLINE, State.CLOSED));
262   }
263 
264   /**
265    * @return True if specified region is in one of the specified states.
266    */
267   public boolean isRegionInState(
268       final HRegionInfo hri, final State... states) {
269     return isRegionInState(hri.getEncodedName(), states);
270   }
271 
272   /**
273    * @return True if specified region is in one of the specified states.
274    */
275   public boolean isRegionInState(
276       final String encodedName, final State... states) {
277     RegionState regionState = getRegionState(encodedName);
278     return isOneOfStates(regionState, states);
279   }
280 
281   /**
282    * Wait for the state map to be updated by assignment manager.
283    */
284   public synchronized void waitForUpdate(
285       final long timeout) throws InterruptedException {
286     this.wait(timeout);
287   }
288 
289   /**
290    * Get region transition state
291    */
292   public RegionState getRegionTransitionState(final HRegionInfo hri) {
293     return getRegionTransitionState(hri.getEncodedName());
294   }
295 
296   /**
297    * Get region transition state
298    */
299   public synchronized RegionState
300       getRegionTransitionState(final String encodedName) {
301     return regionsInTransition.get(encodedName);
302   }
303 
304   /**
305    * Add a list of regions to RegionStates. If a region is split
306    * and offline, its state will be SPLIT. Otherwise, its state will
307    * be OFFLINE. Region already in RegionStates will be skipped.
308    */
309   public void createRegionStates(
310       final List<HRegionInfo> hris) {
311     for (HRegionInfo hri: hris) {
312       createRegionState(hri);
313     }
314   }
315 
316   /**
317    * Add a region to RegionStates. If the region is split
318    * and offline, its state will be SPLIT. Otherwise, its state will
319    * be OFFLINE. If it is already in RegionStates, this call has
320    * no effect, and the original state is returned.
321    */
322   public RegionState createRegionState(final HRegionInfo hri) {
323     return createRegionState(hri, null, null, null);
324   }
325 
326   /**
327    * Add a region to RegionStates with the specified state.
328    * If the region is already in RegionStates, this call has
329    * no effect, and the original state is returned.
330    *
331    * @param hri the region info to create a state for
332    * @param newState the state to the region in set to
333    * @param serverName the server the region is transitioning on
334    * @param lastHost the last server that hosts the region
335    * @return the current state
336    */
337   public synchronized RegionState createRegionState(final HRegionInfo hri,
338       State newState, ServerName serverName, ServerName lastHost) {
339     if (newState == null || (newState == State.OPEN && serverName == null)) {
340       newState =  State.OFFLINE;
341     }
342     if (hri.isOffline() && hri.isSplit()) {
343       newState = State.SPLIT;
344       serverName = null;
345     }
346     String encodedName = hri.getEncodedName();
347     RegionState regionState = regionStates.get(encodedName);
348     if (regionState != null) {
349       LOG.warn("Tried to create a state for a region already in RegionStates, "
350         + "used existing: " + regionState + ", ignored new: " + newState);
351     } else {
352       regionState = new RegionState(hri, newState, serverName);
353       putRegionState(regionState);
354       if (newState == State.OPEN) {
355         if (!serverName.equals(lastHost)) {
356           LOG.warn("Open region's last host " + lastHost
357             + " should be the same as the current one " + serverName
358             + ", ignored the last and used the current one");
359           lastHost = serverName;
360         }
361         lastAssignments.put(encodedName, lastHost);
362         regionAssignments.put(hri, lastHost);
363       } else if (!regionState.isUnassignable()) {
364         regionsInTransition.put(encodedName, regionState);
365       }
366       if (lastHost != null && newState != State.SPLIT) {
367         addToReplicaMapping(hri);
368         addToServerHoldings(lastHost, hri);
369         if (newState != State.OPEN) {
370           oldAssignments.put(encodedName, lastHost);
371         }
372       }
373     }
374     return regionState;
375   }
376 
377   private RegionState putRegionState(RegionState regionState) {
378     HRegionInfo hri = regionState.getRegion();
379     String encodedName = hri.getEncodedName();
380     TableName table = hri.getTable();
381     RegionState oldState = regionStates.put(encodedName, regionState);
382     Map<String, RegionState> map = regionStatesTableIndex.get(table);
383     if (map == null) {
384       map = new HashMap<String, RegionState>();
385       regionStatesTableIndex.put(table, map);
386     }
387     map.put(encodedName, regionState);
388     return oldState;
389   }
390 
391   /**
392    * Set the region state to CLOSED
393    */
394   public RegionState setRegionStateTOCLOSED(
395       final byte[] regionName,
396       final ServerName serverName) {
397     HRegionInfo regionInfo = getRegionInfo(regionName);
398     return setRegionStateTOCLOSED(regionInfo, serverName);
399   }
400 
401   /**
402    * Set the region state to CLOSED
403    */
404   public RegionState setRegionStateTOCLOSED(
405       final HRegionInfo regionInfo,
406       final ServerName serverName) {
407     ServerName sn = serverName;
408     if (sn == null) {
409       RegionState regionState = getRegionState(regionInfo.getEncodedName());
410       if (regionState != null) {
411         sn = regionState.getServerName();
412       }
413       // TODO: if sn is null, should we dig into
414       // lastAssignments.get(regionInfo.getEncodedName() to get the server name?
415       // For now, I just keep the same logic that works in the past
416     }
417     // We have to make sure that the last region server is set to be the same as the
418     // current RS.  If we don't do that, we could run into situation that both AM and SSH
419     // think other would do the assignment work; at the end, neither does the work and
420     // region remains RIT.
421     // See HBASE-13330 and HBASE-17023
422     setLastRegionServerOfRegion(sn, regionInfo.getEncodedName());
423     return updateRegionState(regionInfo, State.CLOSED, sn);
424   }
425 
426   /**
427    * Update a region state. It will be put in transition if not already there.
428    */
429   public RegionState updateRegionState(
430       final HRegionInfo hri, final State state) {
431     RegionState regionState = getRegionState(hri.getEncodedName());
432     return updateRegionState(hri, state,
433       regionState == null ? null : regionState.getServerName());
434   }
435 
436   /**
437    * Update a region state. It will be put in transition if not already there.
438    *
439    * If we can't find the region info based on the region name in
440    * the transition, log a warning and return null.
441    */
442   public RegionState updateRegionState(
443       final RegionTransition transition, final State state) {
444     byte [] regionName = transition.getRegionName();
445     HRegionInfo regionInfo = getRegionInfo(regionName);
446     if (regionInfo == null) {
447       String prettyRegionName = HRegionInfo.prettyPrint(
448         HRegionInfo.encodeRegionName(regionName));
449       LOG.warn("Failed to find region " + prettyRegionName
450         + " in updating its state to " + state
451         + " based on region transition " + transition);
452       return null;
453     }
454     return updateRegionState(regionInfo, state,
455       transition.getServerName());
456   }
457 
458   /**
459    * Transition a region state to OPEN from OPENING/PENDING_OPEN
460    */
461   public synchronized RegionState transitionOpenFromPendingOpenOrOpeningOnServer(
462       final RegionTransition transition, final RegionState fromState, final ServerName sn) {
463     if(fromState.isPendingOpenOrOpeningOnServer(sn)){
464       return updateRegionState(transition, State.OPEN);
465     }
466     return null;
467   }
468 
469   /**
470    * Update a region state. It will be put in transition if not already there.
471    */
472   public RegionState updateRegionState(
473       final HRegionInfo hri, final State state, final ServerName serverName) {
474     return updateRegionState(hri, state, serverName, HConstants.NO_SEQNUM);
475   }
476 
477   public void regionOnline(final HRegionInfo hri, final ServerName serverName) {
478     regionOnline(hri, serverName, HConstants.NO_SEQNUM);
479   }
480 
481   /**
482    * A region is online, won't be in transition any more.
483    * We can't confirm it is really online on specified region server
484    * because it hasn't been put in region server's online region list yet.
485    */
486   public void regionOnline(final HRegionInfo hri, final ServerName serverName, long openSeqNum) {
487     String encodedName = hri.getEncodedName();
488     if (!serverManager.isServerOnline(serverName)) {
489       // This is possible if the region server dies before master gets a
490       // chance to handle ZK event in time. At this time, if the dead server
491       // is already processed by SSH, we should ignore this event.
492       // If not processed yet, ignore and let SSH deal with it.
493       LOG.warn("Ignored, " + encodedName + " was opened on a dead server: " + serverName);
494       return;
495     }
496     updateRegionState(hri, State.OPEN, serverName, openSeqNum);
497 
498     synchronized (this) {
499       RegionState regionState = regionsInTransition.remove(encodedName);
500       // When region is online and remove from regionsInTransition,
501       // update the RIT duration to assignment manager metrics
502       if (regionState != null && this.server.getAssignmentManager() != null) {
503         long ritDuration = System.currentTimeMillis() - regionState.getStamp()
504             + regionState.getRitDuration();
505         this.server.getAssignmentManager().getAssignmentManagerMetrics()
506             .updateRitDuration(ritDuration);
507       }
508       ServerName oldServerName = regionAssignments.put(hri, serverName);
509       if (!serverName.equals(oldServerName)) {
510         if (LOG.isDebugEnabled()) {
511           LOG.debug("Onlined " + hri.getShortNameToLog() + " on " + serverName);
512         }
513         addToServerHoldings(serverName, hri);
514         addToReplicaMapping(hri);
515         if (oldServerName == null) {
516           oldServerName = oldAssignments.remove(encodedName);
517         }
518         if (oldServerName != null
519             && !oldServerName.equals(serverName)
520             && serverHoldings.containsKey(oldServerName)) {
521           LOG.info("Offlined " + hri.getShortNameToLog() + " from " + oldServerName);
522           removeFromServerHoldings(oldServerName, hri);
523         }
524       }
525     }
526   }
527 
528   private void addToServerHoldings(ServerName serverName, HRegionInfo hri) {
529     Set<HRegionInfo> regions = serverHoldings.get(serverName);
530     if (regions == null) {
531       regions = new HashSet<HRegionInfo>();
532       serverHoldings.put(serverName, regions);
533     }
534     regions.add(hri);
535   }
536 
537   private void addToReplicaMapping(HRegionInfo hri) {
538     HRegionInfo defaultReplica = RegionReplicaUtil.getRegionInfoForDefaultReplica(hri);
539     Set<HRegionInfo> replicas =
540         defaultReplicaToOtherReplicas.get(defaultReplica);
541     if (replicas == null) {
542       replicas = new HashSet<HRegionInfo>();
543       defaultReplicaToOtherReplicas.put(defaultReplica, replicas);
544     }
545     replicas.add(hri);
546   }
547 
548   private void removeFromServerHoldings(ServerName serverName, HRegionInfo hri) {
549     Set<HRegionInfo> oldRegions = serverHoldings.get(serverName);
550     oldRegions.remove(hri);
551     if (oldRegions.isEmpty()) {
552       serverHoldings.remove(serverName);
553     }
554   }
555 
556   private void removeFromReplicaMapping(HRegionInfo hri) {
557     HRegionInfo defaultReplica = RegionReplicaUtil.getRegionInfoForDefaultReplica(hri);
558     Set<HRegionInfo> replicas = defaultReplicaToOtherReplicas.get(defaultReplica);
559     if (replicas != null) {
560       replicas.remove(hri);
561       if (replicas.isEmpty()) {
562         defaultReplicaToOtherReplicas.remove(defaultReplica);
563       }
564     }
565   }
566 
567   /**
568    * Used in some unit tests
569    */
570   @VisibleForTesting
571   synchronized boolean existsInServerHoldings(final ServerName serverName,
572       final HRegionInfo hri) {
573     Set<HRegionInfo> oldRegions = serverHoldings.get(serverName);
574     if (oldRegions != null) {
575       return oldRegions.contains(hri);
576     }
577     return false;
578   }
579 
580   /**
581    * A dead server's wals have been split so that all the regions
582    * used to be open on it can be safely assigned now. Mark them assignable.
583    */
584   public synchronized void logSplit(final ServerName serverName) {
585     for (Iterator<Map.Entry<String, ServerName>> it
586         = lastAssignments.entrySet().iterator(); it.hasNext();) {
587       Map.Entry<String, ServerName> e = it.next();
588       if (e.getValue().equals(serverName)) {
589         it.remove();
590       }
591     }
592     long now = System.currentTimeMillis();
593     if (LOG.isDebugEnabled()) {
594       LOG.debug("Adding to log splitting servers " + serverName);
595     }
596     processedServers.put(serverName, Long.valueOf(now));
597     Configuration conf = server.getConfiguration();
598     long obsoleteTime = conf.getLong(LOG_SPLIT_TIME, DEFAULT_LOG_SPLIT_TIME);
599     // Doesn't have to be very accurate about the clean up time
600     if (now > lastProcessedServerCleanTime + obsoleteTime) {
601       lastProcessedServerCleanTime = now;
602       long cutoff = now - obsoleteTime;
603       for (Iterator<Map.Entry<ServerName, Long>> it
604           = processedServers.entrySet().iterator(); it.hasNext();) {
605         Map.Entry<ServerName, Long> e = it.next();
606         if (e.getValue().longValue() < cutoff) {
607           if (LOG.isDebugEnabled()) {
608             LOG.debug("Removed from log splitting servers " + e.getKey());
609           }
610           it.remove();
611         }
612       }
613     }
614   }
615 
616   /**
617    * Log split is done for a given region, so it is assignable now.
618    */
619   public void logSplit(final HRegionInfo region) {
620     clearLastAssignment(region);
621   }
622 
623   public synchronized void clearLastAssignment(final HRegionInfo region) {
624     lastAssignments.remove(region.getEncodedName());
625   }
626 
627   /**
628    * A region is offline, won't be in transition any more.
629    */
630   public void regionOffline(final HRegionInfo hri) {
631     regionOffline(hri, null);
632   }
633 
634   /**
635    * A region is offline, won't be in transition any more. Its state
636    * should be the specified expected state, which can only be
637    * Split/Merged/Offline/null(=Offline)/SplittingNew/MergingNew.
638    */
639   public void regionOffline(
640       final HRegionInfo hri, final State expectedState) {
641     Preconditions.checkArgument(expectedState == null
642       || RegionState.isUnassignable(expectedState),
643         "Offlined region should not be " + expectedState);
644     if (isRegionInState(hri, State.SPLITTING_NEW, State.MERGING_NEW)) {
645       // Remove it from all region maps
646       deleteRegion(hri);
647       return;
648     }
649 
650     /*
651      * One tricky case, if region here is a replica region and its parent is at
652      * SPLIT state, its newState should be same as its parent, not OFFLINE.
653      */
654     State newState =
655         expectedState == null ? State.OFFLINE : expectedState;
656 
657     if ((expectedState == null) && !RegionReplicaUtil.isDefaultReplica(hri)) {
658       RegionState primateState = getRegionState(
659           RegionReplicaUtil.getRegionInfoForDefaultReplica(hri));
660       if ((primateState != null) && (primateState.getState() == State.SPLIT)) {
661         if (LOG.isDebugEnabled()) {
662           LOG.debug("Update region " + hri + "to SPLIT, from primary region " +
663               RegionReplicaUtil.getRegionInfoForDefaultReplica(hri));
664         }
665         newState = State.SPLIT;
666       }
667     }
668 
669     updateRegionState(hri, newState);
670     String encodedName = hri.getEncodedName();
671     synchronized (this) {
672       regionsInTransition.remove(encodedName);
673       ServerName oldServerName = regionAssignments.remove(hri);
674       if (oldServerName != null && serverHoldings.containsKey(oldServerName)) {
675         if (newState == State.MERGED || newState == State.SPLIT
676             || hri.isMetaRegion() || tableStateManager.isTableState(hri.getTable(),
677               ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
678           // Offline the region only if it's merged/split, or the table is disabled/disabling.
679           // Otherwise, offline it from this server only when it is online on a different server.
680           LOG.info("Offlined " + hri.getShortNameToLog() + " from " + oldServerName);
681           removeFromServerHoldings(oldServerName, hri);
682           removeFromReplicaMapping(hri);
683         } else {
684           // Need to remember it so that we can offline it from this
685           // server when it is online on a different server.
686           oldAssignments.put(encodedName, oldServerName);
687         }
688       }
689     }
690   }
691 
692   /**
693    * A server is offline, all regions on it are dead.
694    */
695   public List<HRegionInfo> serverOffline(final ZooKeeperWatcher watcher, final ServerName sn) {
696     // Offline all regions on this server not already in transition.
697     List<HRegionInfo> rits = new ArrayList<HRegionInfo>();
698     Set<HRegionInfo> regionsToClean = new HashSet<HRegionInfo>();
699     // Offline regions outside the loop and synchronized block to avoid
700     // ConcurrentModificationException and deadlock in case of meta anassigned,
701     // but RegionState a blocked.
702     Set<HRegionInfo> regionsToOffline = new HashSet<HRegionInfo>();
703     synchronized (this) {
704       Set<HRegionInfo> assignedRegions = serverHoldings.get(sn);
705       if (assignedRegions == null) {
706         assignedRegions = new HashSet<HRegionInfo>();
707       }
708 
709       for (HRegionInfo region : assignedRegions) {
710         // Offline open regions, no need to offline if SPLIT/MERGED/OFFLINE
711         if (isRegionOnline(region)) {
712           regionsToOffline.add(region);
713         } else if (isRegionInState(region, State.SPLITTING, State.MERGING)) {
714           LOG.debug("Offline splitting/merging region " + getRegionState(region));
715           try {
716             // Delete the ZNode if exists
717             ZKAssign.deleteNodeFailSilent(watcher, region);
718             regionsToOffline.add(region);
719           } catch (KeeperException ke) {
720             server.abort("Unexpected ZK exception deleting node " + region, ke);
721           }
722         }
723       }
724 
725       for (RegionState state : regionsInTransition.values()) {
726         HRegionInfo hri = state.getRegion();
727         if (assignedRegions.contains(hri)) {
728           // Region is open on this region server, but in transition.
729           // This region must be moving away from this server, or splitting/merging.
730           // SSH will handle it, either skip assigning, or re-assign.
731           LOG.info("Transitioning " + state + " will be handled by ServerCrashProcedure for " + sn);
732         } else if (sn.equals(state.getServerName())) {
733           // Region is in transition on this region server, and this
734           // region is not open on this server. So the region must be
735           // moving to this server from another one (i.e. opening or
736           // pending open on this server, was open on another one.
737           // Offline state is also kind of pending open if the region is in
738           // transition. The region could be in failed_close state too if we have
739           // tried several times to open it while this region server is not reachable)
740           if (state.isPendingOpenOrOpening() || state.isFailedClose() || state.isOffline()) {
741             LOG.info("Found region in " + state +
742               " to be reassigned by ServerCrashProcedure for " + sn);
743             rits.add(hri);
744           } else if(state.isSplittingNew() || state.isMergingNew()) {
745             LOG.info("Offline/Cleanup region if no meta entry exists, hri: " + hri +
746                 " state: " + state);
747             regionsToClean.add(state.getRegion());
748           } else {
749             LOG.warn("THIS SHOULD NOT HAPPEN: unexpected " + state);
750           }
751         }
752       }
753       this.notifyAll();
754     }
755 
756     for (HRegionInfo hri : regionsToOffline) {
757       regionOffline(hri);
758     }
759 
760     cleanFailedSplitMergeRegions(regionsToClean);
761     return rits;
762   }
763 
764   /**
765    * This method does an RPC to hbase:meta. Do not call this method with a lock/synchronize held.
766    * In ZK mode we rollback and hence cleanup daughters/merged region. We also cleanup if
767    * meta doesn't have these regions.
768    *
769    * @param hris The hris to check if empty in hbase:meta and if so, clean them up.
770    */
771   private void cleanFailedSplitMergeRegions(Set<HRegionInfo> hris) {
772     if (hris.isEmpty()) {
773       return;
774     }
775 
776     for (HRegionInfo hri : hris) {
777       // This is RPC to meta table. It is done while we have a synchronize on
778       // regionstates. No progress will be made if meta is not available at this time.
779       // This is a cleanup task. Not critical.
780       try {
781         Pair<HRegionInfo, ServerName> regionPair =
782             MetaTableAccessor.getRegion(server.getConnection(), hri.getRegionName());
783         if (regionPair == null || useZK) {
784           regionOffline(hri);
785 
786           // If we use ZK, then we can cleanup entries from meta, since we roll back.
787           if (regionPair != null) {
788             MetaTableAccessor.deleteRegion(this.server.getConnection(), hri);
789           }
790           LOG.debug("Cleaning up HDFS since no meta entry exists, hri: " + hri);
791           FSUtils.deleteRegionDir(server.getConfiguration(), hri);
792         }
793       } catch (IOException e) {
794         LOG.warn("Got exception while cleaning up region " + hri, e);
795       }
796     }
797   }
798 
799   /**
800    * Gets the online regions of the specified table.
801    * This method looks at the in-memory state.  It does not go to <code>hbase:meta</code>.
802    * Only returns <em>online</em> regions.  If a region on this table has been
803    * closed during a disable, etc., it will be included in the returned list.
804    * So, the returned list may not necessarily be ALL regions in this table, its
805    * all the ONLINE regions in the table.
806    * @param tableName
807    * @return Online regions from <code>tableName</code>
808    */
809   public synchronized List<HRegionInfo> getRegionsOfTable(TableName tableName) {
810     List<HRegionInfo> tableRegions = new ArrayList<HRegionInfo>();
811     // boundary needs to have table's name but regionID 0 so that it is sorted
812     // before all table's regions.
813     HRegionInfo boundary = new HRegionInfo(tableName, null, null, false, 0L);
814     for (HRegionInfo hri: regionAssignments.tailMap(boundary).keySet()) {
815       if(!hri.getTable().equals(tableName)) break;
816       tableRegions.add(hri);
817     }
818     return tableRegions;
819   }
820 
821   /**
822    * Gets current state of all regions of the table.
823    * This method looks at the in-memory state.  It does not go to <code>hbase:meta</code>.
824    * Method guaranteed to return keys for all states
825    * in {@link org.apache.hadoop.hbase.master.RegionState.State}
826    *
827    * @param tableName
828    * @return Online regions from <code>tableName</code>
829    */
830   public synchronized Map<RegionState.State, List<HRegionInfo>>
831   getRegionByStateOfTable(TableName tableName) {
832     Map<RegionState.State, List<HRegionInfo>> tableRegions =
833         new HashMap<State, List<HRegionInfo>>();
834     for (State state : State.values()) {
835       tableRegions.put(state, new ArrayList<HRegionInfo>());
836     }
837     Map<String, RegionState> indexMap = regionStatesTableIndex.get(tableName);
838     if (indexMap == null)
839       return tableRegions;
840     for (RegionState regionState : indexMap.values()) {
841       tableRegions.get(regionState.getState()).add(regionState.getRegion());
842     }
843     return tableRegions;
844   }
845 
846   /**
847    * Wait on region to clear regions-in-transition.
848    * <p>
849    * If the region isn't in transition, returns immediately.  Otherwise, method
850    * blocks until the region is out of transition.
851    */
852   public synchronized void waitOnRegionToClearRegionsInTransition(
853       final HRegionInfo hri) throws InterruptedException {
854     if (!isRegionInTransition(hri)) return;
855 
856     while(!server.isStopped() && isRegionInTransition(hri)) {
857       RegionState rs = getRegionState(hri);
858       LOG.info("Waiting on " + rs + " to clear regions-in-transition");
859       waitForUpdate(100);
860     }
861 
862     if (server.isStopped()) {
863       LOG.info("Giving up wait on region in " +
864         "transition because stoppable.isStopped is set");
865     }
866   }
867 
868   /**
869    * A table is deleted. Remove its regions from all internal maps.
870    * We loop through all regions assuming we don't delete tables too much.
871    */
872   public void tableDeleted(final TableName tableName) {
873     Set<HRegionInfo> regionsToDelete = new HashSet<HRegionInfo>();
874     synchronized (this) {
875       for (RegionState state: regionStates.values()) {
876         HRegionInfo region = state.getRegion();
877         if (region.getTable().equals(tableName)) {
878           regionsToDelete.add(region);
879         }
880       }
881     }
882     for (HRegionInfo region: regionsToDelete) {
883       deleteRegion(region);
884     }
885   }
886 
887   /**
888    * Get a copy of all regions assigned to a server
889    */
890   public synchronized Set<HRegionInfo> getServerRegions(ServerName serverName) {
891     Set<HRegionInfo> regions = serverHoldings.get(serverName);
892     if (regions == null) return null;
893     return new HashSet<HRegionInfo>(regions);
894   }
895 
896   /**
897    * Remove a region from all state maps.
898    */
899   @VisibleForTesting
900   public synchronized void deleteRegion(final HRegionInfo hri) {
901     String encodedName = hri.getEncodedName();
902     regionsInTransition.remove(encodedName);
903     regionStates.remove(encodedName);
904     TableName table = hri.getTable();
905     Map<String, RegionState> indexMap = regionStatesTableIndex.get(table);
906     indexMap.remove(encodedName);
907     if (indexMap.size() == 0)
908       regionStatesTableIndex.remove(table);
909     lastAssignments.remove(encodedName);
910     ServerName sn = regionAssignments.remove(hri);
911     if (sn != null) {
912       Set<HRegionInfo> regions = serverHoldings.get(sn);
913       regions.remove(hri);
914     }
915   }
916 
917   @VisibleForTesting
918   public boolean isRegionInRegionStates(final HRegionInfo hri) {
919     return (getRegionState(hri) != null || isRegionOnline(hri)) || isRegionInTransition(hri)
920         || isRegionInState(hri, State.OFFLINE, State.CLOSED);
921      }
922 
923   /**
924    * Checking if a region was assigned to a server which is not online now.
925    * If so, we should hold re-assign this region till SSH has split its wals.
926    * Once logs are split, the last assignment of this region will be reset,
927    * which means a null last assignment server is ok for re-assigning.
928    *
929    * A region server could be dead but we don't know it yet. We may
930    * think it's online falsely. Therefore if a server is online, we still
931    * need to confirm it reachable and having the expected start code.
932    */
933   synchronized boolean wasRegionOnDeadServer(final String encodedName) {
934     ServerName server = lastAssignments.get(encodedName);
935     return isServerDeadAndNotProcessed(server);
936   }
937 
938   synchronized boolean isServerDeadAndNotProcessed(ServerName server) {
939     if (server == null) return false;
940     if (serverManager.isServerOnline(server)) {
941       String hostAndPort = server.getHostAndPort();
942       long startCode = server.getStartcode();
943       Long deadCode = deadServers.get(hostAndPort);
944       if (deadCode == null || startCode > deadCode.longValue()) {
945         if (serverManager.isServerReachable(server)) {
946           return false;
947         }
948         // The size of deadServers won't grow unbounded.
949         deadServers.put(hostAndPort, Long.valueOf(startCode));
950       }
951       // Watch out! If the server is not dead, the region could
952       // remain unassigned. That's why ServerManager#isServerReachable
953       // should use some retry.
954       //
955       // We cache this info since it is very unlikely for that
956       // instance to come back up later on. We don't want to expire
957       // the server since we prefer to let it die naturally.
958       LOG.warn("Couldn't reach online server " + server);
959     }
960     // Now, we know it's dead. Check if it's processed
961     return !processedServers.containsKey(server);
962   }
963 
964  /**
965    * Get the last region server a region was on for purpose of re-assignment,
966    * i.e. should the re-assignment be held back till log split is done?
967    */
968   synchronized ServerName getLastRegionServerOfRegion(final String encodedName) {
969     return lastAssignments.get(encodedName);
970   }
971 
972   synchronized void setLastRegionServerOfRegions(
973       final ServerName serverName, final List<HRegionInfo> regionInfos) {
974     for (HRegionInfo hri: regionInfos) {
975       setLastRegionServerOfRegion(serverName, hri.getEncodedName());
976     }
977   }
978 
979   synchronized void setLastRegionServerOfRegion(
980       final ServerName serverName, final String encodedName) {
981     lastAssignments.put(encodedName, serverName);
982   }
983 
984   void splitRegion(HRegionInfo p,
985       HRegionInfo a, HRegionInfo b, ServerName sn) throws IOException {
986 
987     regionStateStore.splitRegion(p, a, b, sn, getRegionReplication(p));
988     synchronized (this) {
989       // After PONR, split is considered to be done.
990       // Update server holdings to be aligned with the meta.
991       Set<HRegionInfo> regions = serverHoldings.get(sn);
992       if (regions == null) {
993         throw new IllegalStateException(sn + " should host some regions");
994       }
995       regions.remove(p);
996       regions.add(a);
997       regions.add(b);
998     }
999   }
1000 
1001   void mergeRegions(HRegionInfo p,
1002       HRegionInfo a, HRegionInfo b, ServerName sn) throws IOException {
1003     regionStateStore.mergeRegions(p, a, b, sn, getRegionReplication(a));
1004     synchronized (this) {
1005       // After PONR, merge is considered to be done.
1006       // Update server holdings to be aligned with the meta.
1007       Set<HRegionInfo> regions = serverHoldings.get(sn);
1008       if (regions == null) {
1009         throw new IllegalStateException(sn + " should host some regions");
1010       }
1011       regions.remove(a);
1012       regions.remove(b);
1013       regions.add(p);
1014     }
1015   }
1016 
1017   private int getRegionReplication(HRegionInfo r) throws IOException {
1018     if (tableStateManager != null) {
1019       HTableDescriptor htd = ((MasterServices)server).getTableDescriptors().get(r.getTable());
1020       if (htd != null) {
1021         return htd.getRegionReplication();
1022       }
1023     }
1024     return 1;
1025   }
1026 
1027   /**
1028    * At cluster clean re/start, mark all user regions closed except those of tables
1029    * that are excluded, such as disabled/disabling/enabling tables. All user regions
1030    * and their previous locations are returned.
1031    */
1032   synchronized Map<HRegionInfo, ServerName> closeAllUserRegions(Set<TableName> excludedTables) {
1033     boolean noExcludeTables = excludedTables == null || excludedTables.isEmpty();
1034     Set<HRegionInfo> toBeClosed = new HashSet<HRegionInfo>(regionStates.size());
1035     for(RegionState state: regionStates.values()) {
1036       HRegionInfo hri = state.getRegion();
1037       if (state.isSplit() || hri.isSplit()) {
1038         continue;
1039       }
1040       TableName tableName = hri.getTable();
1041       if (!TableName.META_TABLE_NAME.equals(tableName)
1042           && (noExcludeTables || !excludedTables.contains(tableName))) {
1043         toBeClosed.add(hri);
1044       }
1045     }
1046     Map<HRegionInfo, ServerName> allUserRegions =
1047       new HashMap<HRegionInfo, ServerName>(toBeClosed.size());
1048     for (HRegionInfo hri: toBeClosed) {
1049       RegionState regionState = updateRegionState(hri, State.CLOSED);
1050       allUserRegions.put(hri, regionState.getServerName());
1051     }
1052     return allUserRegions;
1053   }
1054 
1055   /**
1056    * Compute the average load across all region servers.
1057    * Currently, this uses a very naive computation - just uses the number of
1058    * regions being served, ignoring stats about number of requests.
1059    * @return the average load
1060    */
1061   protected synchronized double getAverageLoad() {
1062     int numServers = 0, totalLoad = 0;
1063     for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
1064       Set<HRegionInfo> regions = e.getValue();
1065       ServerName serverName = e.getKey();
1066       int regionCount = regions.size();
1067       if (serverManager.isServerOnline(serverName)) {
1068         totalLoad += regionCount;
1069         numServers++;
1070       }
1071     }
1072     if (numServers > 1) {
1073       // The master region server holds only a couple regions.
1074       // Don't consider this server in calculating the average load
1075       // if there are other region servers to avoid possible confusion.
1076       Set<HRegionInfo> hris = serverHoldings.get(server.getServerName());
1077       if (hris != null) {
1078         totalLoad -= hris.size();
1079         numServers--;
1080       }
1081     }
1082     return numServers == 0 ? 0.0 :
1083       (double)totalLoad / (double)numServers;
1084   }
1085 
1086   /**
1087    * This is an EXPENSIVE clone.  Cloning though is the safest thing to do.
1088    * Can't let out original since it can change and at least the load balancer
1089    * wants to iterate this exported list.  We need to synchronize on regions
1090    * since all access to this.servers is under a lock on this.regions.
1091    *
1092    * @return A clone of current assignments by table.
1093    */
1094   protected Map<TableName, Map<ServerName, List<HRegionInfo>>>
1095       getAssignmentsByTable() {
1096     Map<TableName, Map<ServerName, List<HRegionInfo>>> result =
1097       new HashMap<TableName, Map<ServerName,List<HRegionInfo>>>();
1098     synchronized (this) {
1099       if (!server.getConfiguration().getBoolean(
1100             HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, false)) {
1101         Map<ServerName, List<HRegionInfo>> svrToRegions =
1102           new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
1103         for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
1104           svrToRegions.put(e.getKey(), new ArrayList<HRegionInfo>(e.getValue()));
1105         }
1106         result.put(TableName.valueOf(HConstants.ENSEMBLE_TABLE_NAME), svrToRegions);
1107       } else {
1108         for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
1109           for (HRegionInfo hri: e.getValue()) {
1110             if (hri.isMetaRegion()) continue;
1111             TableName tablename = hri.getTable();
1112             Map<ServerName, List<HRegionInfo>> svrToRegions = result.get(tablename);
1113             if (svrToRegions == null) {
1114               svrToRegions = new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
1115               result.put(tablename, svrToRegions);
1116             }
1117             List<HRegionInfo> regions = svrToRegions.get(e.getKey());
1118             if (regions == null) {
1119               regions = new ArrayList<HRegionInfo>();
1120               svrToRegions.put(e.getKey(), regions);
1121             }
1122             regions.add(hri);
1123           }
1124         }
1125       }
1126     }
1127 
1128     Map<ServerName, ServerLoad>
1129       onlineSvrs = serverManager.getOnlineServers();
1130     // Take care of servers w/o assignments, and remove servers in draining mode
1131     List<ServerName> drainingServers = this.serverManager.getDrainingServersList();
1132     for (Map<ServerName, List<HRegionInfo>> map: result.values()) {
1133       for (ServerName svr: onlineSvrs.keySet()) {
1134         if (!map.containsKey(svr)) {
1135           map.put(svr, new ArrayList<HRegionInfo>());
1136         }
1137       }
1138       map.keySet().removeAll(drainingServers);
1139     }
1140     return result;
1141   }
1142 
1143   protected RegionState getRegionState(final HRegionInfo hri) {
1144     return getRegionState(hri.getEncodedName());
1145   }
1146 
1147   /**
1148    * Returns a clone of region assignments per server
1149    * @return a Map of ServerName to a List of HRegionInfo's
1150    */
1151   protected synchronized Map<ServerName, List<HRegionInfo>> getRegionAssignmentsByServer() {
1152     Map<ServerName, List<HRegionInfo>> regionsByServer =
1153         new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
1154     for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
1155       regionsByServer.put(e.getKey(), new ArrayList<HRegionInfo>(e.getValue()));
1156     }
1157     return regionsByServer;
1158   }
1159 
1160   protected synchronized RegionState getRegionState(final String encodedName) {
1161     return regionStates.get(encodedName);
1162   }
1163 
1164   /**
1165    * Get the HRegionInfo from cache, if not there, from the hbase:meta table
1166    * @param  regionName
1167    * @return HRegionInfo for the region
1168    */
1169   @SuppressWarnings("deprecation")
1170   protected HRegionInfo getRegionInfo(final byte [] regionName) {
1171     String encodedName = HRegionInfo.encodeRegionName(regionName);
1172     RegionState regionState = getRegionState(encodedName);
1173     if (regionState != null) {
1174       return regionState.getRegion();
1175     }
1176 
1177     try {
1178       Pair<HRegionInfo, ServerName> p =
1179         MetaTableAccessor.getRegion(server.getConnection(), regionName);
1180       HRegionInfo hri = p == null ? null : p.getFirst();
1181       if (hri != null) {
1182         createRegionState(hri);
1183       }
1184       return hri;
1185     } catch (IOException e) {
1186       server.abort("Aborting because error occurred while reading "
1187         + Bytes.toStringBinary(regionName) + " from hbase:meta", e);
1188       return null;
1189     }
1190   }
1191 
1192   static boolean isOneOfStates(RegionState regionState, State... states) {
1193     State s = regionState != null ? regionState.getState() : null;
1194     for (State state: states) {
1195       if (s == state) return true;
1196     }
1197     return false;
1198   }
1199 
1200   /**
1201    * Update a region state. It will be put in transition if not already there.
1202    */
1203   private RegionState updateRegionState(final HRegionInfo hri,
1204       final State state, final ServerName serverName, long openSeqNum) {
1205     if (state == State.FAILED_CLOSE || state == State.FAILED_OPEN) {
1206       LOG.warn("Failed to open/close " + hri.getShortNameToLog()
1207         + " on " + serverName + ", set to " + state);
1208     }
1209 
1210     String encodedName = hri.getEncodedName();
1211     RegionState regionState = new RegionState(
1212       hri, state, System.currentTimeMillis(), serverName);
1213     RegionState oldState = getRegionState(encodedName);
1214     if (!regionState.equals(oldState)) {
1215       LOG.info("Transition " + oldState + " to " + regionState);
1216       // Persist region state before updating in-memory info, if needed
1217       regionStateStore.updateRegionState(openSeqNum, regionState, oldState);
1218     }
1219 
1220     synchronized (this) {
1221       RegionState oldRegionState = regionsInTransition.put(encodedName, regionState);
1222       // When region transform old region state to new region state,
1223       // accumulate the RIT duration to new region state.
1224       if (oldRegionState != null) {
1225         regionState.updateRitDuration(oldRegionState.getStamp());
1226       }
1227       putRegionState(regionState);
1228 
1229       // For these states, region should be properly closed.
1230       // There should be no log splitting issue.
1231       if ((state == State.CLOSED || state == State.MERGED
1232           || state == State.SPLIT) && lastAssignments.containsKey(encodedName)) {
1233         ServerName last = lastAssignments.get(encodedName);
1234         if (last.equals(serverName)) {
1235           lastAssignments.remove(encodedName);
1236         } else {
1237           LOG.warn(encodedName + " moved to " + state + " on "
1238             + serverName + ", expected " + last);
1239         }
1240       }
1241 
1242       // Once a region is opened, record its last assignment right away.
1243       if (serverName != null && state == State.OPEN) {
1244         ServerName last = lastAssignments.get(encodedName);
1245         if (!serverName.equals(last)) {
1246           lastAssignments.put(encodedName, serverName);
1247           if (last != null && isServerDeadAndNotProcessed(last)) {
1248             LOG.warn(encodedName + " moved to " + serverName
1249               + ", while it's previous host " + last
1250               + " is dead but not processed yet");
1251           }
1252         }
1253       }
1254 
1255       // notify the change
1256       this.notifyAll();
1257     }
1258     return regionState;
1259   }
1260 }