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