View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NavigableMap;
33  import java.util.Random;
34  import java.util.Set;
35  import java.util.TreeMap;
36  import java.util.concurrent.Callable;
37  import java.util.concurrent.ConcurrentHashMap;
38  import java.util.concurrent.CopyOnWriteArrayList;
39  import java.util.concurrent.ThreadFactory;
40  import java.util.concurrent.TimeUnit;
41  import java.util.concurrent.atomic.AtomicBoolean;
42  import java.util.concurrent.atomic.AtomicInteger;
43  import java.util.concurrent.locks.Lock;
44  import java.util.concurrent.locks.ReentrantLock;
45  
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  import org.apache.hadoop.conf.Configuration;
49  import org.apache.hadoop.fs.FileSystem;
50  import org.apache.hadoop.fs.Path;
51  import org.apache.hadoop.hbase.CoordinatedStateException;
52  import org.apache.hadoop.hbase.HBaseIOException;
53  import org.apache.hadoop.hbase.HConstants;
54  import org.apache.hadoop.hbase.HRegionInfo;
55  import org.apache.hadoop.hbase.HRegionLocation;
56  import org.apache.hadoop.hbase.HTableDescriptor;
57  import org.apache.hadoop.hbase.MetaTableAccessor;
58  import org.apache.hadoop.hbase.NotServingRegionException;
59  import org.apache.hadoop.hbase.RegionLocations;
60  import org.apache.hadoop.hbase.RegionStateListener;
61  import org.apache.hadoop.hbase.RegionTransition;
62  import org.apache.hadoop.hbase.ServerName;
63  import org.apache.hadoop.hbase.TableName;
64  import org.apache.hadoop.hbase.TableNotFoundException;
65  import org.apache.hadoop.hbase.TableStateManager;
66  import org.apache.hadoop.hbase.classification.InterfaceAudience;
67  import org.apache.hadoop.hbase.client.Admin;
68  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
69  import org.apache.hadoop.hbase.client.Result;
70  import org.apache.hadoop.hbase.coordination.BaseCoordinatedStateManager;
71  import org.apache.hadoop.hbase.coordination.OpenRegionCoordination;
72  import org.apache.hadoop.hbase.coordination.RegionMergeCoordination;
73  import org.apache.hadoop.hbase.coordination.SplitTransactionCoordination.SplitTransactionDetails;
74  import org.apache.hadoop.hbase.coordination.ZkOpenRegionCoordination;
75  import org.apache.hadoop.hbase.coordination.ZkRegionMergeCoordination;
76  import org.apache.hadoop.hbase.exceptions.DeserializationException;
77  import org.apache.hadoop.hbase.executor.EventHandler;
78  import org.apache.hadoop.hbase.executor.EventType;
79  import org.apache.hadoop.hbase.executor.ExecutorService;
80  import org.apache.hadoop.hbase.ipc.FailedServerException;
81  import org.apache.hadoop.hbase.ipc.RpcClient;
82  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
83  import org.apache.hadoop.hbase.master.RegionState.State;
84  import org.apache.hadoop.hbase.master.balancer.FavoredNodeAssignmentHelper;
85  import org.apache.hadoop.hbase.master.balancer.FavoredNodeLoadBalancer;
86  import org.apache.hadoop.hbase.master.handler.ClosedRegionHandler;
87  import org.apache.hadoop.hbase.master.handler.DisableTableHandler;
88  import org.apache.hadoop.hbase.master.handler.EnableTableHandler;
89  import org.apache.hadoop.hbase.master.handler.OpenedRegionHandler;
90  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionStateTransition;
91  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode;
92  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
93  import org.apache.hadoop.hbase.regionserver.RegionAlreadyInTransitionException;
94  import org.apache.hadoop.hbase.regionserver.RegionOpeningState;
95  import org.apache.hadoop.hbase.regionserver.RegionServerAbortedException;
96  import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
97  import org.apache.hadoop.hbase.util.ConfigUtil;
98  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
99  import org.apache.hadoop.hbase.util.FSUtils;
100 import org.apache.hadoop.hbase.util.KeyLocker;
101 import org.apache.hadoop.hbase.util.Pair;
102 import org.apache.hadoop.hbase.util.PairOfSameType;
103 import org.apache.hadoop.hbase.util.Threads;
104 import org.apache.hadoop.hbase.util.Triple;
105 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
106 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
107 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
108 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
109 import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
110 import org.apache.hadoop.ipc.RemoteException;
111 import org.apache.hadoop.util.StringUtils;
112 import org.apache.zookeeper.AsyncCallback;
113 import org.apache.zookeeper.KeeperException;
114 import org.apache.zookeeper.KeeperException.NoNodeException;
115 import org.apache.zookeeper.KeeperException.NodeExistsException;
116 import org.apache.zookeeper.data.Stat;
117 
118 import com.google.common.annotations.VisibleForTesting;
119 import com.google.common.collect.LinkedHashMultimap;
120 
121 /**
122  * Manages and performs region assignment.
123  * <p>
124  * Monitors ZooKeeper for events related to regions in transition.
125  * <p>
126  * Handles existing regions in transition during master failover.
127  */
128 @InterfaceAudience.Private
129 public class AssignmentManager extends ZooKeeperListener {
130   private static final Log LOG = LogFactory.getLog(AssignmentManager.class);
131 
132   public static final ServerName HBCK_CODE_SERVERNAME = ServerName.valueOf(HConstants.HBCK_CODE_NAME,
133       -1, -1L);
134 
135   static final String ALREADY_IN_TRANSITION_WAITTIME
136     = "hbase.assignment.already.intransition.waittime";
137   static final int DEFAULT_ALREADY_IN_TRANSITION_WAITTIME = 60000; // 1 minute
138 
139   protected final MasterServices server;
140 
141   private ServerManager serverManager;
142 
143   private boolean shouldAssignRegionsWithFavoredNodes;
144 
145   private LoadBalancer balancer;
146 
147   private final MetricsAssignmentManager metricsAssignmentManager;
148 
149   private final TableLockManager tableLockManager;
150 
151   private AtomicInteger numRegionsOpened = new AtomicInteger(0);
152 
153   final private KeyLocker<String> locker = new KeyLocker<String>();
154 
155   Set<HRegionInfo> replicasToClose = Collections.synchronizedSet(new HashSet<HRegionInfo>());
156 
157   /**
158    * Map of regions to reopen after the schema of a table is changed. Key -
159    * encoded region name, value - HRegionInfo
160    */
161   private final Map <String, HRegionInfo> regionsToReopen;
162 
163   /*
164    * Maximum times we recurse an assignment/unassignment.
165    * See below in {@link #assign()} and {@link #unassign()}.
166    */
167   private final int maximumAttempts;
168 
169   /**
170    * Map of two merging regions from the region to be created.
171    */
172   private final Map<String, PairOfSameType<HRegionInfo>> mergingRegions
173     = new HashMap<String, PairOfSameType<HRegionInfo>>();
174 
175   private final Map<HRegionInfo, PairOfSameType<HRegionInfo>> splitRegions
176   = new HashMap<HRegionInfo, PairOfSameType<HRegionInfo>>();
177 
178   /**
179    * The sleep time for which the assignment will wait before retrying in case of hbase:meta assignment
180    * failure due to lack of availability of region plan or bad region plan
181    */
182   private final long sleepTimeBeforeRetryingMetaAssignment;
183 
184   /** Plans for region movement. Key is the encoded version of a region name*/
185   // TODO: When do plans get cleaned out?  Ever? In server open and in server
186   // shutdown processing -- St.Ack
187   // All access to this Map must be synchronized.
188   final NavigableMap<String, RegionPlan> regionPlans =
189     new TreeMap<String, RegionPlan>();
190 
191   private final TableStateManager tableStateManager;
192 
193   private final ExecutorService executorService;
194 
195   // For unit tests, keep track of calls to ClosedRegionHandler
196   private Map<HRegionInfo, AtomicBoolean> closedRegionHandlerCalled = null;
197 
198   // For unit tests, keep track of calls to OpenedRegionHandler
199   private Map<HRegionInfo, AtomicBoolean> openedRegionHandlerCalled = null;
200 
201   //Thread pool executor service for timeout monitor
202   private java.util.concurrent.ExecutorService threadPoolExecutorService;
203 
204   // A bunch of ZK events workers. Each is a single thread executor service
205   private final java.util.concurrent.ExecutorService zkEventWorkers;
206 
207   private List<EventType> ignoreStatesRSOffline = Arrays.asList(
208       EventType.RS_ZK_REGION_FAILED_OPEN, EventType.RS_ZK_REGION_CLOSED);
209 
210   private final RegionStates regionStates;
211 
212   // The threshold to use bulk assigning. Using bulk assignment
213   // only if assigning at least this many regions to at least this
214   // many servers. If assigning fewer regions to fewer servers,
215   // bulk assigning may be not as efficient.
216   private final int bulkAssignThresholdRegions;
217   private final int bulkAssignThresholdServers;
218   private final int bulkPerRegionOpenTimeGuesstimate;
219 
220   // Should bulk assignment wait till all regions are assigned,
221   // or it is timed out?  This is useful to measure bulk assignment
222   // performance, but not needed in most use cases.
223   private final boolean bulkAssignWaitTillAllAssigned;
224 
225   /**
226    * Indicator that AssignmentManager has recovered the region states so
227    * that ServerShutdownHandler can be fully enabled and re-assign regions
228    * of dead servers. So that when re-assignment happens, AssignmentManager
229    * has proper region states.
230    *
231    * Protected to ease testing.
232    */
233   protected final AtomicBoolean failoverCleanupDone = new AtomicBoolean(false);
234 
235   /**
236    * A map to track the count a region fails to open in a row.
237    * So that we don't try to open a region forever if the failure is
238    * unrecoverable.  We don't put this information in region states
239    * because we don't expect this to happen frequently; we don't
240    * want to copy this information over during each state transition either.
241    */
242   private final ConcurrentHashMap<String, AtomicInteger>
243     failedOpenTracker = new ConcurrentHashMap<String, AtomicInteger>();
244 
245   // A flag to indicate if we are using ZK for region assignment
246   private final boolean useZKForAssignment;
247 
248   // In case not using ZK for region assignment, region states
249   // are persisted in meta with a state store
250   private final RegionStateStore regionStateStore;
251 
252   /**
253    * For testing only!  Set to true to skip handling of split and merge.
254    */
255   private static boolean TEST_SKIP_SPLIT_HANDLING = false;
256   private static boolean TEST_SKIP_MERGE_HANDLING = false;
257 
258   /** Listeners that are called on assignment events. */
259   private List<AssignmentListener> listeners = new CopyOnWriteArrayList<AssignmentListener>();
260 
261   private RegionStateListener regionStateListener;
262 
263   public enum ServerHostRegion {
264     NOT_HOSTING_REGION, HOSTING_REGION, UNKNOWN,
265   }
266 
267   /**
268    * Constructs a new assignment manager.
269    *
270    * @param server instance of HMaster this AM running inside
271    * @param serverManager serverManager for associated HMaster
272    * @param balancer implementation of {@link LoadBalancer}
273    * @param service Executor service
274    * @param metricsMaster metrics manager
275    * @param tableLockManager TableLock manager
276    * @throws KeeperException
277    * @throws IOException
278    */
279   public AssignmentManager(MasterServices server, ServerManager serverManager,
280       final LoadBalancer balancer,
281       final ExecutorService service, MetricsMaster metricsMaster,
282       final TableLockManager tableLockManager) throws KeeperException,
283         IOException, CoordinatedStateException {
284     super(server.getZooKeeper());
285     this.server = server;
286     this.serverManager = serverManager;
287     this.executorService = service;
288     this.regionStateStore = new RegionStateStore(server);
289     this.regionsToReopen = Collections.synchronizedMap
290                            (new HashMap<String, HRegionInfo> ());
291     Configuration conf = server.getConfiguration();
292     // Only read favored nodes if using the favored nodes load balancer.
293     this.shouldAssignRegionsWithFavoredNodes = conf.getClass(
294            HConstants.HBASE_MASTER_LOADBALANCER_CLASS, Object.class).equals(
295            FavoredNodeLoadBalancer.class);
296     try {
297       if (server.getCoordinatedStateManager() != null) {
298         this.tableStateManager = server.getCoordinatedStateManager().getTableStateManager();
299       } else {
300         this.tableStateManager = null;
301       }
302     } catch (InterruptedException e) {
303       throw new InterruptedIOException();
304     }
305     // This is the max attempts, not retries, so it should be at least 1.
306     this.maximumAttempts = Math.max(1,
307       this.server.getConfiguration().getInt("hbase.assignment.maximum.attempts", 10));
308     this.sleepTimeBeforeRetryingMetaAssignment = this.server.getConfiguration().getLong(
309         "hbase.meta.assignment.retry.sleeptime", 1000l);
310     this.balancer = balancer;
311     int maxThreads = conf.getInt("hbase.assignment.threads.max", 30);
312     this.threadPoolExecutorService = Threads.getBoundedCachedThreadPool(
313       maxThreads, 60L, TimeUnit.SECONDS, Threads.newDaemonThreadFactory("AM."));
314     this.regionStates = new RegionStates(
315       server, tableStateManager, serverManager, regionStateStore);
316 
317     this.bulkAssignWaitTillAllAssigned =
318       conf.getBoolean("hbase.bulk.assignment.waittillallassigned", false);
319     this.bulkAssignThresholdRegions = conf.getInt("hbase.bulk.assignment.threshold.regions", 7);
320     this.bulkAssignThresholdServers = conf.getInt("hbase.bulk.assignment.threshold.servers", 3);
321     this.bulkPerRegionOpenTimeGuesstimate =
322       conf.getInt("hbase.bulk.assignment.perregion.open.time", 10000);
323 
324     int workers = conf.getInt("hbase.assignment.zkevent.workers", 20);
325     ThreadFactory threadFactory = Threads.newDaemonThreadFactory("AM.ZK.Worker");
326     zkEventWorkers = Threads.getBoundedCachedThreadPool(workers, 60L,
327             TimeUnit.SECONDS, threadFactory);
328     this.tableLockManager = tableLockManager;
329 
330     this.metricsAssignmentManager = new MetricsAssignmentManager();
331     useZKForAssignment = ConfigUtil.useZKForAssignment(conf);
332   }
333 
334   MetricsAssignmentManager getAssignmentManagerMetrics() {
335     return this.metricsAssignmentManager;
336   }
337 
338   /**
339    * Add the listener to the notification list.
340    * @param listener The AssignmentListener to register
341    */
342   public void registerListener(final AssignmentListener listener) {
343     this.listeners.add(listener);
344   }
345 
346   /**
347    * Remove the listener from the notification list.
348    * @param listener The AssignmentListener to unregister
349    */
350   public boolean unregisterListener(final AssignmentListener listener) {
351     return this.listeners.remove(listener);
352   }
353 
354   /**
355    * @return Instance of ZKTableStateManager.
356    */
357   public TableStateManager getTableStateManager() {
358     // These are 'expensive' to make involving trip to zk ensemble so allow
359     // sharing.
360     return this.tableStateManager;
361   }
362 
363   /**
364    * This SHOULD not be public. It is public now
365    * because of some unit tests.
366    *
367    * TODO: make it package private and keep RegionStates in the master package
368    */
369   public RegionStates getRegionStates() {
370     return regionStates;
371   }
372 
373   /**
374    * Used in some tests to mock up region state in meta
375    */
376   @VisibleForTesting
377   RegionStateStore getRegionStateStore() {
378     return regionStateStore;
379   }
380 
381   public RegionPlan getRegionReopenPlan(HRegionInfo hri) {
382     return new RegionPlan(hri, null, regionStates.getRegionServerOfRegion(hri));
383   }
384 
385   /**
386    * Add a regionPlan for the specified region.
387    * @param encodedName
388    * @param plan
389    */
390   public void addPlan(String encodedName, RegionPlan plan) {
391     synchronized (regionPlans) {
392       regionPlans.put(encodedName, plan);
393     }
394   }
395 
396   /**
397    * Add a map of region plans.
398    */
399   public void addPlans(Map<String, RegionPlan> plans) {
400     synchronized (regionPlans) {
401       regionPlans.putAll(plans);
402     }
403   }
404 
405   /**
406    * Set the list of regions that will be reopened
407    * because of an update in table schema
408    *
409    * @param regions
410    *          list of regions that should be tracked for reopen
411    */
412   public void setRegionsToReopen(List <HRegionInfo> regions) {
413     for(HRegionInfo hri : regions) {
414       regionsToReopen.put(hri.getEncodedName(), hri);
415     }
416   }
417 
418   /**
419    * Used by the client to identify if all regions have the schema updates
420    *
421    * @param tableName
422    * @return Pair indicating the status of the alter command
423    * @throws IOException
424    */
425   public Pair<Integer, Integer> getReopenStatus(TableName tableName)
426       throws IOException {
427     List<HRegionInfo> hris;
428     if (TableName.META_TABLE_NAME.equals(tableName)) {
429       hris = new MetaTableLocator().getMetaRegions(server.getZooKeeper());
430     } else {
431       hris = MetaTableAccessor.getTableRegions(server.getZooKeeper(),
432         server.getConnection(), tableName, true);
433     }
434 
435     Integer pending = 0;
436     for (HRegionInfo hri : hris) {
437       String name = hri.getEncodedName();
438       // no lock concurrent access ok: sequential consistency respected.
439       if (regionsToReopen.containsKey(name)
440           || regionStates.isRegionInTransition(name)) {
441         pending++;
442       }
443     }
444     return new Pair<Integer, Integer>(pending, hris.size());
445   }
446 
447   /**
448    * Used by ServerShutdownHandler to make sure AssignmentManager has completed
449    * the failover cleanup before re-assigning regions of dead servers. So that
450    * when re-assignment happens, AssignmentManager has proper region states.
451    */
452   public boolean isFailoverCleanupDone() {
453     return failoverCleanupDone.get();
454   }
455 
456   /**
457    * To avoid racing with AM, external entities may need to lock a region,
458    * for example, when SSH checks what regions to skip re-assigning.
459    */
460   public Lock acquireRegionLock(final String encodedName) {
461     return locker.acquireLock(encodedName);
462   }
463 
464   /**
465    * Now, failover cleanup is completed. Notify server manager to
466    * process queued up dead servers processing, if any.
467    */
468   void failoverCleanupDone() {
469     failoverCleanupDone.set(true);
470     serverManager.processQueuedDeadServers();
471   }
472 
473   /**
474    * Called on startup.
475    * Figures whether a fresh cluster start of we are joining extant running cluster.
476    * @throws IOException
477    * @throws KeeperException
478    * @throws InterruptedException
479    * @throws CoordinatedStateException
480    */
481   void joinCluster() throws IOException,
482       KeeperException, InterruptedException, CoordinatedStateException {
483     long startTime = System.currentTimeMillis();
484     // Concurrency note: In the below the accesses on regionsInTransition are
485     // outside of a synchronization block where usually all accesses to RIT are
486     // synchronized.  The presumption is that in this case it is safe since this
487     // method is being played by a single thread on startup.
488 
489     // TODO: Regions that have a null location and are not in regionsInTransitions
490     // need to be handled.
491 
492     // Scan hbase:meta to build list of existing regions, servers, and assignment
493     // Returns servers who have not checked in (assumed dead) that some regions
494     // were assigned to (according to the meta)
495     Set<ServerName> deadServers = rebuildUserRegions();
496 
497     // This method will assign all user regions if a clean server startup or
498     // it will reconstruct master state and cleanup any leftovers from previous master process.
499     boolean failover = processDeadServersAndRegionsInTransition(deadServers);
500 
501     if (!useZKForAssignment) {
502       // Not use ZK for assignment any more, remove the ZNode
503       ZKUtil.deleteNodeRecursively(watcher, watcher.assignmentZNode);
504     }
505     recoverTableInDisablingState();
506     recoverTableInEnablingState();
507     LOG.info("Joined the cluster in " + (System.currentTimeMillis()
508       - startTime) + "ms, failover=" + failover);
509   }
510 
511   /**
512    * Process all regions that are in transition in zookeeper and also
513    * processes the list of dead servers.
514    * Used by master joining an cluster.  If we figure this is a clean cluster
515    * startup, will assign all user regions.
516    * @param deadServers Set of servers that are offline probably legitimately that were carrying
517    * regions according to a scan of hbase:meta. Can be null.
518    * @throws KeeperException
519    * @throws IOException
520    * @throws InterruptedException
521    */
522   boolean processDeadServersAndRegionsInTransition(final Set<ServerName> deadServers)
523   throws KeeperException, IOException, InterruptedException, CoordinatedStateException {
524     List<String> nodes = ZKUtil.listChildrenNoWatch(watcher, watcher.assignmentZNode);
525 
526     if (useZKForAssignment && nodes == null) {
527       String errorMessage = "Failed to get the children from ZK";
528       server.abort(errorMessage, new IOException(errorMessage));
529       return true; // Doesn't matter in this case
530     }
531 
532     boolean failover = !serverManager.getDeadServers().isEmpty();
533     if (failover) {
534       // This may not be a failover actually, especially if meta is on this master.
535       if (LOG.isDebugEnabled()) {
536         LOG.debug("Found dead servers out on cluster " + serverManager.getDeadServers());
537       }
538     } else {
539       // If any one region except meta is assigned, it's a failover.
540       Set<ServerName> onlineServers = serverManager.getOnlineServers().keySet();
541       for (Map.Entry<HRegionInfo, ServerName> en:
542           regionStates.getRegionAssignments().entrySet()) {
543         HRegionInfo hri = en.getKey();
544         if (!hri.isMetaTable()
545             && onlineServers.contains(en.getValue())) {
546           LOG.debug("Found " + hri + " out on cluster");
547           failover = true;
548           break;
549         }
550       }
551       if (!failover && nodes != null) {
552         // If any one region except meta is in transition, it's a failover.
553         for (String encodedName: nodes) {
554           RegionState regionState = regionStates.getRegionState(encodedName);
555           if (regionState != null && !regionState.getRegion().isMetaRegion()) {
556             LOG.debug("Found " + regionState + " in RITs");
557             failover = true;
558             break;
559           }
560         }
561       }
562     }
563     if (!failover && !useZKForAssignment) {
564       // If any region except meta is in transition on a live server, it's a failover.
565       Map<String, RegionState> regionsInTransition = regionStates.getRegionsInTransition();
566       if (!regionsInTransition.isEmpty()) {
567         Set<ServerName> onlineServers = serverManager.getOnlineServers().keySet();
568         for (RegionState regionState: regionsInTransition.values()) {
569           ServerName serverName = regionState.getServerName();
570           if (!regionState.getRegion().isMetaRegion()
571               && serverName != null && onlineServers.contains(serverName)) {
572             LOG.debug("Found " + regionState + " in RITs");
573             failover = true;
574             break;
575           }
576         }
577       }
578     }
579     if (!failover) {
580       // If we get here, we have a full cluster restart. It is a failover only
581       // if there are some WALs are not split yet. For meta WALs, they should have
582       // been split already, if any. We can walk through those queued dead servers,
583       // if they don't have any WALs, this restart should be considered as a clean one
584       Set<ServerName> queuedDeadServers = serverManager.getRequeuedDeadServers().keySet();
585       if (!queuedDeadServers.isEmpty()) {
586         Configuration conf = server.getConfiguration();
587         Path walRootDir = FSUtils.getWALRootDir(conf);
588         FileSystem walFs = FSUtils.getWALFileSystem(conf);
589         for (ServerName serverName: queuedDeadServers) {
590           // In the case of a clean exit, the shutdown handler would have presplit any WALs and
591           // removed empty directories.
592           Path walDir = new Path(walRootDir,
593               DefaultWALProvider.getWALDirectoryName(serverName.toString()));
594           Path splitDir = walDir.suffix(DefaultWALProvider.SPLITTING_EXT);
595           if (walFs.exists(walDir) || walFs.exists(splitDir)) {
596             LOG.debug("Found queued dead server " + serverName);
597             failover = true;
598             break;
599           }
600         }
601         if (!failover) {
602           // We figured that it's not a failover, so no need to
603           // work on these re-queued dead servers any more.
604           LOG.info("AM figured that it's not a failover and cleaned up "
605             + queuedDeadServers.size() + " queued dead servers");
606           serverManager.removeRequeuedDeadServers();
607         }
608       }
609     }
610 
611     Map<HRegionInfo, ServerName> allRegions = null;
612     if (!failover) {
613       // Retrieve user regions except tables region that are in disabled/disabling/enabling states.
614       allRegions = getUserRegionsToAssign();
615     }
616 
617     // Now region states are restored
618     regionStateStore.start();
619 
620     // If we found user regions out on cluster, its a failover.
621     if (failover) {
622       LOG.info("Found regions out on cluster or in RIT; presuming failover");
623       // Process list of dead servers and regions in RIT.
624       // See HBASE-4580 for more information.
625       processDeadServersAndRecoverLostRegions(deadServers);
626 
627       // Handle the scenario when meta is rebuild by OfflineMetaRepair tool.
628       // In this scenario, meta will have only info:regioninfo entries (won't contain info:server)
629       // which lead SSH/SCP to skip holding region assignment.
630       if (!MetaTableAccessor.infoServerExists(server.getConnection())) {
631         // Need to assign the user region as a fresh startup, otherwise user region assignment will
632         // never happen
633         assignRegionsOnSSHCompletion();
634       }
635     }
636 
637     if (!failover && useZKForAssignment) {
638       // Cleanup any existing ZK nodes and start watching
639       ZKAssign.deleteAllNodes(watcher);
640       ZKUtil.listChildrenAndWatchForNewChildren(this.watcher,
641         this.watcher.assignmentZNode);
642     }
643 
644     // Now we can safely claim failover cleanup completed and enable
645     // ServerShutdownHandler for further processing. The nodes (below)
646     // in transition, if any, are for regions not related to those
647     // dead servers at all, and can be done in parallel to SSH.
648     failoverCleanupDone();
649     if (!failover) {
650       // Fresh cluster startup.
651       LOG.info("Clean cluster startup. Assigning user regions");
652       assignAllUserRegions(allRegions);
653     }
654     // unassign replicas of the split parents and the merged regions
655     // the daughter replicas are opened in assignAllUserRegions if it was
656     // not already opened.
657     for (HRegionInfo h : replicasToClose) {
658       unassign(h);
659     }
660     replicasToClose.clear();
661     return failover;
662   }
663 
664   /*
665    * At cluster clean re/start, mark all user regions closed except those of tables that are
666    * excluded, such as disabled/disabling/enabling tables. All user regions and their previous
667    * locations are returned.
668    */
669   private Map<HRegionInfo, ServerName> getUserRegionsToAssign()
670       throws InterruptedIOException, CoordinatedStateException {
671     Set<TableName> disabledOrDisablingOrEnabling =
672         tableStateManager.getTablesInStates(ZooKeeperProtos.Table.State.DISABLED,
673           ZooKeeperProtos.Table.State.DISABLING, ZooKeeperProtos.Table.State.ENABLING);
674 
675     // Clean re/start, mark all user regions closed before reassignment
676     return regionStates.closeAllUserRegions(disabledOrDisablingOrEnabling);
677   }
678 
679   /*
680    * Wait for SSH completion and assign user region which are not in disabled/disabling/enabling
681    * table states.
682    */
683   private void assignRegionsOnSSHCompletion() {
684     LOG.info("Meta is rebuild by OfflineMetaRepair tool, assigning all user regions.");
685     Thread regionAssignerThread = new Thread("RegionAssignerOnMetaRebuild") {
686       public void run() {
687         // Wait until all dead server processing finish
688         while (serverManager.areDeadServersInProgress()) {
689           try {
690             Thread.sleep(100);
691           } catch (InterruptedException e) {
692             LOG.warn("RegionAssignerOnMetaRebuild got interrupted.", e);
693             Thread.currentThread().interrupt();
694             return;
695           }
696         }
697         LOG.info("SSH has been completed for all dead servers, assigning user regions.");
698         try {
699           // Assign the regions
700           assignAllUserRegions(getUserRegionsToAssign());
701         } catch (CoordinatedStateException | IOException | InterruptedException e) {
702           LOG.error("Exception occured while assigning user regions.", e);
703         }
704       };
705     };
706     regionAssignerThread.setDaemon(true);
707     regionAssignerThread.start();
708   }
709 
710   /**
711    * If region is up in zk in transition, then do fixup and block and wait until
712    * the region is assigned and out of transition.  Used on startup for
713    * catalog regions.
714    * @param hri Region to look for.
715    * @return True if we processed a region in transition else false if region
716    * was not up in zk in transition.
717    * @throws InterruptedException
718    * @throws KeeperException
719    * @throws IOException
720    */
721   boolean processRegionInTransitionAndBlockUntilAssigned(final HRegionInfo hri)
722       throws InterruptedException, KeeperException, IOException {
723     String encodedRegionName = hri.getEncodedName();
724     if (!processRegionInTransition(encodedRegionName, hri)) {
725       return false; // The region is not in transition
726     }
727     LOG.debug("Waiting on " + HRegionInfo.prettyPrint(encodedRegionName));
728     while (!this.server.isStopped() &&
729         this.regionStates.isRegionInTransition(encodedRegionName)) {
730       RegionState state = this.regionStates.getRegionTransitionState(encodedRegionName);
731       if (state == null || !serverManager.isServerOnline(state.getServerName())) {
732         // The region is not in transition, or not in transition on an online
733         // server. Doesn't help to block here any more. Caller need to
734         // verify the region is actually assigned.
735         break;
736       }
737       this.regionStates.waitForUpdate(100);
738     }
739     return true;
740   }
741 
742   /**
743    * Process failover of new master for region <code>encodedRegionName</code>
744    * up in zookeeper.
745    * @param encodedRegionName Region to process failover for.
746    * @param regionInfo If null we'll go get it from meta table.
747    * @return True if we processed <code>regionInfo</code> as a RIT.
748    * @throws KeeperException
749    * @throws IOException
750    */
751   boolean processRegionInTransition(final String encodedRegionName,
752       final HRegionInfo regionInfo) throws KeeperException, IOException {
753     // We need a lock here to ensure that we will not put the same region twice
754     // It has no reason to be a lock shared with the other operations.
755     // We can do the lock on the region only, instead of a global lock: what we want to ensure
756     // is that we don't have two threads working on the same region.
757     Lock lock = locker.acquireLock(encodedRegionName);
758     try {
759       Stat stat = new Stat();
760       byte [] data = ZKAssign.getDataAndWatch(watcher, encodedRegionName, stat);
761       if (data == null) return false;
762       RegionTransition rt;
763       try {
764         rt = RegionTransition.parseFrom(data);
765       } catch (DeserializationException e) {
766         LOG.warn("Failed parse znode data", e);
767         return false;
768       }
769       HRegionInfo hri = regionInfo;
770       if (hri == null) {
771         // The region info is not passed in. We will try to find the region
772         // from region states map/meta based on the encoded region name. But we
773         // may not be able to find it. This is valid for online merge that
774         // the region may have not been created if the merge is not completed.
775         // Therefore, it is not in meta at master recovery time.
776         hri = regionStates.getRegionInfo(rt.getRegionName());
777         EventType et = rt.getEventType();
778         if (hri == null && et != EventType.RS_ZK_REGION_MERGING
779             && et != EventType.RS_ZK_REQUEST_REGION_MERGE) {
780           LOG.warn("Couldn't find the region in recovering " + rt);
781           return false;
782         }
783       }
784 
785       // TODO: This code is tied to ZK anyway, so for now leaving it as is,
786       // will refactor when whole region assignment will be abstracted from ZK
787       BaseCoordinatedStateManager cp =
788         (BaseCoordinatedStateManager) this.server.getCoordinatedStateManager();
789       OpenRegionCoordination openRegionCoordination = cp.getOpenRegionCoordination();
790 
791       ZkOpenRegionCoordination.ZkOpenRegionDetails zkOrd =
792         new ZkOpenRegionCoordination.ZkOpenRegionDetails();
793       zkOrd.setVersion(stat.getVersion());
794       zkOrd.setServerName(cp.getServer().getServerName());
795 
796       return processRegionsInTransition(
797         rt, hri, openRegionCoordination, zkOrd);
798     } finally {
799       lock.unlock();
800     }
801   }
802 
803   /**
804    * This call is invoked only (1) master assign meta;
805    * (2) during failover mode startup, zk assignment node processing.
806    * The locker is set in the caller. It returns true if the region
807    * is in transition for sure, false otherwise.
808    *
809    * It should be private but it is used by some test too.
810    */
811   boolean processRegionsInTransition(
812       final RegionTransition rt, final HRegionInfo regionInfo,
813       OpenRegionCoordination coordination,
814       final OpenRegionCoordination.OpenRegionDetails ord) throws KeeperException {
815     EventType et = rt.getEventType();
816     // Get ServerName.  Could not be null.
817     final ServerName sn = rt.getServerName();
818     final byte[] regionName = rt.getRegionName();
819     final String encodedName = HRegionInfo.encodeRegionName(regionName);
820     final String prettyPrintedRegionName = HRegionInfo.prettyPrint(encodedName);
821     LOG.info("Processing " + prettyPrintedRegionName + " in state: " + et);
822 
823     if (regionStates.isRegionInTransition(encodedName)
824         && (regionInfo.isMetaRegion() || !useZKForAssignment)) {
825       LOG.info("Processed region " + prettyPrintedRegionName + " in state: "
826         + et + ", does nothing since the region is already in transition "
827         + regionStates.getRegionTransitionState(encodedName));
828       // Just return
829       return true;
830     }
831     if (!serverManager.isServerOnline(sn)) {
832       // It was transitioning on a dead server, so it's closed now.
833       // Force to OFFLINE and put it in transition, but not assign it
834       // since log splitting for the dead server is not done yet.
835       LOG.debug("RIT " + encodedName + " in state=" + rt.getEventType() +
836         " was on deadserver; forcing offline");
837       if (regionStates.isRegionOnline(regionInfo)) {
838         // Meta could still show the region is assigned to the previous
839         // server. If that server is online, when we reload the meta, the
840         // region is put back to online, we need to offline it.
841         regionStates.regionOffline(regionInfo);
842         sendRegionClosedNotification(regionInfo);
843       }
844       // Put it back in transition so that SSH can re-assign it
845       regionStates.updateRegionState(regionInfo, State.OFFLINE, sn);
846 
847       if (regionInfo.isMetaRegion()) {
848         // If it's meta region, reset the meta location.
849         // So that master knows the right meta region server.
850         MetaTableLocator.setMetaLocation(watcher, sn, State.OPEN);
851       } else {
852         // No matter the previous server is online or offline,
853         // we need to reset the last region server of the region.
854         regionStates.setLastRegionServerOfRegion(sn, encodedName);
855         // Make sure we know the server is dead.
856         if (!serverManager.isServerDead(sn)) {
857           serverManager.expireServer(sn);
858         }
859       }
860       return false;
861     }
862     switch (et) {
863       case M_ZK_REGION_CLOSING:
864         // Insert into RIT & resend the query to the region server: may be the previous master
865         // died before sending the query the first time.
866         final RegionState rsClosing = regionStates.updateRegionState(rt, State.CLOSING);
867         this.executorService.submit(
868           new EventHandler(server, EventType.M_MASTER_RECOVERY) {
869             @Override
870             public void process() throws IOException {
871               ReentrantLock lock = locker.acquireLock(regionInfo.getEncodedName());
872               try {
873                 final int expectedVersion = ((ZkOpenRegionCoordination.ZkOpenRegionDetails) ord)
874                   .getVersion();
875                 unassign(regionInfo, rsClosing, expectedVersion, null, useZKForAssignment, null);
876                 if (regionStates.isRegionOffline(regionInfo)) {
877                   assign(regionInfo, true);
878                 }
879               } finally {
880                 lock.unlock();
881               }
882             }
883           });
884         break;
885 
886       case RS_ZK_REGION_CLOSED:
887       case RS_ZK_REGION_FAILED_OPEN:
888         // Region is closed, insert into RIT and handle it
889         regionStates.setRegionStateTOCLOSED(regionInfo, sn);
890         if (!replicasToClose.contains(regionInfo)) {
891           invokeAssign(regionInfo);
892         } else {
893           offlineDisabledRegion(regionInfo);
894         }
895         break;
896 
897       case M_ZK_REGION_OFFLINE:
898         // Insert in RIT and resend to the regionserver
899         regionStates.updateRegionState(rt, State.OFFLINE);
900         final RegionState rsOffline = regionStates.getRegionState(regionInfo);
901         this.executorService.submit(
902           new EventHandler(server, EventType.M_MASTER_RECOVERY) {
903             @Override
904             public void process() throws IOException {
905               ReentrantLock lock = locker.acquireLock(regionInfo.getEncodedName());
906               try {
907                 RegionPlan plan = new RegionPlan(regionInfo, null, sn);
908                 addPlan(encodedName, plan);
909                 assign(rsOffline, true, false);
910               } finally {
911                 lock.unlock();
912               }
913             }
914           });
915         break;
916 
917       case RS_ZK_REGION_OPENING:
918         regionStates.updateRegionState(rt, State.OPENING);
919         break;
920 
921       case RS_ZK_REGION_OPENED:
922         // Region is opened, insert into RIT and handle it
923         // This could be done asynchronously, we would need then to acquire the lock in the
924         //  handler.
925         regionStates.updateRegionState(rt, State.OPEN);
926         new OpenedRegionHandler(server, this, regionInfo, coordination, ord).process();
927         break;
928       case RS_ZK_REQUEST_REGION_SPLIT:
929       case RS_ZK_REGION_SPLITTING:
930       case RS_ZK_REGION_SPLIT:
931         // Splitting region should be online. We could have skipped it during
932         // user region rebuilding since we may consider the split is completed.
933         // Put it in SPLITTING state to avoid complications.
934         regionStates.regionOnline(regionInfo, sn);
935         regionStates.updateRegionState(rt, State.SPLITTING);
936         if (!handleRegionSplitting(
937             rt, encodedName, prettyPrintedRegionName, sn)) {
938           deleteSplittingNode(encodedName, sn);
939         }
940         break;
941       case RS_ZK_REQUEST_REGION_MERGE:
942       case RS_ZK_REGION_MERGING:
943       case RS_ZK_REGION_MERGED:
944         if (!handleRegionMerging(
945             rt, encodedName, prettyPrintedRegionName, sn)) {
946           deleteMergingNode(encodedName, sn);
947         }
948         break;
949       default:
950         throw new IllegalStateException("Received region in state:" + et + " is not valid.");
951     }
952     LOG.info("Processed region " + prettyPrintedRegionName + " in state "
953       + et + ", on " + (serverManager.isServerOnline(sn) ? "" : "dead ")
954       + "server: " + sn);
955     return true;
956   }
957 
958   /**
959    * When a region is closed, it should be removed from the regionsToReopen
960    * @param hri HRegionInfo of the region which was closed
961    */
962   public void removeClosedRegion(HRegionInfo hri) {
963     if (regionsToReopen.remove(hri.getEncodedName()) != null) {
964       LOG.debug("Removed region from reopening regions because it was closed");
965     }
966   }
967 
968   /**
969    * Handles various states an unassigned node can be in.
970    * <p>
971    * Method is called when a state change is suspected for an unassigned node.
972    * <p>
973    * This deals with skipped transitions (we got a CLOSED but didn't see CLOSING
974    * yet).
975    * @param rt region transition
976    * @param coordination coordination for opening region
977    * @param ord details about opening region
978    */
979   @edu.umd.cs.findbugs.annotations.SuppressWarnings(
980       value="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION",
981       justification="Needs work; says access to ConcurrentHashMaps not ATOMIC!!!")
982   void handleRegion(final RegionTransition rt, OpenRegionCoordination coordination,
983                     OpenRegionCoordination.OpenRegionDetails ord) {
984     if (rt == null) {
985       LOG.warn("Unexpected NULL input for RegionTransition rt");
986       return;
987     }
988     final ServerName sn = rt.getServerName();
989     // Check if this is a special HBCK transition
990     if (sn.equals(HBCK_CODE_SERVERNAME)) {
991       handleHBCK(rt);
992       return;
993     }
994     final long createTime = rt.getCreateTime();
995     final byte[] regionName = rt.getRegionName();
996     String encodedName = HRegionInfo.encodeRegionName(regionName);
997     String prettyPrintedRegionName = HRegionInfo.prettyPrint(encodedName);
998     // Verify this is a known server
999     if (!serverManager.isServerOnline(sn)
1000       && !ignoreStatesRSOffline.contains(rt.getEventType())) {
1001       LOG.warn("Attempted to handle region transition for server but " +
1002         "it is not online: " + prettyPrintedRegionName + ", " + rt);
1003       return;
1004     }
1005 
1006     RegionState regionState =
1007       regionStates.getRegionState(encodedName);
1008     long startTime = System.currentTimeMillis();
1009     if (LOG.isDebugEnabled()) {
1010       boolean lateEvent = createTime < (startTime - 15000);
1011       LOG.debug("Handling " + rt.getEventType() +
1012         ", server=" + sn + ", region=" +
1013         (prettyPrintedRegionName == null ? "null" : prettyPrintedRegionName) +
1014         (lateEvent ? ", which is more than 15 seconds late" : "") +
1015         ", current_state=" + regionState);
1016     }
1017     // We don't do anything for this event,
1018     // so separate it out, no need to lock/unlock anything
1019     if (rt.getEventType() == EventType.M_ZK_REGION_OFFLINE) {
1020       return;
1021     }
1022 
1023     // We need a lock on the region as we could update it
1024     Lock lock = locker.acquireLock(encodedName);
1025     try {
1026       RegionState latestState =
1027         regionStates.getRegionState(encodedName);
1028       if ((regionState == null && latestState != null)
1029           || (regionState != null && latestState == null)
1030           || (regionState != null && latestState != null
1031             && latestState.getState() != regionState.getState())) {
1032         LOG.warn("Region state changed from " + regionState + " to "
1033           + latestState + ", while acquiring lock");
1034       }
1035       long waitedTime = System.currentTimeMillis() - startTime;
1036       if (waitedTime > 5000) {
1037         LOG.warn("Took " + waitedTime + "ms to acquire the lock");
1038       }
1039       regionState = latestState;
1040       switch (rt.getEventType()) {
1041       case RS_ZK_REQUEST_REGION_SPLIT:
1042       case RS_ZK_REGION_SPLITTING:
1043       case RS_ZK_REGION_SPLIT:
1044         if (!handleRegionSplitting(
1045             rt, encodedName, prettyPrintedRegionName, sn)) {
1046           deleteSplittingNode(encodedName, sn);
1047         }
1048         break;
1049 
1050       case RS_ZK_REQUEST_REGION_MERGE:
1051       case RS_ZK_REGION_MERGING:
1052       case RS_ZK_REGION_MERGED:
1053         // Merged region is a new region, we can't find it in the region states now.
1054         // However, the two merging regions are not new. They should be in state for merging.
1055         if (!handleRegionMerging(
1056             rt, encodedName, prettyPrintedRegionName, sn)) {
1057           deleteMergingNode(encodedName, sn);
1058         }
1059         break;
1060 
1061       case M_ZK_REGION_CLOSING:
1062         // Should see CLOSING after we have asked it to CLOSE or additional
1063         // times after already being in state of CLOSING
1064         if (regionState == null
1065             || !regionState.isPendingCloseOrClosingOnServer(sn)) {
1066           LOG.warn("Received CLOSING for " + prettyPrintedRegionName
1067             + " from " + sn + " but the region isn't PENDING_CLOSE/CLOSING here: "
1068             + regionStates.getRegionState(encodedName));
1069           return;
1070         }
1071         // Transition to CLOSING (or update stamp if already CLOSING)
1072         regionStates.updateRegionState(rt, State.CLOSING);
1073         break;
1074 
1075       case RS_ZK_REGION_CLOSED:
1076         // Should see CLOSED after CLOSING but possible after PENDING_CLOSE
1077         if (regionState == null
1078             || !regionState.isPendingCloseOrClosingOnServer(sn)) {
1079           LOG.warn("Received CLOSED for " + prettyPrintedRegionName
1080             + " from " + sn + " but the region isn't PENDING_CLOSE/CLOSING here: "
1081             + regionStates.getRegionState(encodedName));
1082           return;
1083         }
1084         // Handle CLOSED by assigning elsewhere or stopping if a disable
1085         // If we got here all is good.  Need to update RegionState -- else
1086         // what follows will fail because not in expected state.
1087         new ClosedRegionHandler(server, this, regionState.getRegion()).process();
1088         updateClosedRegionHandlerTracker(regionState.getRegion());
1089         break;
1090 
1091         case RS_ZK_REGION_FAILED_OPEN:
1092           if (regionState == null
1093               || !regionState.isPendingOpenOrOpeningOnServer(sn)) {
1094             LOG.warn("Received FAILED_OPEN for " + prettyPrintedRegionName
1095               + " from " + sn + " but the region isn't PENDING_OPEN/OPENING here: "
1096               + regionStates.getRegionState(encodedName));
1097             return;
1098           }
1099           AtomicInteger failedOpenCount = failedOpenTracker.get(encodedName);
1100           if (failedOpenCount == null) {
1101             failedOpenCount = new AtomicInteger();
1102             // No need to use putIfAbsent, or extra synchronization since
1103             // this whole handleRegion block is locked on the encoded region
1104             // name, and failedOpenTracker is updated only in this block
1105             // FindBugs: AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION
1106             failedOpenTracker.put(encodedName, failedOpenCount);
1107           }
1108           if (failedOpenCount.incrementAndGet() >= maximumAttempts) {
1109             // FindBugs: AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION
1110             regionStates.updateRegionState(rt, State.FAILED_OPEN);
1111             // remove the tracking info to save memory, also reset
1112             // the count for next open initiative
1113             failedOpenTracker.remove(encodedName);
1114           } else {
1115             // Handle this the same as if it were opened and then closed.
1116             regionState = regionStates.setRegionStateTOCLOSED(rt.getRegionName(), sn);
1117             if (regionState != null) {
1118               // When there are more than one region server a new RS is selected as the
1119               // destination and the same is updated in the regionplan. (HBASE-5546)
1120               try {
1121                 getRegionPlan(regionState.getRegion(), sn, true);
1122                 new ClosedRegionHandler(server, this, regionState.getRegion()).process();
1123               } catch (HBaseIOException e) {
1124                 LOG.warn("Failed to get region plan", e);
1125               }
1126             }
1127           }
1128           break;
1129 
1130         case RS_ZK_REGION_OPENING:
1131           // Should see OPENING after we have asked it to OPEN or additional
1132           // times after already being in state of OPENING
1133           if (regionState == null
1134               || !regionState.isPendingOpenOrOpeningOnServer(sn)) {
1135             LOG.warn("Received OPENING for " + prettyPrintedRegionName
1136               + " from " + sn + " but the region isn't PENDING_OPEN/OPENING here: "
1137               + regionStates.getRegionState(encodedName));
1138             return;
1139           }
1140           // Transition to OPENING (or update stamp if already OPENING)
1141           regionStates.updateRegionState(rt, State.OPENING);
1142           break;
1143 
1144         case RS_ZK_REGION_OPENED:
1145           // Should see OPENED after OPENING but possible after PENDING_OPEN.
1146           if (regionState == null
1147               || !regionState.isPendingOpenOrOpeningOnServer(sn)) {
1148             LOG.warn("Received OPENED for " + prettyPrintedRegionName
1149               + " from " + sn + " but the region isn't PENDING_OPEN/OPENING here: "
1150               + regionStates.getRegionState(encodedName));
1151 
1152             if (regionState != null) {
1153               if(regionState.isOpened() && regionState.getServerName().equals(sn)) {
1154                 //if this region was opened before on this rs, we don't have to unassign it. It won't cause
1155                 //double assign. One possible scenario of what happened is HBASE-17275
1156                 failedOpenTracker.remove(encodedName); // reset the count, if any
1157                 new OpenedRegionHandler(
1158                     server, this, regionState.getRegion(), coordination, ord).process();
1159                 updateOpenedRegionHandlerTracker(regionState.getRegion());
1160               } else {
1161                 // Close it without updating the internal region states,
1162                 // so as not to create double assignments in unlucky scenarios
1163                 // mentioned in OpenRegionHandler#process
1164                 unassign(regionState.getRegion(), null, -1, null, false, sn);
1165               }
1166             }
1167             return;
1168           }
1169           // Handle OPENED by removing from transition and deleted zk node
1170           regionState =
1171               regionStates.transitionOpenFromPendingOpenOrOpeningOnServer(rt,regionState, sn);
1172           if (regionState != null) {
1173             failedOpenTracker.remove(encodedName); // reset the count, if any
1174             new OpenedRegionHandler(
1175               server, this, regionState.getRegion(), coordination, ord).process();
1176             updateOpenedRegionHandlerTracker(regionState.getRegion());
1177           }
1178           break;
1179 
1180         default:
1181           throw new IllegalStateException("Received event is not valid.");
1182       }
1183     } finally {
1184       lock.unlock();
1185     }
1186   }
1187 
1188   //For unit tests only
1189   boolean wasClosedHandlerCalled(HRegionInfo hri) {
1190     AtomicBoolean b = closedRegionHandlerCalled.get(hri);
1191     //compareAndSet to be sure that unit tests don't see stale values. Means,
1192     //we will return true exactly once unless the handler code resets to true
1193     //this value.
1194     return b == null ? false : b.compareAndSet(true, false);
1195   }
1196 
1197   //For unit tests only
1198   boolean wasOpenedHandlerCalled(HRegionInfo hri) {
1199     AtomicBoolean b = openedRegionHandlerCalled.get(hri);
1200     //compareAndSet to be sure that unit tests don't see stale values. Means,
1201     //we will return true exactly once unless the handler code resets to true
1202     //this value.
1203     return b == null ? false : b.compareAndSet(true, false);
1204   }
1205 
1206   //For unit tests only
1207   void initializeHandlerTrackers() {
1208     closedRegionHandlerCalled = new HashMap<HRegionInfo, AtomicBoolean>();
1209     openedRegionHandlerCalled = new HashMap<HRegionInfo, AtomicBoolean>();
1210   }
1211 
1212   void updateClosedRegionHandlerTracker(HRegionInfo hri) {
1213     if (closedRegionHandlerCalled != null) { //only for unit tests this is true
1214       closedRegionHandlerCalled.put(hri, new AtomicBoolean(true));
1215     }
1216   }
1217 
1218   void updateOpenedRegionHandlerTracker(HRegionInfo hri) {
1219     if (openedRegionHandlerCalled != null) { //only for unit tests this is true
1220       openedRegionHandlerCalled.put(hri, new AtomicBoolean(true));
1221     }
1222   }
1223 
1224   // TODO: processFavoredNodes might throw an exception, for e.g., if the
1225   // meta could not be contacted/updated. We need to see how seriously to treat
1226   // this problem as. Should we fail the current assignment. We should be able
1227   // to recover from this problem eventually (if the meta couldn't be updated
1228   // things should work normally and eventually get fixed up).
1229   void processFavoredNodes(List<HRegionInfo> regions) throws IOException {
1230     if (!shouldAssignRegionsWithFavoredNodes) return;
1231     // The AM gets the favored nodes info for each region and updates the meta
1232     // table with that info
1233     Map<HRegionInfo, List<ServerName>> regionToFavoredNodes =
1234         new HashMap<HRegionInfo, List<ServerName>>();
1235     for (HRegionInfo region : regions) {
1236       regionToFavoredNodes.put(region,
1237           ((FavoredNodeLoadBalancer)this.balancer).getFavoredNodes(region));
1238     }
1239     FavoredNodeAssignmentHelper.updateMetaWithFavoredNodesInfo(regionToFavoredNodes,
1240       this.server.getConnection());
1241   }
1242 
1243   /**
1244    * Handle a ZK unassigned node transition triggered by HBCK repair tool.
1245    * <p>
1246    * This is handled in a separate code path because it breaks the normal rules.
1247    * @param rt
1248    */
1249   @SuppressWarnings("deprecation")
1250   private void handleHBCK(RegionTransition rt) {
1251     String encodedName = HRegionInfo.encodeRegionName(rt.getRegionName());
1252     LOG.info("Handling HBCK triggered transition=" + rt.getEventType() +
1253       ", server=" + rt.getServerName() + ", region=" +
1254       HRegionInfo.prettyPrint(encodedName));
1255     RegionState regionState = regionStates.getRegionTransitionState(encodedName);
1256     switch (rt.getEventType()) {
1257       case M_ZK_REGION_OFFLINE:
1258         HRegionInfo regionInfo;
1259         if (regionState != null) {
1260           regionInfo = regionState.getRegion();
1261         } else {
1262           try {
1263             byte [] name = rt.getRegionName();
1264             Pair<HRegionInfo, ServerName> p = MetaTableAccessor.getRegion(
1265               this.server.getConnection(), name);
1266             regionInfo = p.getFirst();
1267           } catch (IOException e) {
1268             LOG.info("Exception reading hbase:meta doing HBCK repair operation", e);
1269             return;
1270           }
1271         }
1272         LOG.info("HBCK repair is triggering assignment of region=" +
1273             regionInfo.getRegionNameAsString());
1274         // trigger assign, node is already in OFFLINE so don't need to update ZK
1275         assign(regionInfo, false);
1276         break;
1277 
1278       default:
1279         LOG.warn("Received unexpected region state from HBCK: " + rt.toString());
1280         break;
1281     }
1282 
1283   }
1284 
1285   // ZooKeeper events
1286 
1287   /**
1288    * New unassigned node has been created.
1289    *
1290    * <p>This happens when an RS begins the OPENING or CLOSING of a region by
1291    * creating an unassigned node.
1292    *
1293    * <p>When this happens we must:
1294    * <ol>
1295    *   <li>Watch the node for further events</li>
1296    *   <li>Read and handle the state in the node</li>
1297    * </ol>
1298    */
1299   @Override
1300   public void nodeCreated(String path) {
1301     handleAssignmentEvent(path);
1302   }
1303 
1304   /**
1305    * Existing unassigned node has had data changed.
1306    *
1307    * <p>This happens when an RS transitions from OFFLINE to OPENING, or between
1308    * OPENING/OPENED and CLOSING/CLOSED.
1309    *
1310    * <p>When this happens we must:
1311    * <ol>
1312    *   <li>Watch the node for further events</li>
1313    *   <li>Read and handle the state in the node</li>
1314    * </ol>
1315    */
1316   @Override
1317   public void nodeDataChanged(String path) {
1318     handleAssignmentEvent(path);
1319   }
1320 
1321 
1322   // We  don't want to have two events on the same region managed simultaneously.
1323   // For this reason, we need to wait if an event on the same region is currently in progress.
1324   // So we track the region names of the events in progress, and we keep a waiting list.
1325   private final Set<String> regionsInProgress = new HashSet<String>();
1326   // In a LinkedHashMultimap, the put order is kept when we retrieve the collection back. We need
1327   //  this as we want the events to be managed in the same order as we received them.
1328   private final LinkedHashMultimap <String, RegionRunnable>
1329       zkEventWorkerWaitingList = LinkedHashMultimap.create();
1330 
1331   /**
1332    * A specific runnable that works only on a region.
1333    */
1334   private interface RegionRunnable extends Runnable{
1335     /**
1336      * @return - the name of the region it works on.
1337      */
1338     String getRegionName();
1339   }
1340 
1341   /**
1342    * Submit a task, ensuring that there is only one task at a time that working on a given region.
1343    * Order is respected.
1344    */
1345   protected void zkEventWorkersSubmit(final RegionRunnable regRunnable) {
1346 
1347     synchronized (regionsInProgress) {
1348       // If we're there is already a task with this region, we add it to the
1349       //  waiting list and return.
1350       if (regionsInProgress.contains(regRunnable.getRegionName())) {
1351         synchronized (zkEventWorkerWaitingList){
1352           zkEventWorkerWaitingList.put(regRunnable.getRegionName(), regRunnable);
1353         }
1354         return;
1355       }
1356 
1357       // No event in progress on this region => we can submit a new task immediately.
1358       regionsInProgress.add(regRunnable.getRegionName());
1359       zkEventWorkers.submit(new Runnable() {
1360         @Override
1361         public void run() {
1362           try {
1363             regRunnable.run();
1364           } finally {
1365             // now that we have finished, let's see if there is an event for the same region in the
1366             //  waiting list. If it's the case, we can now submit it to the pool.
1367             synchronized (regionsInProgress) {
1368               regionsInProgress.remove(regRunnable.getRegionName());
1369               synchronized (zkEventWorkerWaitingList) {
1370                 java.util.Set<RegionRunnable> waiting = zkEventWorkerWaitingList.get(
1371                     regRunnable.getRegionName());
1372                 if (!waiting.isEmpty()) {
1373                   // We want the first object only. The only way to get it is through an iterator.
1374                   RegionRunnable toSubmit = waiting.iterator().next();
1375                   zkEventWorkerWaitingList.remove(toSubmit.getRegionName(), toSubmit);
1376                   zkEventWorkersSubmit(toSubmit);
1377                 }
1378               }
1379             }
1380           }
1381         }
1382       });
1383     }
1384   }
1385 
1386   @Override
1387   public void nodeDeleted(final String path) {
1388     if (path.startsWith(watcher.assignmentZNode)) {
1389       final String regionName = ZKAssign.getRegionName(watcher, path);
1390       zkEventWorkersSubmit(new RegionRunnable() {
1391         @Override
1392         public String getRegionName() {
1393           return regionName;
1394         }
1395 
1396         @Override
1397         public void run() {
1398           Lock lock = locker.acquireLock(regionName);
1399           try {
1400             RegionState rs = regionStates.getRegionTransitionState(regionName);
1401             if (rs == null) {
1402               rs = regionStates.getRegionState(regionName);
1403               if (rs == null || !rs.isMergingNew()) {
1404                 // MergingNew is an offline state
1405                 return;
1406               }
1407             }
1408 
1409             HRegionInfo regionInfo = rs.getRegion();
1410             String regionNameStr = regionInfo.getRegionNameAsString();
1411             LOG.debug("Znode " + regionNameStr + " deleted, state: " + rs);
1412 
1413             boolean disabled = getTableStateManager().isTableState(regionInfo.getTable(),
1414                 ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING);
1415 
1416             ServerName serverName = rs.getServerName();
1417             if (serverManager.isServerOnline(serverName)) {
1418               if (rs.isOnServer(serverName) && (rs.isOpened() || rs.isSplitting())) {
1419                 synchronized (regionStates) {
1420                   regionOnline(regionInfo, serverName);
1421                   if (rs.isSplitting() && splitRegions.containsKey(regionInfo)) {
1422                     // Check if the daugter regions are still there, if they are present, offline
1423                     // as its the case of a rollback.
1424                     HRegionInfo hri_a = splitRegions.get(regionInfo).getFirst();
1425                     HRegionInfo hri_b = splitRegions.get(regionInfo).getSecond();
1426                     if (!regionStates.isRegionInTransition(hri_a.getEncodedName())) {
1427                       LOG.warn("Split daughter region not in transition " + hri_a);
1428                     }
1429                     if (!regionStates.isRegionInTransition(hri_b.getEncodedName())) {
1430                       LOG.warn("Split daughter region not in transition" + hri_b);
1431                     }
1432                     regionOffline(hri_a);
1433                     regionOffline(hri_b);
1434                     splitRegions.remove(regionInfo);
1435                   }
1436                   if (disabled) {
1437                     // if server is offline, no hurt to unassign again
1438                     LOG.info("Opened " + regionNameStr
1439                         + "but this table is disabled, triggering close of region");
1440                     unassign(regionInfo);
1441                   }
1442                 }
1443               } else if (rs.isMergingNew()) {
1444                 synchronized (regionStates) {
1445                   String p = regionInfo.getEncodedName();
1446                   PairOfSameType<HRegionInfo> regions = mergingRegions.get(p);
1447                   if (regions != null) {
1448                     onlineMergingRegion(disabled, regions.getFirst(), serverName);
1449                     onlineMergingRegion(disabled, regions.getSecond(), serverName);
1450                   }
1451                 }
1452               }
1453             }
1454           } finally {
1455             lock.unlock();
1456           }
1457         }
1458 
1459         private void onlineMergingRegion(boolean disabled,
1460             final HRegionInfo hri, final ServerName serverName) {
1461           RegionState regionState = regionStates.getRegionState(hri);
1462           if (regionState != null && regionState.isMerging()
1463               && regionState.isOnServer(serverName)) {
1464             regionOnline(regionState.getRegion(), serverName);
1465             if (disabled) {
1466               unassign(hri);
1467             }
1468           }
1469         }
1470       });
1471     }
1472   }
1473 
1474   /**
1475    * New unassigned node has been created.
1476    *
1477    * <p>This happens when an RS begins the OPENING, SPLITTING or CLOSING of a
1478    * region by creating a znode.
1479    *
1480    * <p>When this happens we must:
1481    * <ol>
1482    *   <li>Watch the node for further children changed events</li>
1483    *   <li>Watch all new children for changed events</li>
1484    * </ol>
1485    */
1486   @Override
1487   public void nodeChildrenChanged(String path) {
1488     if (path.equals(watcher.assignmentZNode)) {
1489       zkEventWorkers.submit(new Runnable() {
1490         @Override
1491         public void run() {
1492           try {
1493             // Just make sure we see the changes for the new znodes
1494             List<String> children =
1495               ZKUtil.listChildrenAndWatchForNewChildren(
1496                 watcher, watcher.assignmentZNode);
1497             if (children != null) {
1498               Stat stat = new Stat();
1499               for (String child : children) {
1500                 // if region is in transition, we already have a watch
1501                 // on it, so no need to watch it again. So, as I know for now,
1502                 // this is needed to watch splitting nodes only.
1503                 if (!regionStates.isRegionInTransition(child)) {
1504                   ZKAssign.getDataAndWatch(watcher, child, stat);
1505                 }
1506               }
1507             }
1508           } catch (KeeperException e) {
1509             server.abort("Unexpected ZK exception reading unassigned children", e);
1510           }
1511         }
1512       });
1513     }
1514   }
1515 
1516 
1517   /**
1518    * Marks the region as online.  Removes it from regions in transition and
1519    * updates the in-memory assignment information.
1520    * <p>
1521    * Used when a region has been successfully opened on a region server.
1522    * @param regionInfo
1523    * @param sn
1524    */
1525   void regionOnline(HRegionInfo regionInfo, ServerName sn) {
1526     regionOnline(regionInfo, sn, HConstants.NO_SEQNUM);
1527   }
1528 
1529   void regionOnline(HRegionInfo regionInfo, ServerName sn, long openSeqNum) {
1530     numRegionsOpened.incrementAndGet();
1531     regionStates.regionOnline(regionInfo, sn, openSeqNum);
1532 
1533     // Remove plan if one.
1534     clearRegionPlan(regionInfo);
1535     balancer.regionOnline(regionInfo, sn);
1536 
1537     // Tell our listeners that a region was opened
1538     sendRegionOpenedNotification(regionInfo, sn);
1539   }
1540 
1541   /**
1542    * Pass the assignment event to a worker for processing.
1543    * Each worker is a single thread executor service.  The reason
1544    * for just one thread is to make sure all events for a given
1545    * region are processed in order.
1546    *
1547    * @param path
1548    */
1549   private void handleAssignmentEvent(final String path) {
1550     if (path.startsWith(watcher.assignmentZNode)) {
1551       final String regionName = ZKAssign.getRegionName(watcher, path);
1552 
1553       zkEventWorkersSubmit(new RegionRunnable() {
1554         @Override
1555         public String getRegionName() {
1556           return regionName;
1557         }
1558 
1559         @Override
1560         public void run() {
1561           try {
1562             Stat stat = new Stat();
1563             byte [] data = ZKAssign.getDataAndWatch(watcher, path, stat);
1564             if (data == null) return;
1565 
1566             RegionTransition rt = RegionTransition.parseFrom(data);
1567 
1568             // TODO: This code is tied to ZK anyway, so for now leaving it as is,
1569             // will refactor when whole region assignment will be abstracted from ZK
1570             BaseCoordinatedStateManager csm =
1571               (BaseCoordinatedStateManager) server.getCoordinatedStateManager();
1572             OpenRegionCoordination openRegionCoordination = csm.getOpenRegionCoordination();
1573 
1574             ZkOpenRegionCoordination.ZkOpenRegionDetails zkOrd =
1575               new ZkOpenRegionCoordination.ZkOpenRegionDetails();
1576             zkOrd.setVersion(stat.getVersion());
1577             zkOrd.setServerName(csm.getServer().getServerName());
1578 
1579             handleRegion(rt, openRegionCoordination, zkOrd);
1580           } catch (KeeperException e) {
1581             server.abort("Unexpected ZK exception reading unassigned node data", e);
1582           } catch (DeserializationException e) {
1583             server.abort("Unexpected exception deserializing node data", e);
1584           }
1585         }
1586       });
1587     }
1588   }
1589 
1590   /**
1591    * Marks the region as offline.  Removes it from regions in transition and
1592    * removes in-memory assignment information.
1593    * <p>
1594    * Used when a region has been closed and should remain closed.
1595    * @param regionInfo
1596    */
1597   public void regionOffline(final HRegionInfo regionInfo) {
1598     if (regionStates.isRegionInState(regionInfo, State.MERGED, State.SPLIT)) {
1599       LOG.info("Try to offline region " + regionInfo.getEncodedName() +
1600           ", which is at state " + regionStates.getRegionState(regionInfo).getState() + ", skip");
1601       return;
1602     }
1603     regionOffline(regionInfo, null);
1604   }
1605 
1606   public void offlineDisabledRegion(HRegionInfo regionInfo) {
1607     if (useZKForAssignment) {
1608       // Disabling so should not be reassigned, just delete the CLOSED node
1609       LOG.debug("Table being disabled so deleting ZK node and removing from " +
1610         "regions in transition, skipping assignment of region " +
1611           regionInfo.getRegionNameAsString());
1612       String encodedName = regionInfo.getEncodedName();
1613       deleteNodeInStates(encodedName, "closed", null,
1614         EventType.RS_ZK_REGION_CLOSED, EventType.M_ZK_REGION_OFFLINE);
1615     }
1616     replicasToClose.remove(regionInfo);
1617     //Set servername in regionstate to null, see HBASE-18014
1618     getRegionStates().updateRegionState(regionInfo, State.OFFLINE, null);
1619     regionOffline(regionInfo);
1620   }
1621 
1622   // Assignment methods
1623 
1624   /**
1625    * Assigns the specified region.
1626    * <p>
1627    * If a RegionPlan is available with a valid destination then it will be used
1628    * to determine what server region is assigned to.  If no RegionPlan is
1629    * available, region will be assigned to a random available server.
1630    * <p>
1631    * Updates the RegionState and sends the OPEN RPC.
1632    * <p>
1633    * This will only succeed if the region is in transition and in a CLOSED or
1634    * OFFLINE state or not in transition (in-memory not zk), and of course, the
1635    * chosen server is up and running (It may have just crashed!).  If the
1636    * in-memory checks pass, the zk node is forced to OFFLINE before assigning.
1637    *
1638    * @param region server to be assigned
1639    * @param setOfflineInZK whether ZK node should be created/transitioned to an
1640    *                       OFFLINE state before assigning the region
1641    */
1642   public void assign(HRegionInfo region, boolean setOfflineInZK) {
1643     assign(region, setOfflineInZK, false);
1644   }
1645 
1646   /**
1647    * Use care with forceNewPlan. It could cause double assignment.
1648    */
1649   @VisibleForTesting
1650   public void assign(HRegionInfo region,
1651       boolean setOfflineInZK, boolean forceNewPlan) {
1652     if (isDisabledorDisablingRegionInRIT(region)) {
1653       return;
1654     }
1655     String encodedName = region.getEncodedName();
1656     Lock lock = locker.acquireLock(encodedName);
1657     try {
1658       RegionState state = forceRegionStateToOffline(region, forceNewPlan);
1659       if (state != null) {
1660         if (regionStates.wasRegionOnDeadServer(encodedName)) {
1661           LOG.info("Skip assigning " + region.getRegionNameAsString()
1662             + ", it's host " + regionStates.getLastRegionServerOfRegion(encodedName)
1663             + " is dead but not processed yet");
1664           return;
1665         }
1666         assign(state, setOfflineInZK && useZKForAssignment, forceNewPlan);
1667       }
1668     } finally {
1669       lock.unlock();
1670     }
1671   }
1672 
1673   /**
1674    * Bulk assign regions to <code>destination</code>.
1675    * @param destination
1676    * @param regions Regions to assign.
1677    * @return true if successful
1678    */
1679   boolean assign(final ServerName destination, final List<HRegionInfo> regions)
1680     throws InterruptedException {
1681     long startTime = EnvironmentEdgeManager.currentTime();
1682     try {
1683       int regionCount = regions.size();
1684       if (regionCount == 0) {
1685         return true;
1686       }
1687       LOG.info("Assigning " + regionCount + " region(s) to " + destination.toString());
1688       Set<String> encodedNames = new HashSet<String>(regionCount);
1689       for (HRegionInfo region : regions) {
1690         encodedNames.add(region.getEncodedName());
1691       }
1692 
1693       List<HRegionInfo> failedToOpenRegions = new ArrayList<HRegionInfo>();
1694       Map<String, Lock> locks = locker.acquireLocks(encodedNames);
1695       try {
1696         AtomicInteger counter = new AtomicInteger(0);
1697         Map<String, Integer> offlineNodesVersions = new ConcurrentHashMap<String, Integer>();
1698         OfflineCallback cb = new OfflineCallback(
1699           watcher, destination, counter, offlineNodesVersions);
1700         Map<String, RegionPlan> plans = new HashMap<String, RegionPlan>(regions.size());
1701         List<RegionState> states = new ArrayList<RegionState>(regions.size());
1702         for (HRegionInfo region : regions) {
1703           String encodedName = region.getEncodedName();
1704           if (!isDisabledorDisablingRegionInRIT(region)) {
1705             RegionState state = forceRegionStateToOffline(region, false);
1706             boolean onDeadServer = false;
1707             if (state != null) {
1708               if (regionStates.wasRegionOnDeadServer(encodedName)) {
1709                 LOG.info("Skip assigning " + region.getRegionNameAsString()
1710                   + ", it's host " + regionStates.getLastRegionServerOfRegion(encodedName)
1711                   + " is dead but not processed yet");
1712                 onDeadServer = true;
1713               } else if (!useZKForAssignment
1714                   || asyncSetOfflineInZooKeeper(state, cb, destination)) {
1715                 RegionPlan plan = new RegionPlan(region, state.getServerName(), destination);
1716                 plans.put(encodedName, plan);
1717                 states.add(state);
1718                 continue;
1719               }
1720             }
1721             // Reassign if the region wasn't on a dead server
1722             if (!onDeadServer) {
1723               LOG.info("failed to force region state to offline or "
1724                 + "failed to set it offline in ZK, will reassign later: " + region);
1725               failedToOpenRegions.add(region); // assign individually later
1726             }
1727           }
1728           // Release the lock, this region is excluded from bulk assign because
1729           // we can't update its state, or set its znode to offline.
1730           Lock lock = locks.remove(encodedName);
1731           lock.unlock();
1732         }
1733 
1734         if (useZKForAssignment) {
1735           // Wait until all unassigned nodes have been put up and watchers set.
1736           int total = states.size();
1737           for (int oldCounter = 0; !server.isStopped();) {
1738             int count = counter.get();
1739             if (oldCounter != count) {
1740               LOG.debug(destination.toString() + " unassigned znodes=" + count +
1741                 " of total=" + total + "; oldCounter=" + oldCounter);
1742               oldCounter = count;
1743             }
1744             if (count >= total) break;
1745             Thread.sleep(5);
1746           }
1747         }
1748 
1749         if (server.isStopped()) {
1750           return false;
1751         }
1752 
1753         // Add region plans, so we can updateTimers when one region is opened so
1754         // that unnecessary timeout on RIT is reduced.
1755         this.addPlans(plans);
1756 
1757         List<Triple<HRegionInfo, Integer, List<ServerName>>> regionOpenInfos =
1758           new ArrayList<Triple<HRegionInfo, Integer, List<ServerName>>>(states.size());
1759         for (RegionState state: states) {
1760           HRegionInfo region = state.getRegion();
1761           String encodedRegionName = region.getEncodedName();
1762           Integer nodeVersion = offlineNodesVersions.get(encodedRegionName);
1763           if (useZKForAssignment && (nodeVersion == null || nodeVersion == -1)) {
1764             LOG.warn("failed to offline in zookeeper: " + region);
1765             failedToOpenRegions.add(region); // assign individually later
1766             Lock lock = locks.remove(encodedRegionName);
1767             lock.unlock();
1768           } else {
1769             regionStates.updateRegionState(
1770               region, State.PENDING_OPEN, destination);
1771             List<ServerName> favoredNodes = ServerName.EMPTY_SERVER_LIST;
1772             if (this.shouldAssignRegionsWithFavoredNodes) {
1773               favoredNodes = ((FavoredNodeLoadBalancer)this.balancer).getFavoredNodes(region);
1774             }
1775             regionOpenInfos.add(new Triple<HRegionInfo, Integer,  List<ServerName>>(
1776               region, nodeVersion, favoredNodes));
1777           }
1778         }
1779 
1780         // Move on to open regions.
1781         try {
1782           // Send OPEN RPC. If it fails on a IOE or RemoteException,
1783           // regions will be assigned individually.
1784           long maxWaitTime = System.currentTimeMillis() +
1785             this.server.getConfiguration().
1786               getLong("hbase.regionserver.rpc.startup.waittime", 60000);
1787           for (int i = 1; i <= maximumAttempts && !server.isStopped(); i++) {
1788             try {
1789               // regionOpenInfos is empty if all regions are in failedToOpenRegions list
1790               if (regionOpenInfos.isEmpty()) {
1791                 break;
1792               }
1793               List<RegionOpeningState> regionOpeningStateList = serverManager
1794                 .sendRegionOpen(destination, regionOpenInfos);
1795               if (regionOpeningStateList == null) {
1796                 // Failed getting RPC connection to this server
1797                 return false;
1798               }
1799               for (int k = 0, n = regionOpeningStateList.size(); k < n; k++) {
1800                 RegionOpeningState openingState = regionOpeningStateList.get(k);
1801                 if (openingState != RegionOpeningState.OPENED) {
1802                   HRegionInfo region = regionOpenInfos.get(k).getFirst();
1803                   if (openingState == RegionOpeningState.ALREADY_OPENED) {
1804                     processAlreadyOpenedRegion(region, destination);
1805                   } else if (openingState == RegionOpeningState.FAILED_OPENING) {
1806                     // Failed opening this region, reassign it later
1807                     failedToOpenRegions.add(region);
1808                   } else {
1809                     LOG.warn("THIS SHOULD NOT HAPPEN: unknown opening state "
1810                       + openingState + " in assigning region " + region);
1811                   }
1812                 }
1813               }
1814               break;
1815             } catch (IOException e) {
1816               if (e instanceof RemoteException) {
1817                 e = ((RemoteException)e).unwrapRemoteException();
1818               }
1819               if (e instanceof RegionServerStoppedException) {
1820                 LOG.warn("The region server was shut down, ", e);
1821                 // No need to retry, the region server is a goner.
1822                 return false;
1823               } else if (e instanceof ServerNotRunningYetException) {
1824                 long now = System.currentTimeMillis();
1825                 if (now < maxWaitTime) {
1826                   LOG.debug("Server is not yet up; waiting up to " +
1827                     (maxWaitTime - now) + "ms", e);
1828                   Thread.sleep(100);
1829                   i--; // reset the try count
1830                   continue;
1831                 }
1832               } else if (e instanceof java.net.SocketTimeoutException
1833                   && this.serverManager.isServerOnline(destination)) {
1834                 // In case socket is timed out and the region server is still online,
1835                 // the openRegion RPC could have been accepted by the server and
1836                 // just the response didn't go through.  So we will retry to
1837                 // open the region on the same server.
1838                 if (LOG.isDebugEnabled()) {
1839                   LOG.debug("Bulk assigner openRegion() to " + destination
1840                     + " has timed out, but the regions might"
1841                     + " already be opened on it.", e);
1842                 }
1843                 // wait and reset the re-try count, server might be just busy.
1844                 Thread.sleep(100);
1845                 i--;
1846                 continue;
1847               }
1848               throw e;
1849             }
1850           }
1851         } catch (IOException e) {
1852           // Can be a socket timeout, EOF, NoRouteToHost, etc
1853           LOG.info("Unable to communicate with " + destination
1854             + " in order to assign regions, ", e);
1855           return false;
1856         }
1857       } finally {
1858         for (Lock lock : locks.values()) {
1859           lock.unlock();
1860         }
1861       }
1862 
1863       if (!failedToOpenRegions.isEmpty()) {
1864         for (HRegionInfo region : failedToOpenRegions) {
1865           if (!regionStates.isRegionOnline(region)) {
1866             invokeAssign(region);
1867           }
1868         }
1869       }
1870 
1871       // wait for assignment completion
1872       ArrayList<HRegionInfo> userRegionSet = new ArrayList<HRegionInfo>(regions.size());
1873       for (HRegionInfo region: regions) {
1874         if (!region.getTable().isSystemTable()) {
1875           userRegionSet.add(region);
1876         }
1877       }
1878       if (!waitForAssignment(userRegionSet, true, userRegionSet.size(),
1879             System.currentTimeMillis())) {
1880         LOG.debug("some user regions are still in transition: " + userRegionSet);
1881       }
1882       LOG.debug("Bulk assigning done for " + destination);
1883       return true;
1884     } finally {
1885       metricsAssignmentManager.updateBulkAssignTime(EnvironmentEdgeManager.currentTime() - startTime);
1886     }
1887   }
1888 
1889   /**
1890    * Send CLOSE RPC if the server is online, otherwise, offline the region.
1891    *
1892    * The RPC will be sent only to the region sever found in the region state
1893    * if it is passed in, otherwise, to the src server specified. If region
1894    * state is not specified, we don't update region state at all, instead
1895    * we just send the RPC call. This is useful for some cleanup without
1896    * messing around the region states (see handleRegion, on region opened
1897    * on an unexpected server scenario, for an example)
1898    */
1899   private void unassign(final HRegionInfo region,
1900       final RegionState state, final int versionOfClosingNode,
1901       final ServerName dest, final boolean transitionInZK,
1902       final ServerName src) {
1903     ServerName server = src;
1904     if (state != null) {
1905       server = state.getServerName();
1906     }
1907     long maxWaitTime = -1;
1908     for (int i = 1; i <= this.maximumAttempts; i++) {
1909       if (this.server.isStopped() || this.server.isAborted()) {
1910         LOG.debug("Server stopped/aborted; skipping unassign of " + region);
1911         return;
1912       }
1913       // ClosedRegionhandler can remove the server from this.regions
1914       if (!serverManager.isServerOnline(server)) {
1915         LOG.debug("Offline " + region.getRegionNameAsString()
1916           + ", no need to unassign since it's on a dead server: " + server);
1917         if (transitionInZK) {
1918           // delete the node. if no node exists need not bother.
1919           deleteClosingOrClosedNode(region, server);
1920         }
1921         if (state != null) {
1922           regionOffline(region);
1923         }
1924         return;
1925       }
1926       try {
1927         // Send CLOSE RPC
1928         if (serverManager.sendRegionClose(server, region,
1929           versionOfClosingNode, dest, transitionInZK)) {
1930           LOG.debug("Sent CLOSE to " + server + " for region " +
1931             region.getRegionNameAsString());
1932           if (useZKForAssignment && !transitionInZK && state != null) {
1933             // Retry to make sure the region is
1934             // closed so as to avoid double assignment.
1935             unassign(region, state, versionOfClosingNode,
1936               dest, transitionInZK, src);
1937           }
1938           return;
1939         }
1940         // This never happens. Currently regionserver close always return true.
1941         // Todo; this can now happen (0.96) if there is an exception in a coprocessor
1942         LOG.warn("Server " + server + " region CLOSE RPC returned false for " +
1943           region.getRegionNameAsString());
1944       } catch (Throwable t) {
1945         long sleepTime = 0;
1946         Configuration conf = this.server.getConfiguration();
1947         if (t instanceof RemoteException) {
1948           t = ((RemoteException)t).unwrapRemoteException();
1949         }
1950         boolean logRetries = true;
1951         if (t instanceof RegionServerAbortedException
1952             || t instanceof RegionServerStoppedException
1953             || t instanceof ServerNotRunningYetException) {
1954           // RS is aborting or stopping, we cannot offline the region since the region may need
1955           // to do WAL recovery. Until we see  the RS expiration, we should retry.
1956           sleepTime = 1 + conf.getInt(RpcClient.FAILED_SERVER_EXPIRY_KEY,
1957             RpcClient.FAILED_SERVER_EXPIRY_DEFAULT);
1958 
1959         } else if (t instanceof NotServingRegionException) {
1960           LOG.debug("Offline " + region.getRegionNameAsString()
1961             + ", it's not any more on " + server, t);
1962           if (transitionInZK) {
1963             deleteClosingOrClosedNode(region, server);
1964           }
1965           if (state != null) {
1966             regionOffline(region);
1967           }
1968           return;
1969         } else if ((t instanceof FailedServerException) || (state != null &&
1970             t instanceof RegionAlreadyInTransitionException)) {
1971           if(t instanceof FailedServerException) {
1972             sleepTime = 1 + conf.getInt(RpcClient.FAILED_SERVER_EXPIRY_KEY,
1973                   RpcClient.FAILED_SERVER_EXPIRY_DEFAULT);
1974           } else {
1975             // RS is already processing this region, only need to update the timestamp
1976             LOG.debug("update " + state + " the timestamp.");
1977             state.updateTimestampToNow();
1978             if (maxWaitTime < 0) {
1979               maxWaitTime =
1980                   EnvironmentEdgeManager.currentTime()
1981                       + conf.getLong(ALREADY_IN_TRANSITION_WAITTIME,
1982                         DEFAULT_ALREADY_IN_TRANSITION_WAITTIME);
1983             }
1984             long now = EnvironmentEdgeManager.currentTime();
1985             if (now < maxWaitTime) {
1986               LOG.debug("Region is already in transition; "
1987                 + "waiting up to " + (maxWaitTime - now) + "ms", t);
1988               sleepTime = 100;
1989               i--; // reset the try count
1990               logRetries = false;
1991             }
1992           }
1993         }
1994 
1995         try {
1996           if (sleepTime > 0) {
1997             Thread.sleep(sleepTime);
1998           }
1999         } catch (InterruptedException ie) {
2000           LOG.warn("Failed to unassign "
2001             + region.getRegionNameAsString() + " since interrupted", ie);
2002           Thread.currentThread().interrupt();
2003           if (state != null) {
2004             regionStates.updateRegionState(region, State.FAILED_CLOSE);
2005           }
2006           return;
2007         }
2008 
2009         if (logRetries) {
2010           LOG.info("Server " + server + " returned " + t + " for "
2011             + region.getRegionNameAsString() + ", try=" + i
2012             + " of " + this.maximumAttempts, t);
2013           // Presume retry or server will expire.
2014         }
2015       }
2016     }
2017     // Run out of attempts
2018     if (state != null) {
2019       regionStates.updateRegionState(region, State.FAILED_CLOSE);
2020     }
2021   }
2022 
2023   /**
2024    * Set region to OFFLINE unless it is opening and forceNewPlan is false.
2025    */
2026   private RegionState forceRegionStateToOffline(
2027       final HRegionInfo region, final boolean forceNewPlan) {
2028     RegionState state = regionStates.getRegionState(region);
2029     if (state == null) {
2030       LOG.warn("Assigning but not in region states: " + region);
2031       state = regionStates.createRegionState(region);
2032     }
2033 
2034     ServerName sn = state.getServerName();
2035     if (forceNewPlan && LOG.isDebugEnabled()) {
2036       LOG.debug("Force region state offline " + state);
2037     }
2038 
2039     switch (state.getState()) {
2040     case OPEN:
2041     case OPENING:
2042     case PENDING_OPEN:
2043     case CLOSING:
2044     case PENDING_CLOSE:
2045       if (!forceNewPlan) {
2046         LOG.debug("Skip assigning " +
2047           region + ", it is already " + state);
2048         return null;
2049       }
2050     case FAILED_CLOSE:
2051     case FAILED_OPEN:
2052       unassign(region, state, -1, null, false, null);
2053       state = regionStates.getRegionState(region);
2054       if (state.isFailedClose()) {
2055         // If we can't close the region, we can't re-assign
2056         // it so as to avoid possible double assignment/data loss.
2057         LOG.info("Skip assigning " +
2058           region + ", we couldn't close it: " + state);
2059         return null;
2060       }
2061     case OFFLINE:
2062       // This region could have been open on this server
2063       // for a while. If the server is dead and not processed
2064       // yet, we can move on only if the meta shows the
2065       // region is not on this server actually, or on a server
2066       // not dead, or dead and processed already.
2067       // In case not using ZK, we don't need this check because
2068       // we have the latest info in memory, and the caller
2069       // will do another round checking any way.
2070       if (useZKForAssignment
2071           && regionStates.isServerDeadAndNotProcessed(sn)
2072           && wasRegionOnDeadServerByMeta(region, sn)) {
2073         if (!regionStates.isRegionInTransition(region)) {
2074           LOG.info("Updating the state to " + State.OFFLINE + " to allow to be reassigned by SSH");
2075           regionStates.updateRegionState(region, State.OFFLINE);
2076         }
2077         LOG.info("Skip assigning " + region.getRegionNameAsString()
2078             + ", it is on a dead but not processed yet server: " + sn);
2079         return null;
2080       }
2081     case CLOSED:
2082       break;
2083     default:
2084       LOG.error("Trying to assign region " + region
2085         + ", which is " + state);
2086       return null;
2087     }
2088     return state;
2089   }
2090 
2091   @SuppressWarnings("deprecation")
2092   protected boolean wasRegionOnDeadServerByMeta(
2093       final HRegionInfo region, final ServerName sn) {
2094     try {
2095       if (region.isMetaRegion()) {
2096         ServerName server = this.server.getMetaTableLocator().
2097           getMetaRegionLocation(this.server.getZooKeeper());
2098         return regionStates.isServerDeadAndNotProcessed(server);
2099       }
2100       while (!server.isStopped()) {
2101         try {
2102           this.server.getMetaTableLocator().waitMetaRegionLocation(server.getZooKeeper());
2103           Result r = MetaTableAccessor.getRegionResult(server.getConnection(),
2104             region.getRegionName());
2105           if (r == null || r.isEmpty()) return false;
2106           ServerName server = HRegionInfo.getServerName(r);
2107           return regionStates.isServerDeadAndNotProcessed(server);
2108         } catch (IOException ioe) {
2109           LOG.info("Received exception accessing hbase:meta during force assign "
2110             + region.getRegionNameAsString() + ", retrying", ioe);
2111         }
2112       }
2113     } catch (InterruptedException e) {
2114       Thread.currentThread().interrupt();
2115       LOG.info("Interrupted accessing hbase:meta", e);
2116     }
2117     // Call is interrupted or server is stopped.
2118     return regionStates.isServerDeadAndNotProcessed(sn);
2119   }
2120 
2121   /**
2122    * Caller must hold lock on the passed <code>state</code> object.
2123    * @param state
2124    * @param setOfflineInZK
2125    * @param forceNewPlan
2126    */
2127   public void assign(RegionState state,
2128       boolean setOfflineInZK, final boolean forceNewPlan) {
2129     long startTime = EnvironmentEdgeManager.currentTime();
2130     try {
2131       Configuration conf = server.getConfiguration();
2132       RegionState currentState = state;
2133       int versionOfOfflineNode = -1;
2134       RegionPlan plan = null;
2135       long maxWaitTime = -1;
2136       HRegionInfo region = state.getRegion();
2137       RegionOpeningState regionOpenState;
2138       Throwable previousException = null;
2139       for (int i = 1; i <= maximumAttempts; i++) {
2140         if (server.isStopped() || server.isAborted()) {
2141           LOG.info("Skip assigning " + region.getRegionNameAsString()
2142             + ", the server is stopped/aborted");
2143           return;
2144         }
2145 
2146         if (plan == null) { // Get a server for the region at first
2147           try {
2148             plan = getRegionPlan(region, forceNewPlan);
2149           } catch (HBaseIOException e) {
2150             LOG.warn("Failed to get region plan", e);
2151           }
2152         }
2153 
2154         if (plan == null) {
2155           LOG.warn("Unable to determine a plan to assign " + region);
2156 
2157           // For meta region, we have to keep retrying until succeeding
2158           if (region.isMetaRegion()) {
2159             if (i == maximumAttempts) {
2160               i = 0; // re-set attempt count to 0 for at least 1 retry
2161 
2162               LOG.warn("Unable to determine a plan to assign a hbase:meta region " + region +
2163                 " after maximumAttempts (" + this.maximumAttempts +
2164                 "). Reset attempts count and continue retrying.");
2165             }
2166             waitForRetryingMetaAssignment();
2167             continue;
2168           }
2169 
2170           regionStates.updateRegionState(region, State.FAILED_OPEN);
2171           return;
2172         }
2173         if (setOfflineInZK && versionOfOfflineNode == -1) {
2174           LOG.info("Setting node as OFFLINED in ZooKeeper for region " + region);
2175           // get the version of the znode after setting it to OFFLINE.
2176           // versionOfOfflineNode will be -1 if the znode was not set to OFFLINE
2177           versionOfOfflineNode = setOfflineInZooKeeper(currentState, plan.getDestination());
2178           if (versionOfOfflineNode != -1) {
2179             if (isDisabledorDisablingRegionInRIT(region)) {
2180               return;
2181             }
2182             // In case of assignment from EnableTableHandler table state is ENABLING. Any how
2183             // EnableTableHandler will set ENABLED after assigning all the table regions. If we
2184             // try to set to ENABLED directly then client API may think table is enabled.
2185             // When we have a case such as all the regions are added directly into hbase:meta and we call
2186             // assignRegion then we need to make the table ENABLED. Hence in such case the table
2187             // will not be in ENABLING or ENABLED state.
2188             TableName tableName = region.getTable();
2189             if (!tableStateManager.isTableState(tableName,
2190               ZooKeeperProtos.Table.State.ENABLED, ZooKeeperProtos.Table.State.ENABLING)) {
2191               LOG.debug("Setting table " + tableName + " to ENABLED state.");
2192               setEnabledTable(tableName);
2193             }
2194           }
2195         }
2196         if (setOfflineInZK && versionOfOfflineNode == -1) {
2197           LOG.info("Unable to set offline in ZooKeeper to assign " + region);
2198           // Setting offline in ZK must have been failed due to ZK racing or some
2199           // exception which may make the server to abort. If it is ZK racing,
2200           // we should retry since we already reset the region state,
2201           // existing (re)assignment will fail anyway.
2202           if (!server.isAborted()) {
2203             continue;
2204           }
2205         }
2206         LOG.info("Assigning " + region.getRegionNameAsString() +
2207             " to " + plan.getDestination().toString());
2208         // Transition RegionState to PENDING_OPEN
2209         currentState = regionStates.updateRegionState(region,
2210           State.PENDING_OPEN, plan.getDestination());
2211 
2212         boolean needNewPlan;
2213         final String assignMsg = "Failed assignment of " + region.getRegionNameAsString() +
2214             " to " + plan.getDestination();
2215         try {
2216           List<ServerName> favoredNodes = ServerName.EMPTY_SERVER_LIST;
2217           if (this.shouldAssignRegionsWithFavoredNodes) {
2218             favoredNodes = ((FavoredNodeLoadBalancer)this.balancer).getFavoredNodes(region);
2219           }
2220           regionOpenState = serverManager.sendRegionOpen(
2221               plan.getDestination(), region, versionOfOfflineNode, favoredNodes);
2222 
2223           if (regionOpenState == RegionOpeningState.FAILED_OPENING) {
2224             // Failed opening this region, looping again on a new server.
2225             needNewPlan = true;
2226             LOG.warn(assignMsg + ", regionserver says 'FAILED_OPENING', " +
2227                 " trying to assign elsewhere instead; " +
2228                 "try=" + i + " of " + this.maximumAttempts);
2229           } else {
2230             // we're done
2231             if (regionOpenState == RegionOpeningState.ALREADY_OPENED) {
2232               processAlreadyOpenedRegion(region, plan.getDestination());
2233             }
2234             return;
2235           }
2236 
2237         } catch (Throwable t) {
2238           if (t instanceof RemoteException) {
2239             t = ((RemoteException) t).unwrapRemoteException();
2240           }
2241           previousException = t;
2242 
2243           // Should we wait a little before retrying? If the server is starting it's yes.
2244           // If the region is already in transition, it's yes as well: we want to be sure that
2245           //  the region will get opened but we don't want a double assignment.
2246           boolean hold = (t instanceof RegionAlreadyInTransitionException ||
2247               t instanceof ServerNotRunningYetException);
2248 
2249           // In case socket is timed out and the region server is still online,
2250           // the openRegion RPC could have been accepted by the server and
2251           // just the response didn't go through.  So we will retry to
2252           // open the region on the same server to avoid possible
2253           // double assignment.
2254           boolean retry = !hold && (t instanceof java.net.SocketTimeoutException
2255               && this.serverManager.isServerOnline(plan.getDestination()));
2256 
2257 
2258           if (hold) {
2259             LOG.warn(assignMsg + ", waiting a little before trying on the same region server " +
2260               "try=" + i + " of " + this.maximumAttempts, t);
2261 
2262             if (maxWaitTime < 0) {
2263               if (t instanceof RegionAlreadyInTransitionException) {
2264                 maxWaitTime = EnvironmentEdgeManager.currentTime()
2265                   + this.server.getConfiguration().getLong(ALREADY_IN_TRANSITION_WAITTIME,
2266                     DEFAULT_ALREADY_IN_TRANSITION_WAITTIME);
2267               } else {
2268                 maxWaitTime = EnvironmentEdgeManager.currentTime()
2269                   + this.server.getConfiguration().getLong(
2270                     "hbase.regionserver.rpc.startup.waittime", 60000);
2271               }
2272             }
2273             try {
2274               needNewPlan = false;
2275               long now = EnvironmentEdgeManager.currentTime();
2276               if (now < maxWaitTime) {
2277                 LOG.debug("Server is not yet up or region is already in transition; "
2278                   + "waiting up to " + (maxWaitTime - now) + "ms", t);
2279                 Thread.sleep(100);
2280                 i--; // reset the try count
2281               } else if (!(t instanceof RegionAlreadyInTransitionException)) {
2282                 LOG.debug("Server is not up for a while; try a new one", t);
2283                 needNewPlan = true;
2284               }
2285             } catch (InterruptedException ie) {
2286               LOG.warn("Failed to assign "
2287                   + region.getRegionNameAsString() + " since interrupted", ie);
2288               regionStates.updateRegionState(region, State.FAILED_OPEN);
2289               Thread.currentThread().interrupt();
2290               return;
2291             }
2292           } else if (retry) {
2293             needNewPlan = false;
2294             i--; // we want to retry as many times as needed as long as the RS is not dead.
2295             LOG.warn(assignMsg + ", trying to assign to the same region server due ", t);
2296           } else {
2297             needNewPlan = true;
2298             LOG.warn(assignMsg + ", trying to assign elsewhere instead;" +
2299                 " try=" + i + " of " + this.maximumAttempts, t);
2300           }
2301         }
2302 
2303         if (i == this.maximumAttempts) {
2304           // For meta region, we have to keep retrying until succeeding
2305           if (region.isMetaRegion()) {
2306             i = 0; // re-set attempt count to 0 for at least 1 retry
2307             LOG.warn(assignMsg +
2308                 ", trying to assign a hbase:meta region reached to maximumAttempts (" +
2309                 this.maximumAttempts + ").  Reset attempt counts and continue retrying.");
2310             waitForRetryingMetaAssignment();
2311           }
2312           else {
2313             // Don't reset the region state or get a new plan any more.
2314             // This is the last try.
2315             continue;
2316           }
2317         }
2318 
2319         // If region opened on destination of present plan, reassigning to new
2320         // RS may cause double assignments. In case of RegionAlreadyInTransitionException
2321         // reassigning to same RS.
2322         if (needNewPlan) {
2323           // Force a new plan and reassign. Will return null if no servers.
2324           // The new plan could be the same as the existing plan since we don't
2325           // exclude the server of the original plan, which should not be
2326           // excluded since it could be the only server up now.
2327           RegionPlan newPlan = null;
2328           try {
2329             newPlan = getRegionPlan(region, true);
2330           } catch (HBaseIOException e) {
2331             LOG.warn("Failed to get region plan", e);
2332           }
2333           if (newPlan == null) {
2334             regionStates.updateRegionState(region, State.FAILED_OPEN);
2335             LOG.warn("Unable to find a viable location to assign region " +
2336                 region.getRegionNameAsString());
2337             return;
2338           }
2339 
2340           if (plan != newPlan && !plan.getDestination().equals(newPlan.getDestination())) {
2341             // Clean out plan we failed execute and one that doesn't look like it'll
2342             // succeed anyways; we need a new plan!
2343             // Transition back to OFFLINE
2344             LOG.info("Region assignment plan changed from " + plan.getDestination() + " to "
2345                 + newPlan.getDestination() + " server.");
2346             currentState = regionStates.updateRegionState(region, State.OFFLINE);
2347             versionOfOfflineNode = -1;
2348             if (useZKForAssignment) {
2349               setOfflineInZK = true;
2350             }
2351             plan = newPlan;
2352           } else if(plan.getDestination().equals(newPlan.getDestination()) &&
2353               previousException instanceof FailedServerException) {
2354             try {
2355               LOG.info("Trying to re-assign " + region.getRegionNameAsString() +
2356                 " to the same failed server.");
2357               Thread.sleep(1 + conf.getInt(RpcClient.FAILED_SERVER_EXPIRY_KEY,
2358                 RpcClient.FAILED_SERVER_EXPIRY_DEFAULT));
2359             } catch (InterruptedException ie) {
2360               LOG.warn("Failed to assign "
2361                   + region.getRegionNameAsString() + " since interrupted", ie);
2362               regionStates.updateRegionState(region, State.FAILED_OPEN);
2363               Thread.currentThread().interrupt();
2364               return;
2365             }
2366           }
2367         }
2368       }
2369       // Run out of attempts
2370       regionStates.updateRegionState(region, State.FAILED_OPEN);
2371     } finally {
2372       metricsAssignmentManager.updateAssignmentTime(EnvironmentEdgeManager.currentTime() - startTime);
2373     }
2374   }
2375 
2376   private void processAlreadyOpenedRegion(HRegionInfo region, ServerName sn) {
2377     // Remove region from in-memory transition and unassigned node from ZK
2378     // While trying to enable the table the regions of the table were
2379     // already enabled.
2380     LOG.debug("ALREADY_OPENED " + region.getRegionNameAsString()
2381       + " to " + sn);
2382     String encodedName = region.getEncodedName();
2383     
2384     //If use ZkForAssignment, region already Opened event should not be handled, 
2385     //leave it to zk event. See HBase-14407.
2386     if(useZKForAssignment){
2387       String node = ZKAssign.getNodeName(watcher, encodedName);
2388       Stat stat = new Stat();
2389       try {
2390         byte[] existingBytes = ZKUtil.getDataNoWatch(watcher, node, stat);
2391         if(existingBytes!=null){
2392           RegionTransition rt= RegionTransition.parseFrom(existingBytes);
2393           EventType et = rt.getEventType();
2394           if (et.equals(EventType.RS_ZK_REGION_OPENED)) {
2395             LOG.debug("ALREADY_OPENED " + region.getRegionNameAsString()
2396               + " and node in "+et+" state");
2397             return;
2398           }
2399         }
2400       } catch (KeeperException ke) {
2401         LOG.warn("Unexpected ZK exception getData " + node
2402           + " node for the region " + encodedName, ke);
2403       } catch (DeserializationException e) {
2404         LOG.warn("Get RegionTransition from zk deserialization failed! ", e);
2405       }
2406       
2407       deleteNodeInStates(encodedName, "offline", sn, EventType.M_ZK_REGION_OFFLINE);
2408     }
2409     
2410     regionStates.regionOnline(region, sn);
2411   }
2412 
2413   private boolean isDisabledorDisablingRegionInRIT(final HRegionInfo region) {
2414     if (this.tableStateManager.isTableState(region.getTable(),
2415         ZooKeeperProtos.Table.State.DISABLED,
2416         ZooKeeperProtos.Table.State.DISABLING) || replicasToClose.contains(region)) {
2417       LOG.info("Table " + region.getTable() + " is disabled or disabling;"
2418         + " skipping assign of " + region.getRegionNameAsString());
2419       offlineDisabledRegion(region);
2420       return true;
2421     }
2422     return false;
2423   }
2424 
2425   /**
2426    * Set region as OFFLINED up in zookeeper
2427    *
2428    * @param state
2429    * @return the version of the offline node if setting of the OFFLINE node was
2430    *         successful, -1 otherwise.
2431    */
2432   private int setOfflineInZooKeeper(final RegionState state, final ServerName destination) {
2433     if (!state.isClosed() && !state.isOffline()) {
2434       String msg = "Unexpected state : " + state + " .. Cannot transit it to OFFLINE.";
2435       this.server.abort(msg, new IllegalStateException(msg));
2436       return -1;
2437     }
2438     regionStates.updateRegionState(state.getRegion(), State.OFFLINE);
2439     int versionOfOfflineNode;
2440     try {
2441       // get the version after setting the znode to OFFLINE
2442       versionOfOfflineNode = ZKAssign.createOrForceNodeOffline(watcher,
2443         state.getRegion(), destination);
2444       if (versionOfOfflineNode == -1) {
2445         LOG.warn("Attempted to create/force node into OFFLINE state before "
2446             + "completing assignment but failed to do so for " + state);
2447         return -1;
2448       }
2449     } catch (KeeperException e) {
2450       server.abort("Unexpected ZK exception creating/setting node OFFLINE", e);
2451       return -1;
2452     }
2453     return versionOfOfflineNode;
2454   }
2455 
2456   /**
2457    * @param region the region to assign
2458    * @return Plan for passed <code>region</code> (If none currently, it creates one or
2459    * if no servers to assign, it returns null).
2460    */
2461   private RegionPlan getRegionPlan(final HRegionInfo region,
2462       final boolean forceNewPlan)  throws HBaseIOException {
2463     return getRegionPlan(region, null, forceNewPlan);
2464   }
2465 
2466   /**
2467    * @param region the region to assign
2468    * @param serverToExclude Server to exclude (we know its bad). Pass null if
2469    * all servers are thought to be assignable.
2470    * @param forceNewPlan If true, then if an existing plan exists, a new plan
2471    * will be generated.
2472    * @return Plan for passed <code>region</code> (If none currently, it creates one or
2473    * if no servers to assign, it returns null).
2474    */
2475   private RegionPlan getRegionPlan(final HRegionInfo region,
2476       final ServerName serverToExclude, final boolean forceNewPlan) throws HBaseIOException {
2477     // Pickup existing plan or make a new one
2478     final String encodedName = region.getEncodedName();
2479     final List<ServerName> destServers =
2480       serverManager.createDestinationServersList(serverToExclude);
2481 
2482     if (destServers.isEmpty()){
2483       LOG.warn("Can't move " + encodedName +
2484         ", there is no destination server available.");
2485       return null;
2486     }
2487 
2488     RegionPlan randomPlan = null;
2489     boolean newPlan = false;
2490     RegionPlan existingPlan;
2491 
2492     synchronized (this.regionPlans) {
2493       existingPlan = this.regionPlans.get(encodedName);
2494 
2495       if (existingPlan != null && existingPlan.getDestination() != null) {
2496         LOG.debug("Found an existing plan for " + region.getRegionNameAsString()
2497           + " destination server is " + existingPlan.getDestination() +
2498             " accepted as a dest server = " + destServers.contains(existingPlan.getDestination()));
2499       }
2500 
2501       if (forceNewPlan
2502           || existingPlan == null
2503           || existingPlan.getDestination() == null
2504           || !destServers.contains(existingPlan.getDestination())) {
2505         newPlan = true;
2506       }
2507     }
2508 
2509     if (newPlan) {
2510       ServerName destination = balancer.randomAssignment(region, destServers);
2511       if (destination == null) {
2512         LOG.warn("Can't find a destination for " + encodedName);
2513         return null;
2514       }
2515       synchronized (this.regionPlans) {
2516         randomPlan = new RegionPlan(region, null, destination);
2517         if (!region.isMetaTable() && shouldAssignRegionsWithFavoredNodes) {
2518           List<HRegionInfo> regions = new ArrayList<HRegionInfo>(1);
2519           regions.add(region);
2520           try {
2521             processFavoredNodes(regions);
2522           } catch (IOException ie) {
2523             LOG.warn("Ignoring exception in processFavoredNodes " + ie);
2524           }
2525         }
2526         this.regionPlans.put(encodedName, randomPlan);
2527       }
2528       LOG.debug("No previous transition plan found (or ignoring " + "an existing plan) for "
2529           + region.getRegionNameAsString() + "; generated random plan=" + randomPlan + "; "
2530           + destServers.size() + " (online=" + serverManager.getOnlineServers().size()
2531           + ") available servers, forceNewPlan=" + forceNewPlan);
2532       return randomPlan;
2533     }
2534     LOG.debug("Using pre-existing plan for " +
2535       region.getRegionNameAsString() + "; plan=" + existingPlan);
2536     return existingPlan;
2537   }
2538 
2539   /**
2540    * Wait for some time before retrying meta table region assignment
2541    */
2542   private void waitForRetryingMetaAssignment() {
2543     try {
2544       Thread.sleep(this.sleepTimeBeforeRetryingMetaAssignment);
2545     } catch (InterruptedException e) {
2546       LOG.error("Got exception while waiting for hbase:meta assignment");
2547       Thread.currentThread().interrupt();
2548     }
2549   }
2550 
2551   /**
2552    * Unassigns the specified region.
2553    * <p>
2554    * Updates the RegionState and sends the CLOSE RPC unless region is being
2555    * split by regionserver; then the unassign fails (silently) because we
2556    * presume the region being unassigned no longer exists (its been split out
2557    * of existence). TODO: What to do if split fails and is rolled back and
2558    * parent is revivified?
2559    * <p>
2560    * If a RegionPlan is already set, it will remain.
2561    *
2562    * @param region server to be unassigned
2563    */
2564   public void unassign(HRegionInfo region) {
2565     unassign(region, false);
2566   }
2567 
2568 
2569   /**
2570    * Unassigns the specified region.
2571    * <p>
2572    * Updates the RegionState and sends the CLOSE RPC unless region is being
2573    * split by regionserver; then the unassign fails (silently) because we
2574    * presume the region being unassigned no longer exists (its been split out
2575    * of existence). TODO: What to do if split fails and is rolled back and
2576    * parent is revivified?
2577    * <p>
2578    * If a RegionPlan is already set, it will remain.
2579    *
2580    * @param region server to be unassigned
2581    * @param force if region should be closed even if already closing
2582    */
2583   public void unassign(HRegionInfo region, boolean force, ServerName dest) {
2584     // TODO: Method needs refactoring.  Ugly buried returns throughout.  Beware!
2585     LOG.debug("Starting unassign of " + region.getRegionNameAsString()
2586       + " (offlining), current state: " + regionStates.getRegionState(region));
2587 
2588     String encodedName = region.getEncodedName();
2589     // Grab the state of this region and synchronize on it
2590     int versionOfClosingNode = -1;
2591     // We need a lock here as we're going to do a put later and we don't want multiple states
2592     //  creation
2593     ReentrantLock lock = locker.acquireLock(encodedName);
2594     RegionState state = regionStates.getRegionTransitionState(encodedName);
2595     boolean reassign = true;
2596     try {
2597       if (state == null) {
2598         // Region is not in transition.
2599         // We can unassign it only if it's not SPLIT/MERGED.
2600         state = regionStates.getRegionState(encodedName);
2601         if (state != null && state.isUnassignable()) {
2602           LOG.info("Attempting to unassign " + state + ", ignored");
2603           // Offline region will be reassigned below
2604           return;
2605         }
2606         // Create the znode in CLOSING state
2607         try {
2608           if (state == null || state.getServerName() == null) {
2609             // We don't know where the region is, offline it.
2610             // No need to send CLOSE RPC
2611             LOG.warn("Attempting to unassign a region not in RegionStates "
2612               + region.getRegionNameAsString() + ", offlined");
2613             regionOffline(region);
2614             return;
2615           }
2616           if (useZKForAssignment) {
2617             versionOfClosingNode = ZKAssign.createNodeClosing(
2618               watcher, region, state.getServerName());
2619             if (versionOfClosingNode == -1) {
2620               LOG.info("Attempting to unassign " +
2621                 region.getRegionNameAsString() + " but ZK closing node "
2622                 + "can't be created.");
2623               reassign = false; // not unassigned at all
2624               return;
2625             }
2626           }
2627         } catch (KeeperException e) {
2628           if (e instanceof NodeExistsException) {
2629             // Handle race between master initiated close and regionserver
2630             // orchestrated splitting. See if existing node is in a
2631             // SPLITTING or SPLIT state.  If so, the regionserver started
2632             // an op on node before we could get our CLOSING in.  Deal.
2633             NodeExistsException nee = (NodeExistsException)e;
2634             String path = nee.getPath();
2635             try {
2636               if (isSplitOrSplittingOrMergedOrMerging(path)) {
2637                 LOG.debug(path + " is SPLIT or SPLITTING or MERGED or MERGING; " +
2638                   "skipping unassign because region no longer exists -- its split or merge");
2639                 reassign = false; // no need to reassign for split/merged region
2640                 return;
2641               }
2642             } catch (KeeperException.NoNodeException ke) {
2643               LOG.warn("Failed getData on SPLITTING/SPLIT at " + path +
2644                 "; presuming split and that the region to unassign, " +
2645                 encodedName + ", no longer exists -- confirm", ke);
2646               return;
2647             } catch (KeeperException ke) {
2648               LOG.error("Unexpected zk state", ke);
2649             } catch (DeserializationException de) {
2650               LOG.error("Failed parse", de);
2651             }
2652           }
2653           // If we get here, don't understand whats going on -- abort.
2654           server.abort("Unexpected ZK exception creating node CLOSING", e);
2655           reassign = false; // heading out already
2656           return;
2657         }
2658         state = regionStates.updateRegionState(region, State.PENDING_CLOSE);
2659       } else if (state.isFailedOpen()) {
2660         // The region is not open yet
2661         regionOffline(region);
2662         return;
2663       } else if (force && state.isPendingCloseOrClosing()) {
2664         LOG.debug("Attempting to unassign " + region.getRegionNameAsString() +
2665           " which is already " + state.getState()  +
2666           " but forcing to send a CLOSE RPC again ");
2667         if (state.isFailedClose()) {
2668           state = regionStates.updateRegionState(region, State.PENDING_CLOSE);
2669         }
2670         state.updateTimestampToNow();
2671       } else {
2672         LOG.debug("Attempting to unassign " +
2673           region.getRegionNameAsString() + " but it is " +
2674           "already in transition (" + state.getState() + ", force=" + force + ")");
2675         return;
2676       }
2677 
2678       unassign(region, state, versionOfClosingNode, dest, useZKForAssignment, null);
2679     } finally {
2680       lock.unlock();
2681 
2682       // Region is expected to be reassigned afterwards
2683       if (!replicasToClose.contains(region) && reassign && regionStates.isRegionOffline(region)) {
2684         assign(region, true);
2685       }
2686     }
2687   }
2688 
2689   public void unassign(HRegionInfo region, boolean force){
2690      unassign(region, force, null);
2691   }
2692 
2693   /**
2694    * @param region regioninfo of znode to be deleted.
2695    */
2696   public void deleteClosingOrClosedNode(HRegionInfo region, ServerName sn) {
2697     String encodedName = region.getEncodedName();
2698     deleteNodeInStates(encodedName, "closing", sn, EventType.M_ZK_REGION_CLOSING,
2699       EventType.RS_ZK_REGION_CLOSED);
2700   }
2701 
2702   /**
2703    * @param path
2704    * @return True if znode is in SPLIT or SPLITTING or MERGED or MERGING state.
2705    * @throws KeeperException Can happen if the znode went away in meantime.
2706    * @throws DeserializationException
2707    */
2708   private boolean isSplitOrSplittingOrMergedOrMerging(final String path)
2709       throws KeeperException, DeserializationException {
2710     boolean result = false;
2711     // This may fail if the SPLIT or SPLITTING or MERGED or MERGING znode gets
2712     // cleaned up before we can get data from it.
2713     byte [] data = ZKAssign.getData(watcher, path);
2714     if (data == null) {
2715       LOG.info("Node " + path + " is gone");
2716       return false;
2717     }
2718     RegionTransition rt = RegionTransition.parseFrom(data);
2719     switch (rt.getEventType()) {
2720     case RS_ZK_REQUEST_REGION_SPLIT:
2721     case RS_ZK_REGION_SPLIT:
2722     case RS_ZK_REGION_SPLITTING:
2723     case RS_ZK_REQUEST_REGION_MERGE:
2724     case RS_ZK_REGION_MERGED:
2725     case RS_ZK_REGION_MERGING:
2726       result = true;
2727       break;
2728     default:
2729       LOG.info("Node " + path + " is in " + rt.getEventType());
2730       break;
2731     }
2732     return result;
2733   }
2734 
2735   /**
2736    * Used by unit tests. Return the number of regions opened so far in the life
2737    * of the master. Increases by one every time the master opens a region
2738    * @return the counter value of the number of regions opened so far
2739    */
2740   public int getNumRegionsOpened() {
2741     return numRegionsOpened.get();
2742   }
2743 
2744   /**
2745    * Waits until the specified region has completed assignment.
2746    * <p>
2747    * If the region is already assigned, returns immediately.  Otherwise, method
2748    * blocks until the region is assigned.
2749    * @param regionInfo region to wait on assignment for
2750    * @return true if the region is assigned false otherwise.
2751    * @throws InterruptedException
2752    */
2753   public boolean waitForAssignment(HRegionInfo regionInfo)
2754       throws InterruptedException {
2755     ArrayList<HRegionInfo> regionSet = new ArrayList<HRegionInfo>(1);
2756     regionSet.add(regionInfo);
2757     return waitForAssignment(regionSet, true, Long.MAX_VALUE);
2758   }
2759 
2760   /**
2761    * Waits until the specified region has completed assignment, or the deadline is reached.
2762    */
2763   protected boolean waitForAssignment(final Collection<HRegionInfo> regionSet,
2764       final boolean waitTillAllAssigned, final int reassigningRegions,
2765       final long minEndTime) throws InterruptedException {
2766     long deadline = minEndTime + bulkPerRegionOpenTimeGuesstimate * (reassigningRegions + 1);
2767     if (deadline < 0) { // Overflow
2768       deadline = Long.MAX_VALUE; // wait forever
2769     }
2770     return waitForAssignment(regionSet, waitTillAllAssigned, deadline);
2771   }
2772 
2773   /**
2774    * Waits until the specified region has completed assignment, or the deadline is reached.
2775    * @param regionSet set of region to wait on. the set is modified and the assigned regions removed
2776    * @param waitTillAllAssigned true if we should wait all the regions to be assigned
2777    * @param deadline the timestamp after which the wait is aborted
2778    * @return true if all the regions are assigned false otherwise.
2779    * @throws InterruptedException
2780    */
2781   protected boolean waitForAssignment(final Collection<HRegionInfo> regionSet,
2782       final boolean waitTillAllAssigned, final long deadline) throws InterruptedException {
2783     // We're not synchronizing on regionsInTransition now because we don't use any iterator.
2784     while (!regionSet.isEmpty() && !server.isStopped() && deadline > System.currentTimeMillis()) {
2785       int failedOpenCount = 0;
2786       Iterator<HRegionInfo> regionInfoIterator = regionSet.iterator();
2787       while (regionInfoIterator.hasNext()) {
2788         HRegionInfo hri = regionInfoIterator.next();
2789         if (regionStates.isRegionOnline(hri) || regionStates.isRegionInState(hri,
2790             State.SPLITTING, State.SPLIT, State.MERGING, State.MERGED)) {
2791           regionInfoIterator.remove();
2792         } else if (regionStates.isRegionInState(hri, State.FAILED_OPEN)) {
2793           failedOpenCount++;
2794         }
2795       }
2796       if (!waitTillAllAssigned) {
2797         // No need to wait, let assignment going on asynchronously
2798         break;
2799       }
2800       if (!regionSet.isEmpty()) {
2801         if (failedOpenCount == regionSet.size()) {
2802           // all the regions we are waiting had an error on open.
2803           break;
2804         }
2805         regionStates.waitForUpdate(100);
2806       }
2807     }
2808     return regionSet.isEmpty();
2809   }
2810 
2811   /**
2812    * Assigns the hbase:meta region or a replica.
2813    * <p>
2814    * Assumes that hbase:meta is currently closed and is not being actively served by
2815    * any RegionServer.
2816    * <p>
2817    * Forcibly unsets the current meta region location in ZooKeeper and assigns
2818    * hbase:meta to a random RegionServer.
2819    * @param hri TODO
2820    * @throws KeeperException
2821    */
2822   public void assignMeta(HRegionInfo hri) throws KeeperException {
2823     this.server.getMetaTableLocator().deleteMetaLocation(this.watcher, hri.getReplicaId());
2824     assign(hri, true);
2825   }
2826 
2827   /**
2828    * Assigns specified regions retaining assignments, if any.
2829    * <p>
2830    * This is a synchronous call and will return once every region has been
2831    * assigned.  If anything fails, an exception is thrown
2832    * @throws InterruptedException
2833    * @throws IOException
2834    */
2835   public void assign(Map<HRegionInfo, ServerName> regions)
2836         throws IOException, InterruptedException {
2837     if (regions == null || regions.isEmpty()) {
2838       return;
2839     }
2840     List<ServerName> servers = serverManager.createDestinationServersList();
2841     if (servers == null || servers.isEmpty()) {
2842       throw new IOException("Found no destination server to assign region(s)");
2843     }
2844 
2845     // Reuse existing assignment info
2846     Map<ServerName, List<HRegionInfo>> bulkPlan =
2847       balancer.retainAssignment(regions, servers);
2848     if (bulkPlan == null) {
2849       throw new IOException("Unable to determine a plan to assign region(s)");
2850     }
2851 
2852     assign(regions.size(), servers.size(),
2853       "retainAssignment=true", bulkPlan);
2854   }
2855 
2856   /**
2857    * Assigns specified regions round robin, if any.
2858    * <p>
2859    * This is a synchronous call and will return once every region has been
2860    * assigned.  If anything fails, an exception is thrown
2861    * @throws InterruptedException
2862    * @throws IOException
2863    */
2864   public void assign(List<HRegionInfo> regions)
2865         throws IOException, InterruptedException {
2866     if (regions == null || regions.isEmpty()) {
2867       return;
2868     }
2869 
2870     List<ServerName> servers = serverManager.createDestinationServersList();
2871     if (servers == null || servers.isEmpty()) {
2872       throw new IOException("Found no destination server to assign region(s)");
2873     }
2874 
2875     // Generate a round-robin bulk assignment plan
2876     Map<ServerName, List<HRegionInfo>> bulkPlan = balancer.roundRobinAssignment(regions, servers);
2877     if (bulkPlan == null) {
2878       throw new IOException("Unable to determine a plan to assign region(s)");
2879     }
2880 
2881     processFavoredNodes(regions);
2882     assign(regions.size(), servers.size(), "round-robin=true", bulkPlan);
2883   }
2884 
2885   private void assign(int regions, int totalServers,
2886       String message, Map<ServerName, List<HRegionInfo>> bulkPlan)
2887           throws InterruptedException, IOException {
2888 
2889     int servers = bulkPlan.size();
2890     if (servers == 1 || (regions < bulkAssignThresholdRegions
2891         && servers < bulkAssignThresholdServers)) {
2892 
2893       // Not use bulk assignment.  This could be more efficient in small
2894       // cluster, especially mini cluster for testing, so that tests won't time out
2895       if (LOG.isTraceEnabled()) {
2896         LOG.trace("Not using bulk assignment since we are assigning only " + regions +
2897           " region(s) to " + servers + " server(s)");
2898       }
2899 
2900       // invoke assignment (async)
2901       ArrayList<HRegionInfo> userRegionSet = new ArrayList<HRegionInfo>(regions);
2902       for (Map.Entry<ServerName, List<HRegionInfo>> plan: bulkPlan.entrySet()) {
2903         if (!assign(plan.getKey(), plan.getValue())) {
2904           for (HRegionInfo region: plan.getValue()) {
2905             if (!regionStates.isRegionOnline(region)) {
2906               invokeAssign(region);
2907               if (!region.getTable().isSystemTable()) {
2908                 userRegionSet.add(region);
2909               }
2910             }
2911           }
2912         }
2913       }
2914 
2915       // wait for assignment completion
2916       if (!waitForAssignment(userRegionSet, true, userRegionSet.size(),
2917             System.currentTimeMillis())) {
2918         LOG.debug("some user regions are still in transition: " + userRegionSet);
2919       }
2920     } else {
2921       LOG.info("Bulk assigning " + regions + " region(s) across "
2922         + totalServers + " server(s), " + message);
2923 
2924       // Use fixed count thread pool assigning.
2925       BulkAssigner ba = new GeneralBulkAssigner(
2926         this.server, bulkPlan, this, bulkAssignWaitTillAllAssigned);
2927       ba.bulkAssign();
2928       LOG.info("Bulk assigning done");
2929     }
2930   }
2931 
2932   /**
2933    * Assigns all user regions, if any exist.  Used during cluster startup.
2934    * <p>
2935    * This is a synchronous call and will return once every region has been
2936    * assigned.  If anything fails, an exception is thrown and the cluster
2937    * should be shutdown.
2938    * @throws InterruptedException
2939    * @throws IOException
2940    */
2941   private void assignAllUserRegions(Map<HRegionInfo, ServerName> allRegions)
2942       throws IOException, InterruptedException {
2943     if (allRegions == null || allRegions.isEmpty()) return;
2944 
2945     // Determine what type of assignment to do on startup
2946     boolean retainAssignment = server.getConfiguration().
2947       getBoolean("hbase.master.startup.retainassign", true);
2948 
2949     Set<HRegionInfo> regionsFromMetaScan = allRegions.keySet();
2950     if (retainAssignment) {
2951       assign(allRegions);
2952     } else {
2953       List<HRegionInfo> regions = new ArrayList<HRegionInfo>(regionsFromMetaScan);
2954       assign(regions);
2955     }
2956 
2957     for (HRegionInfo hri : regionsFromMetaScan) {
2958       TableName tableName = hri.getTable();
2959       if (!tableStateManager.isTableState(tableName,
2960           ZooKeeperProtos.Table.State.ENABLED)) {
2961         setEnabledTable(tableName);
2962       }
2963     }
2964     // assign all the replicas that were not recorded in the meta
2965     assign(replicaRegionsNotRecordedInMeta(regionsFromMetaScan, server));
2966   }
2967 
2968   /**
2969    * Get a list of replica regions that are:
2970    * not recorded in meta yet. We might not have recorded the locations
2971    * for the replicas since the replicas may not have been online yet, master restarted
2972    * in the middle of assigning, ZK erased, etc.
2973    * @param regionsRecordedInMeta the list of regions we know are recorded in meta
2974    * either as a default, or, as the location of a replica
2975    * @param master
2976    * @return list of replica regions
2977    * @throws IOException
2978    */
2979   public static List<HRegionInfo> replicaRegionsNotRecordedInMeta(
2980       Set<HRegionInfo> regionsRecordedInMeta, MasterServices master)throws IOException {
2981     List<HRegionInfo> regionsNotRecordedInMeta = new ArrayList<HRegionInfo>();
2982     for (HRegionInfo hri : regionsRecordedInMeta) {
2983       TableName table = hri.getTable();
2984       HTableDescriptor htd = master.getTableDescriptors().get(table);
2985       // look at the HTD for the replica count. That's the source of truth
2986       int desiredRegionReplication = htd.getRegionReplication();
2987       for (int i = 0; i < desiredRegionReplication; i++) {
2988         HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
2989         if (regionsRecordedInMeta.contains(replica)) continue;
2990         regionsNotRecordedInMeta.add(replica);
2991       }
2992     }
2993     return regionsNotRecordedInMeta;
2994   }
2995 
2996   /**
2997    * Wait until no regions in transition.
2998    * @param timeout How long to wait.
2999    * @return True if nothing in regions in transition.
3000    * @throws InterruptedException
3001    */
3002   boolean waitUntilNoRegionsInTransition(final long timeout)
3003       throws InterruptedException {
3004     // Blocks until there are no regions in transition. It is possible that
3005     // there
3006     // are regions in transition immediately after this returns but guarantees
3007     // that if it returns without an exception that there was a period of time
3008     // with no regions in transition from the point-of-view of the in-memory
3009     // state of the Master.
3010     final long endTime = System.currentTimeMillis() + timeout;
3011 
3012     while (!this.server.isStopped() && regionStates.isRegionsInTransition()
3013         && endTime > System.currentTimeMillis()) {
3014       regionStates.waitForUpdate(100);
3015     }
3016 
3017     return !regionStates.isRegionsInTransition();
3018   }
3019 
3020   /**
3021    * Rebuild the list of user regions and assignment information.
3022    * Updates regionstates with findings as we go through list of regions.
3023    * @return set of servers not online that hosted some regions according to a scan of hbase:meta
3024    * @throws IOException
3025    */
3026   Set<ServerName> rebuildUserRegions() throws
3027       IOException, KeeperException, CoordinatedStateException {
3028     Set<TableName> disabledOrEnablingTables = tableStateManager.getTablesInStates(
3029       ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.ENABLING);
3030 
3031     Set<TableName> disabledOrDisablingOrEnabling = tableStateManager.getTablesInStates(
3032       ZooKeeperProtos.Table.State.DISABLED,
3033       ZooKeeperProtos.Table.State.DISABLING,
3034       ZooKeeperProtos.Table.State.ENABLING);
3035 
3036     // Region assignment from META
3037     List<Result> results = MetaTableAccessor.fullScanOfMeta(server.getConnection());
3038     // Get any new but slow to checkin region server that joined the cluster
3039     Set<ServerName> onlineServers = serverManager.getOnlineServers().keySet();
3040     // Set of offline servers to be returned
3041     Set<ServerName> offlineServers = new HashSet<ServerName>();
3042     // Iterate regions in META
3043     for (Result result : results) {
3044       if (result == null && LOG.isDebugEnabled()){
3045         LOG.debug("null result from meta - ignoring but this is strange.");
3046         continue;
3047       }
3048       // keep a track of replicas to close. These were the replicas of the originally
3049       // unmerged regions. The master might have closed them before but it mightn't
3050       // maybe because it crashed.
3051       PairOfSameType<HRegionInfo> p = MetaTableAccessor.getMergeRegions(result);
3052       if (p.getFirst() != null && p.getSecond() != null) {
3053         int numReplicas = server.getTableDescriptors().get(p.getFirst().
3054             getTable()).getRegionReplication();
3055         for (HRegionInfo merge : p) {
3056           for (int i = 1; i < numReplicas; i++) {
3057             replicasToClose.add(RegionReplicaUtil.getRegionInfoForReplica(merge, i));
3058           }
3059         }
3060       }
3061       RegionLocations rl =  MetaTableAccessor.getRegionLocations(result);
3062       if (rl == null) continue;
3063       HRegionLocation[] locations = rl.getRegionLocations();
3064       if (locations == null) continue;
3065       for (HRegionLocation hrl : locations) {
3066         if (hrl == null) continue;
3067         HRegionInfo regionInfo = hrl.getRegionInfo();
3068         if (regionInfo == null) continue;
3069         int replicaId = regionInfo.getReplicaId();
3070         State state = RegionStateStore.getRegionState(result, replicaId);
3071         // keep a track of replicas to close. These were the replicas of the split parents
3072         // from the previous life of the master. The master should have closed them before
3073         // but it couldn't maybe because it crashed
3074         if (replicaId == 0 && state.equals(State.SPLIT)) {
3075           for (HRegionLocation h : locations) {
3076             replicasToClose.add(h.getRegionInfo());
3077           }
3078         }
3079         ServerName lastHost = hrl.getServerName();
3080         ServerName regionLocation = RegionStateStore.getRegionServer(result, replicaId);
3081         if (tableStateManager.isTableState(regionInfo.getTable(),
3082              ZooKeeperProtos.Table.State.DISABLED)) {
3083           // force region to forget it hosts for disabled/disabling tables.
3084           // see HBASE-13326
3085           lastHost = null;
3086           regionLocation = null;
3087         }
3088         regionStates.createRegionState(regionInfo, state, regionLocation, lastHost);
3089         if (!regionStates.isRegionInState(regionInfo, State.OPEN)) {
3090           // Region is not open (either offline or in transition), skip
3091           continue;
3092         }
3093         TableName tableName = regionInfo.getTable();
3094         if (!onlineServers.contains(regionLocation)) {
3095           // Region is located on a server that isn't online
3096           offlineServers.add(regionLocation);
3097           if (useZKForAssignment) {
3098             regionStates.regionOffline(regionInfo);
3099           }
3100         } else if (!disabledOrEnablingTables.contains(tableName)) {
3101           // Region is being served and on an active server
3102           // add only if region not in disabled or enabling table
3103           regionStates.regionOnline(regionInfo, regionLocation);
3104           balancer.regionOnline(regionInfo, regionLocation);
3105         } else if (useZKForAssignment) {
3106           regionStates.regionOffline(regionInfo);
3107         }
3108         // need to enable the table if not disabled or disabling or enabling
3109         // this will be used in rolling restarts
3110         if (!disabledOrDisablingOrEnabling.contains(tableName)
3111           && !getTableStateManager().isTableState(tableName,
3112             ZooKeeperProtos.Table.State.ENABLED)) {
3113           setEnabledTable(tableName);
3114         }
3115       }
3116     }
3117     return offlineServers;
3118   }
3119 
3120   /**
3121    * Recover the tables that were not fully moved to DISABLED state. These
3122    * tables are in DISABLING state when the master restarted/switched.
3123    *
3124    * @throws KeeperException
3125    * @throws TableNotFoundException
3126    * @throws IOException
3127    */
3128   private void recoverTableInDisablingState()
3129       throws KeeperException, IOException, CoordinatedStateException {
3130     Set<TableName> disablingTables =
3131       tableStateManager.getTablesInStates(ZooKeeperProtos.Table.State.DISABLING);
3132     if (disablingTables.size() != 0) {
3133       for (TableName tableName : disablingTables) {
3134         // Recover by calling DisableTableHandler
3135         LOG.info("The table " + tableName
3136             + " is in DISABLING state.  Hence recovering by moving the table"
3137             + " to DISABLED state.");
3138         new DisableTableHandler(this.server, tableName,
3139             this, tableLockManager, true).prepare().process();
3140       }
3141     }
3142   }
3143 
3144   /**
3145    * Recover the tables that are not fully moved to ENABLED state. These tables
3146    * are in ENABLING state when the master restarted/switched
3147    *
3148    * @throws KeeperException
3149    * @throws org.apache.hadoop.hbase.TableNotFoundException
3150    * @throws IOException
3151    */
3152   private void recoverTableInEnablingState()
3153       throws KeeperException, IOException, CoordinatedStateException {
3154     Set<TableName> enablingTables = tableStateManager.
3155       getTablesInStates(ZooKeeperProtos.Table.State.ENABLING);
3156     if (enablingTables.size() != 0) {
3157       for (TableName tableName : enablingTables) {
3158         // Recover by calling EnableTableHandler
3159         LOG.info("The table " + tableName
3160             + " is in ENABLING state.  Hence recovering by moving the table"
3161             + " to ENABLED state.");
3162         // enableTable in sync way during master startup,
3163         // no need to invoke coprocessor
3164         EnableTableHandler eth = new EnableTableHandler(this.server, tableName,
3165           this, tableLockManager, true);
3166         try {
3167           eth.prepare();
3168         } catch (TableNotFoundException e) {
3169           LOG.warn("Table " + tableName + " not found in hbase:meta to recover.");
3170           continue;
3171         }
3172         eth.process();
3173       }
3174     }
3175   }
3176 
3177   /**
3178    * Processes list of dead servers from result of hbase:meta scan and regions in RIT.
3179    * This is used for failover to recover the lost regions that belonged to
3180    * RegionServers which failed while there was no active master or are offline for whatever
3181    * reason and for regions that were in RIT.
3182    *
3183    * @param deadServers
3184    *          The list of dead servers which failed while there was no active master. Can be null.
3185    * @throws IOException
3186    * @throws KeeperException
3187    */
3188   private void processDeadServersAndRecoverLostRegions(Set<ServerName> deadServers)
3189   throws IOException, KeeperException {
3190     if (deadServers != null && !deadServers.isEmpty()) {
3191       for (ServerName serverName: deadServers) {
3192         if (!serverManager.isServerDead(serverName)) {
3193           serverManager.expireServer(serverName); // Let SSH do region re-assign
3194         }
3195       }
3196     }
3197 
3198     List<String> nodes = useZKForAssignment ?
3199       ZKUtil.listChildrenAndWatchForNewChildren(watcher, watcher.assignmentZNode)
3200       : ZKUtil.listChildrenNoWatch(watcher, watcher.assignmentZNode);
3201     if (nodes != null && !nodes.isEmpty()) {
3202       for (String encodedRegionName : nodes) {
3203         processRegionInTransition(encodedRegionName, null);
3204       }
3205     } else if (!useZKForAssignment) {
3206       processRegionInTransitionZkLess();
3207     }
3208   }
3209 
3210   void processRegionInTransitionZkLess() {
3211     // We need to send RPC call again for PENDING_OPEN/PENDING_CLOSE regions
3212     // in case the RPC call is not sent out yet before the master was shut down
3213     // since we update the state before we send the RPC call. We can't update
3214     // the state after the RPC call. Otherwise, we don't know what's happened
3215     // to the region if the master dies right after the RPC call is out.
3216     Map<String, RegionState> rits = regionStates.getRegionsInTransition();
3217     for (RegionState regionState : rits.values()) {
3218       LOG.info("Processing " + regionState);
3219       ServerName serverName = regionState.getServerName();
3220       // Server could be null in case of FAILED_OPEN when master cannot find a region plan. In that
3221       // case, try assigning it here.
3222       if (serverName != null
3223           && !serverManager.getOnlineServers().containsKey(serverName)) {
3224         LOG.info("Server " + serverName + " isn't online. SSH will handle this");
3225         continue;
3226       }
3227       HRegionInfo regionInfo = regionState.getRegion();
3228       State state = regionState.getState();
3229 
3230       switch (state) {
3231       case CLOSED:
3232         invokeAssign(regionInfo);
3233         break;
3234       case PENDING_OPEN:
3235         retrySendRegionOpen(regionState);
3236         break;
3237       case PENDING_CLOSE:
3238         retrySendRegionClose(regionState);
3239         break;
3240       case FAILED_CLOSE:
3241       case FAILED_OPEN:
3242         invokeUnAssign(regionInfo);
3243         break;
3244       default:
3245         // No process for other states
3246       }
3247     }
3248   }
3249 
3250   /**
3251    * At master failover, for pending_open region, make sure
3252    * sendRegionOpen RPC call is sent to the target regionserver
3253    */
3254   private void retrySendRegionOpen(final RegionState regionState) {
3255     this.executorService.submit(
3256       new EventHandler(server, EventType.M_MASTER_RECOVERY) {
3257         @Override
3258         public void process() throws IOException {
3259           HRegionInfo hri = regionState.getRegion();
3260           ServerName serverName = regionState.getServerName();
3261           ReentrantLock lock = locker.acquireLock(hri.getEncodedName());
3262           try {
3263             for (int i = 1; i <= maximumAttempts; i++) {
3264               if (!serverManager.isServerOnline(serverName)
3265                   || server.isStopped() || server.isAborted()) {
3266                 return; // No need any more
3267               }
3268               try {
3269                 if (!regionState.equals(regionStates.getRegionState(hri))) {
3270                   return; // Region is not in the expected state any more
3271                 }
3272                 List<ServerName> favoredNodes = ServerName.EMPTY_SERVER_LIST;
3273                 if (shouldAssignRegionsWithFavoredNodes) {
3274                   favoredNodes = ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(hri);
3275                 }
3276                 RegionOpeningState regionOpenState = serverManager.sendRegionOpen(
3277                   serverName, hri, -1, favoredNodes);
3278 
3279                 if (regionOpenState == RegionOpeningState.FAILED_OPENING) {
3280                   // Failed opening this region, this means the target server didn't get
3281                   // the original region open RPC, so re-assign it with a new plan
3282                   LOG.debug("Got failed_opening in retry sendRegionOpen for "
3283                     + regionState + ", re-assign it");
3284                   invokeAssign(hri, true);
3285                 }
3286                 return; // Done.
3287               } catch (Throwable t) {
3288                 if (t instanceof RemoteException) {
3289                   t = ((RemoteException) t).unwrapRemoteException();
3290                 }
3291                 // In case SocketTimeoutException/FailedServerException, retry
3292                 if (t instanceof java.net.SocketTimeoutException
3293                     || t instanceof FailedServerException) {
3294                   Threads.sleep(100);
3295                   continue;
3296                 }
3297                 // For other exceptions, re-assign it
3298                 LOG.debug("Got exception in retry sendRegionOpen for "
3299                   + regionState + ", re-assign it", t);
3300                 invokeAssign(hri);
3301                 return; // Done.
3302               }
3303             }
3304           } finally {
3305             lock.unlock();
3306           }
3307         }
3308       });
3309   }
3310 
3311   /**
3312    * At master failover, for pending_close region, make sure
3313    * sendRegionClose RPC call is sent to the target regionserver
3314    */
3315   private void retrySendRegionClose(final RegionState regionState) {
3316     this.executorService.submit(
3317       new EventHandler(server, EventType.M_MASTER_RECOVERY) {
3318         @Override
3319         public void process() throws IOException {
3320           HRegionInfo hri = regionState.getRegion();
3321           ServerName serverName = regionState.getServerName();
3322           ReentrantLock lock = locker.acquireLock(hri.getEncodedName());
3323           try {
3324             for (int i = 1; i <= maximumAttempts; i++) {
3325               if (!serverManager.isServerOnline(serverName)
3326                   || server.isStopped() || server.isAborted()) {
3327                 return; // No need any more
3328               }
3329               try {
3330                 if (!regionState.equals(regionStates.getRegionState(hri))) {
3331                   return; // Region is not in the expected state any more
3332                 }
3333                 if (!serverManager.sendRegionClose(serverName, hri, -1, null, false)) {
3334                   // This means the region is still on the target server
3335                   LOG.debug("Got false in retry sendRegionClose for "
3336                     + regionState + ", re-close it");
3337                   invokeUnAssign(hri);
3338                 }
3339                 return; // Done.
3340               } catch (Throwable t) {
3341                 if (t instanceof RemoteException) {
3342                   t = ((RemoteException) t).unwrapRemoteException();
3343                 }
3344                 // In case SocketTimeoutException/FailedServerException, retry
3345                 if (t instanceof java.net.SocketTimeoutException
3346                     || t instanceof FailedServerException) {
3347                   Threads.sleep(100);
3348                   continue;
3349                 }
3350                 if (!(t instanceof NotServingRegionException
3351                     || t instanceof RegionAlreadyInTransitionException)) {
3352                   // NotServingRegionException/RegionAlreadyInTransitionException
3353                   // means the target server got the original region close request.
3354                   // For other exceptions, re-close it
3355                   LOG.debug("Got exception in retry sendRegionClose for "
3356                     + regionState + ", re-close it", t);
3357                   invokeUnAssign(hri);
3358                 }
3359                 return; // Done.
3360               }
3361             }
3362           } finally {
3363             lock.unlock();
3364           }
3365         }
3366       });
3367   }
3368 
3369   /**
3370    * Set Regions in transitions metrics.
3371    * This takes an iterator on the RegionInTransition map (CLSM), and is not synchronized.
3372    * This iterator is not fail fast, which may lead to stale read; but that's better than
3373    * creating a copy of the map for metrics computation, as this method will be invoked
3374    * on a frequent interval.
3375    */
3376   public void updateRegionsInTransitionMetrics() {
3377     long currentTime = System.currentTimeMillis();
3378     int totalRITs = 0;
3379     int totalRITsOverThreshold = 0;
3380     long oldestRITTime = 0;
3381     int ritThreshold = this.server.getConfiguration().
3382       getInt(HConstants.METRICS_RIT_STUCK_WARNING_THRESHOLD, 60000);
3383     for (RegionState state: regionStates.getRegionsInTransition().values()) {
3384       totalRITs++;
3385       long ritTime = currentTime - state.getStamp();
3386       if (ritTime > ritThreshold) { // more than the threshold
3387         totalRITsOverThreshold++;
3388       }
3389       if (oldestRITTime < ritTime) {
3390         oldestRITTime = ritTime;
3391       }
3392     }
3393     if (this.metricsAssignmentManager != null) {
3394       this.metricsAssignmentManager.updateRITOldestAge(oldestRITTime);
3395       this.metricsAssignmentManager.updateRITCount(totalRITs);
3396       this.metricsAssignmentManager.updateRITCountOverThreshold(totalRITsOverThreshold);
3397     }
3398   }
3399 
3400   /**
3401    * @param region Region whose plan we are to clear.
3402    */
3403   void clearRegionPlan(final HRegionInfo region) {
3404     synchronized (this.regionPlans) {
3405       this.regionPlans.remove(region.getEncodedName());
3406     }
3407   }
3408 
3409   /**
3410    * Wait on region to clear regions-in-transition.
3411    * @param hri Region to wait on.
3412    * @throws IOException
3413    */
3414   public void waitOnRegionToClearRegionsInTransition(final HRegionInfo hri)
3415       throws IOException, InterruptedException {
3416     waitOnRegionToClearRegionsInTransition(hri, -1L);
3417   }
3418 
3419   /**
3420    * Wait on region to clear regions-in-transition or time out
3421    * @param hri
3422    * @param timeOut Milliseconds to wait for current region to be out of transition state.
3423    * @return True when a region clears regions-in-transition before timeout otherwise false
3424    * @throws InterruptedException
3425    */
3426   public boolean waitOnRegionToClearRegionsInTransition(final HRegionInfo hri, long timeOut)
3427       throws InterruptedException {
3428     if (!regionStates.isRegionInTransition(hri)) return true;
3429     long end = (timeOut <= 0) ? Long.MAX_VALUE : EnvironmentEdgeManager.currentTime()
3430         + timeOut;
3431     // There is already a timeout monitor on regions in transition so I
3432     // should not have to have one here too?
3433     LOG.info("Waiting for " + hri.getEncodedName() +
3434         " to leave regions-in-transition, timeOut=" + timeOut + " ms.");
3435     while (!this.server.isStopped() && regionStates.isRegionInTransition(hri)) {
3436       regionStates.waitForUpdate(100);
3437       if (EnvironmentEdgeManager.currentTime() > end) {
3438         LOG.info("Timed out on waiting for " + hri.getEncodedName() + " to be assigned.");
3439         return false;
3440       }
3441     }
3442     if (this.server.isStopped()) {
3443       LOG.info("Giving up wait on regions in transition because stoppable.isStopped is set");
3444       return false;
3445     }
3446     return true;
3447   }
3448 
3449   void invokeAssign(HRegionInfo regionInfo) {
3450     invokeAssign(regionInfo, true);
3451   }
3452 
3453   void invokeAssign(HRegionInfo regionInfo, boolean newPlan) {
3454     threadPoolExecutorService.submit(new AssignCallable(this, regionInfo, newPlan));
3455   }
3456 
3457   void invokeUnAssign(HRegionInfo regionInfo) {
3458     threadPoolExecutorService.submit(new UnAssignCallable(this, regionInfo));
3459   }
3460 
3461   public ServerHostRegion isCarryingMeta(ServerName serverName) {
3462     return isCarryingRegion(serverName, HRegionInfo.FIRST_META_REGIONINFO);
3463   }
3464 
3465   public ServerHostRegion isCarryingMetaReplica(ServerName serverName, int replicaId) {
3466     return isCarryingRegion(serverName,
3467         RegionReplicaUtil.getRegionInfoForReplica(HRegionInfo.FIRST_META_REGIONINFO, replicaId));
3468   }
3469 
3470   public ServerHostRegion isCarryingMetaReplica(ServerName serverName, HRegionInfo metaHri) {
3471     return isCarryingRegion(serverName, metaHri);
3472   }
3473 
3474   /**
3475    * Check if the shutdown server carries the specific region.
3476    * We have a bunch of places that store region location
3477    * Those values aren't consistent. There is a delay of notification.
3478    * The location from zookeeper unassigned node has the most recent data;
3479    * but the node could be deleted after the region is opened by AM.
3480    * The AM's info could be old when OpenedRegionHandler
3481    * processing hasn't finished yet when server shutdown occurs.
3482    * @return whether the serverName currently hosts the region
3483    */
3484   private ServerHostRegion isCarryingRegion(ServerName serverName, HRegionInfo hri) {
3485     RegionTransition rt = null;
3486     try {
3487       byte [] data = ZKAssign.getData(watcher, hri.getEncodedName());
3488       // This call can legitimately come by null
3489       rt = data == null? null: RegionTransition.parseFrom(data);
3490     } catch (KeeperException e) {
3491       server.abort("Exception reading unassigned node for region=" + hri.getEncodedName(), e);
3492     } catch (DeserializationException e) {
3493       server.abort("Exception parsing unassigned node for region=" + hri.getEncodedName(), e);
3494     }
3495 
3496     ServerName addressFromZK = rt != null? rt.getServerName():  null;
3497     if (addressFromZK != null) {
3498       // if we get something from ZK, we will use the data
3499       boolean matchZK = addressFromZK.equals(serverName);
3500       LOG.debug("Checking region=" + hri.getRegionNameAsString() + ", zk server=" + addressFromZK +
3501         " current=" + serverName + ", matches=" + matchZK);
3502       return matchZK ? ServerHostRegion.HOSTING_REGION : ServerHostRegion.NOT_HOSTING_REGION;
3503     }
3504 
3505     ServerName addressFromAM = regionStates.getRegionServerOfRegion(hri);
3506     if (LOG.isDebugEnabled()) {
3507       LOG.debug("based on AM, current region=" + hri.getRegionNameAsString() +
3508         " is on server=" + (addressFromAM != null ? addressFromAM : "null") +
3509         " server being checked: " + serverName);
3510     }
3511     if (addressFromAM != null) {
3512       return addressFromAM.equals(serverName) ?
3513           ServerHostRegion.HOSTING_REGION : ServerHostRegion.NOT_HOSTING_REGION;
3514     }
3515 
3516     if (hri.isMetaRegion() && RegionReplicaUtil.isDefaultReplica(hri)) {
3517       // For the Meta region (default replica), we can do one more check on MetaTableLocator
3518       final ServerName serverNameInZK =
3519           server.getMetaTableLocator().getMetaRegionLocation(this.server.getZooKeeper());
3520       if (LOG.isDebugEnabled()) {
3521         LOG.debug("Based on MetaTableLocator, the META region is on server=" +
3522           (serverNameInZK == null ? "null" : serverNameInZK) +
3523           " server being checked: " + serverName);
3524       }
3525       if (serverNameInZK != null) {
3526         return serverNameInZK.equals(serverName) ?
3527             ServerHostRegion.HOSTING_REGION : ServerHostRegion.NOT_HOSTING_REGION;
3528       }
3529     }
3530 
3531     // Checked everywhere, if reaching here, we are unsure whether the server is carrying region.
3532     return ServerHostRegion.UNKNOWN;
3533   }
3534 
3535   /**
3536    * Clean out crashed server removing any assignments.
3537    * @param sn Server that went down.
3538    * @return list of regions in transition on this server
3539    */
3540   public List<HRegionInfo> cleanOutCrashedServerReferences(final ServerName sn) {
3541     // Clean out any existing assignment plans for this server
3542     synchronized (this.regionPlans) {
3543       for (Iterator <Map.Entry<String, RegionPlan>> i = this.regionPlans.entrySet().iterator();
3544           i.hasNext();) {
3545         Map.Entry<String, RegionPlan> e = i.next();
3546         ServerName otherSn = e.getValue().getDestination();
3547         // The name will be null if the region is planned for a random assign.
3548         if (otherSn != null && otherSn.equals(sn)) {
3549           // Use iterator's remove else we'll get CME
3550           i.remove();
3551         }
3552       }
3553     }
3554     List<HRegionInfo> regions = regionStates.serverOffline(watcher, sn);
3555     for (Iterator<HRegionInfo> it = regions.iterator(); it.hasNext(); ) {
3556       HRegionInfo hri = it.next();
3557       String encodedName = hri.getEncodedName();
3558 
3559       // We need a lock on the region as we could update it
3560       Lock lock = locker.acquireLock(encodedName);
3561       try {
3562         RegionState regionState = regionStates.getRegionTransitionState(encodedName);
3563         if (regionState == null
3564             || (regionState.getServerName() != null && !regionState.isOnServer(sn))
3565             || !(regionState.isFailedClose() || regionState.isOffline()
3566               || regionState.isPendingOpenOrOpening())) {
3567           LOG.info("Skip " + regionState + " since it is not opening/failed_close"
3568             + " on the dead server any more: " + sn);
3569           it.remove();
3570         } else {
3571           try {
3572             // Delete the ZNode if exists
3573             ZKAssign.deleteNodeFailSilent(watcher, hri);
3574           } catch (KeeperException ke) {
3575             server.abort("Unexpected ZK exception deleting node " + hri, ke);
3576           }
3577           if (tableStateManager.isTableState(hri.getTable(),
3578               ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3579             regionStates.regionOffline(hri);
3580             it.remove();
3581             continue;
3582           }
3583           // Mark the region offline and assign it again by SSH
3584           regionStates.updateRegionState(hri, State.OFFLINE);
3585         }
3586       } finally {
3587         lock.unlock();
3588       }
3589     }
3590     return regions;
3591   }
3592 
3593   /**
3594    * @param plan Plan to execute.
3595    */
3596   public void balance(final RegionPlan plan) {
3597 
3598     HRegionInfo hri = plan.getRegionInfo();
3599     TableName tableName = hri.getTable();
3600     if (tableStateManager.isTableState(tableName,
3601       ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3602       LOG.info("Ignored moving region of disabling/disabled table "
3603         + tableName);
3604       return;
3605     }
3606 
3607     // Move the region only if it's assigned
3608     String encodedName = hri.getEncodedName();
3609     ReentrantLock lock = locker.acquireLock(encodedName);
3610     try {
3611       if (!regionStates.isRegionOnline(hri)) {
3612         RegionState state = regionStates.getRegionState(encodedName);
3613         LOG.info("Ignored moving region not assigned: " + hri + ", "
3614           + (state == null ? "not in region states" : state));
3615         return;
3616       }
3617       synchronized (this.regionPlans) {
3618         this.regionPlans.put(plan.getRegionName(), plan);
3619       }
3620       unassign(hri, false, plan.getDestination());
3621     } finally {
3622       lock.unlock();
3623     }
3624   }
3625 
3626   public void stop() {
3627     shutdown(); // Stop executor service, etc
3628   }
3629 
3630   /**
3631    * Shutdown the threadpool executor service
3632    */
3633   public void shutdown() {
3634     // It's an immediate shutdown, so we're clearing the remaining tasks.
3635     synchronized (zkEventWorkerWaitingList){
3636       zkEventWorkerWaitingList.clear();
3637     }
3638 
3639     // Shutdown the threadpool executor service
3640     threadPoolExecutorService.shutdownNow();
3641     zkEventWorkers.shutdownNow();
3642     regionStateStore.stop();
3643   }
3644 
3645   protected void setEnabledTable(TableName tableName) {
3646     try {
3647       this.tableStateManager.setTableState(tableName,
3648         ZooKeeperProtos.Table.State.ENABLED);
3649     } catch (CoordinatedStateException e) {
3650       // here we can abort as it is the start up flow
3651       String errorMsg = "Unable to ensure that the table " + tableName
3652           + " will be" + " enabled because of a ZooKeeper issue";
3653       LOG.error(errorMsg);
3654       this.server.abort(errorMsg, e);
3655     }
3656   }
3657 
3658   /**
3659    * Set region as OFFLINED up in zookeeper asynchronously.
3660    * @param state
3661    * @return True if we succeeded, false otherwise (State was incorrect or failed
3662    * updating zk).
3663    */
3664   private boolean asyncSetOfflineInZooKeeper(final RegionState state,
3665       final AsyncCallback.StringCallback cb, final ServerName destination) {
3666     if (!state.isClosed() && !state.isOffline()) {
3667       this.server.abort("Unexpected state trying to OFFLINE; " + state,
3668         new IllegalStateException());
3669       return false;
3670     }
3671     regionStates.updateRegionState(state.getRegion(), State.OFFLINE);
3672     try {
3673       ZKAssign.asyncCreateNodeOffline(watcher, state.getRegion(),
3674         destination, cb, state);
3675     } catch (KeeperException e) {
3676       if (e instanceof NodeExistsException) {
3677         LOG.warn("Node for " + state.getRegion() + " already exists");
3678       } else {
3679         server.abort("Unexpected ZK exception creating/setting node OFFLINE", e);
3680       }
3681       return false;
3682     }
3683     return true;
3684   }
3685 
3686   private boolean deleteNodeInStates(String encodedName,
3687       String desc, ServerName sn, EventType... types) {
3688     try {
3689       for (EventType et: types) {
3690         if (ZKAssign.deleteNode(watcher, encodedName, et, sn)) {
3691           return true;
3692         }
3693       }
3694       LOG.info("Failed to delete the " + desc + " node for "
3695         + encodedName + ". The node type may not match");
3696     } catch (NoNodeException e) {
3697       if (LOG.isDebugEnabled()) {
3698         LOG.debug("The " + desc + " node for " + encodedName + " already deleted");
3699       }
3700     } catch (KeeperException ke) {
3701       server.abort("Unexpected ZK exception deleting " + desc
3702         + " node for the region " + encodedName, ke);
3703     }
3704     return false;
3705   }
3706 
3707   private void deleteMergingNode(String encodedName, ServerName sn) {
3708     deleteNodeInStates(encodedName, "merging", sn, EventType.RS_ZK_REGION_MERGING,
3709       EventType.RS_ZK_REQUEST_REGION_MERGE, EventType.RS_ZK_REGION_MERGED);
3710   }
3711 
3712   private void deleteSplittingNode(String encodedName, ServerName sn) {
3713     deleteNodeInStates(encodedName, "splitting", sn, EventType.RS_ZK_REGION_SPLITTING,
3714       EventType.RS_ZK_REQUEST_REGION_SPLIT, EventType.RS_ZK_REGION_SPLIT);
3715   }
3716 
3717   @edu.umd.cs.findbugs.annotations.SuppressWarnings(
3718       value="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION",
3719       justification="Modification of Maps not ATOMIC!!!! FIX!!!")
3720   private void onRegionFailedOpen(
3721       final HRegionInfo hri, final ServerName sn) {
3722     String encodedName = hri.getEncodedName();
3723     // FindBugs: AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION Worth fixing!!!
3724     AtomicInteger failedOpenCount = failedOpenTracker.get(encodedName);
3725     if (failedOpenCount == null) {
3726       failedOpenCount = new AtomicInteger();
3727       // No need to use putIfAbsent, or extra synchronization since
3728       // this whole handleRegion block is locked on the encoded region
3729       // name, and failedOpenTracker is updated only in this block
3730       // FindBugs: AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION
3731       failedOpenTracker.put(encodedName, failedOpenCount);
3732     }
3733     if (failedOpenCount.incrementAndGet() >= maximumAttempts && !hri.isMetaRegion()) {
3734       // FindBugs: AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION
3735       regionStates.updateRegionState(hri, State.FAILED_OPEN);
3736       // remove the tracking info to save memory, also reset
3737       // the count for next open initiative
3738       failedOpenTracker.remove(encodedName);
3739     } else {
3740       if (hri.isMetaRegion() && failedOpenCount.get() >= maximumAttempts) {
3741         // Log a warning message if a meta region failedOpenCount exceeds maximumAttempts
3742         // so that we are aware of potential problem if it persists for a long time.
3743         LOG.warn("Failed to open the hbase:meta region " +
3744             hri.getRegionNameAsString() + " after" +
3745             failedOpenCount.get() + " retries. Continue retrying.");
3746       }
3747 
3748       // Handle this the same as if it were opened and then closed.
3749       RegionState regionState = regionStates.updateRegionState(hri, State.CLOSED);
3750       if (regionState != null) {
3751         // When there are more than one region server a new RS is selected as the
3752         // destination and the same is updated in the region plan. (HBASE-5546)
3753         if (getTableStateManager().isTableState(hri.getTable(),
3754             ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING) ||
3755             replicasToClose.contains(hri)) {
3756           offlineDisabledRegion(hri);
3757           return;
3758         }
3759         // ZK Node is in CLOSED state, assign it.
3760          regionStates.updateRegionState(hri, RegionState.State.CLOSED);
3761         // This below has to do w/ online enable/disable of a table
3762         removeClosedRegion(hri);
3763         try {
3764           getRegionPlan(hri, sn, true);
3765         } catch (HBaseIOException e) {
3766           LOG.warn("Failed to get region plan", e);
3767         }
3768         invokeAssign(hri, false);
3769       }
3770     }
3771   }
3772 
3773   private void onRegionOpen(final HRegionInfo hri, final ServerName sn, long openSeqNum) {
3774     regionOnline(hri, sn, openSeqNum);
3775     if (useZKForAssignment) {
3776       try {
3777         // Delete the ZNode if exists
3778         ZKAssign.deleteNodeFailSilent(watcher, hri);
3779       } catch (KeeperException ke) {
3780         server.abort("Unexpected ZK exception deleting node " + hri, ke);
3781       }
3782     }
3783 
3784     // reset the count, if any
3785     failedOpenTracker.remove(hri.getEncodedName());
3786     if (getTableStateManager().isTableState(hri.getTable(),
3787         ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3788       invokeUnAssign(hri);
3789     }
3790   }
3791 
3792   private void onRegionClosed(final HRegionInfo hri) {
3793     if (getTableStateManager().isTableState(hri.getTable(),
3794         ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING) ||
3795         replicasToClose.contains(hri)) {
3796       offlineDisabledRegion(hri);
3797       return;
3798     }
3799     regionStates.updateRegionState(hri, RegionState.State.CLOSED);
3800     sendRegionClosedNotification(hri);
3801     // This below has to do w/ online enable/disable of a table
3802     removeClosedRegion(hri);
3803     invokeAssign(hri, false);
3804   }
3805 
3806   private String checkInStateForSplit(ServerName sn,
3807       final HRegionInfo p, final HRegionInfo a, final HRegionInfo b) {
3808     final RegionState rs_p = regionStates.getRegionState(p);
3809     RegionState rs_a = regionStates.getRegionState(a);
3810     RegionState rs_b = regionStates.getRegionState(b);
3811     if (!(rs_p.isOpenOrSplittingOnServer(sn)
3812         && (rs_a == null || rs_a.isOpenOrSplittingNewOnServer(sn))
3813         && (rs_b == null || rs_b.isOpenOrSplittingNewOnServer(sn)))) {
3814       return "Not in state good for split";
3815     }
3816     return "";
3817   }
3818 
3819   private String onRegionSplitReverted(ServerName sn,
3820       final HRegionInfo p, final HRegionInfo a, final HRegionInfo b) {
3821     String s = checkInStateForSplit(sn, p, a, b);
3822     if (!org.apache.commons.lang.StringUtils.isEmpty(s)) {
3823       return s;
3824     }
3825 
3826     // Always bring the parent back online. Even if it's not offline
3827     // There's no harm in making it online again.
3828     regionOnline(p, sn);
3829 
3830     // Only offline the region if they are known to exist.
3831     RegionState regionStateA = regionStates.getRegionState(a);
3832     RegionState regionStateB = regionStates.getRegionState(b);
3833     if (regionStateA != null) {
3834       regionOffline(a);
3835     }
3836     if (regionStateB != null) {
3837       regionOffline(b);
3838     }
3839 
3840     if (getTableStateManager().isTableState(p.getTable(),
3841         ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3842       invokeUnAssign(p);
3843     }
3844     return null;
3845   }
3846 
3847   private String onRegionSplit(ServerName sn, TransitionCode code,
3848       final HRegionInfo p, final HRegionInfo a, final HRegionInfo b) {
3849     String s = checkInStateForSplit(sn, p, a, b);
3850     if (!org.apache.commons.lang.StringUtils.isEmpty(s)) {
3851       return s;
3852     }
3853     regionStates.updateRegionState(a, State.SPLITTING_NEW, sn);
3854     regionStates.updateRegionState(b, State.SPLITTING_NEW, sn);
3855     regionStates.updateRegionState(p, State.SPLITTING);
3856 
3857     if (code == TransitionCode.SPLIT) {
3858       if (TEST_SKIP_SPLIT_HANDLING) {
3859         return "Skipping split message, TEST_SKIP_SPLIT_HANDLING is set";
3860       }
3861       regionOffline(p, State.SPLIT);
3862       regionOnline(a, sn, 1);
3863       regionOnline(b, sn, 1);
3864 
3865       // User could disable the table before master knows the new region.
3866       if (getTableStateManager().isTableState(p.getTable(),
3867           ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3868         invokeUnAssign(a);
3869         invokeUnAssign(b);
3870       } else {
3871         Callable<Object> splitReplicasCallable = new Callable<Object>() {
3872           @Override
3873           public Object call() {
3874             doSplittingOfReplicas(p, a, b);
3875             return null;
3876           }
3877         };
3878         threadPoolExecutorService.submit(splitReplicasCallable);
3879       }
3880     } else if (code == TransitionCode.SPLIT_PONR) {
3881       try {
3882         regionStates.splitRegion(p, a, b, sn);
3883       } catch (IOException ioe) {
3884         LOG.info("Failed to record split region " + p.getShortNameToLog());
3885         return "Failed to record the splitting in meta";
3886       }
3887     }
3888     return null;
3889   }
3890 
3891   private String onRegionMerge(ServerName sn, TransitionCode code,
3892       final HRegionInfo p, final HRegionInfo a, final HRegionInfo b) {
3893     RegionState rs_p = regionStates.getRegionState(p);
3894     RegionState rs_a = regionStates.getRegionState(a);
3895     RegionState rs_b = regionStates.getRegionState(b);
3896     if (!(rs_a.isOpenOrMergingOnServer(sn) && rs_b.isOpenOrMergingOnServer(sn)
3897         && (rs_p == null || rs_p.isOpenOrMergingNewOnServer(sn)))) {
3898       return "Not in state good for merge";
3899     }
3900     regionStates.updateRegionState(a, State.MERGING);
3901     regionStates.updateRegionState(b, State.MERGING);
3902     regionStates.updateRegionState(p, State.MERGING_NEW, sn);
3903 
3904     String encodedName = p.getEncodedName();
3905     if (code == TransitionCode.READY_TO_MERGE) {
3906       mergingRegions.put(encodedName,
3907         new PairOfSameType<HRegionInfo>(a, b));
3908     } else if (code == TransitionCode.MERGED) {
3909 
3910       if (TEST_SKIP_MERGE_HANDLING) {
3911         return "Skipping merge message, TEST_SKIP_MERGE_HANDLING is set for merge parent: " + p;
3912       }
3913 
3914       mergingRegions.remove(encodedName);
3915       regionOffline(a, State.MERGED);
3916       regionOffline(b, State.MERGED);
3917       regionOnline(p, sn, 1);
3918 
3919       // User could disable the table before master knows the new region.
3920       if (getTableStateManager().isTableState(p.getTable(),
3921           ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3922         invokeUnAssign(p);
3923       } else {
3924         Callable<Object> mergeReplicasCallable = new Callable<Object>() {
3925           @Override
3926           public Object call() {
3927             doMergingOfReplicas(p, a, b);
3928             return null;
3929           }
3930         };
3931         threadPoolExecutorService.submit(mergeReplicasCallable);
3932       }
3933     } else if (code == TransitionCode.MERGE_PONR) {
3934       try {
3935         regionStates.mergeRegions(p, a, b, sn);
3936       } catch (IOException ioe) {
3937         LOG.info("Failed to record merged region " + p.getShortNameToLog());
3938         return "Failed to record the merging in meta";
3939       }
3940     }
3941     return null;
3942   }
3943 
3944   private String onRegionMergeReverted(ServerName sn, TransitionCode code,
3945 	      final HRegionInfo p, final HRegionInfo a, final HRegionInfo b) {
3946     RegionState rs_p = regionStates.getRegionState(p);
3947     String encodedName = p.getEncodedName();
3948     mergingRegions.remove(encodedName);
3949 
3950     // Always bring the children back online. Even if they are not offline
3951     // there's no harm in making them online again.
3952     regionOnline(a, sn);
3953     regionOnline(b, sn);
3954 
3955     // Only offline the merging region if it is known to exist.
3956     if (rs_p != null) {
3957       regionOffline(p);
3958     }
3959 
3960     if (getTableStateManager().isTableState(p.getTable(),
3961         ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
3962       invokeUnAssign(a);
3963       invokeUnAssign(b);
3964     }
3965 
3966     return null;
3967   }
3968 
3969   /**
3970    * A helper to handle region merging transition event.
3971    * It transitions merging regions to MERGING state.
3972    */
3973   private boolean handleRegionMerging(final RegionTransition rt, final String encodedName,
3974       final String prettyPrintedRegionName, final ServerName sn) {
3975     if (!serverManager.isServerOnline(sn)) {
3976       LOG.warn("Dropped merging! ServerName=" + sn + " unknown.");
3977       return false;
3978     }
3979     byte [] payloadOfMerging = rt.getPayload();
3980     List<HRegionInfo> mergingRegions;
3981     try {
3982       mergingRegions = HRegionInfo.parseDelimitedFrom(
3983         payloadOfMerging, 0, payloadOfMerging.length);
3984     } catch (IOException e) {
3985       LOG.error("Dropped merging! Failed reading "  + rt.getEventType()
3986         + " payload for " + prettyPrintedRegionName);
3987       return false;
3988     }
3989     assert mergingRegions.size() == 3;
3990     HRegionInfo p = mergingRegions.get(0);
3991     HRegionInfo hri_a = mergingRegions.get(1);
3992     HRegionInfo hri_b = mergingRegions.get(2);
3993 
3994     RegionState rs_p = regionStates.getRegionState(p);
3995     RegionState rs_a = regionStates.getRegionState(hri_a);
3996     RegionState rs_b = regionStates.getRegionState(hri_b);
3997 
3998     if (!((rs_a == null || rs_a.isOpenOrMergingOnServer(sn))
3999         && (rs_b == null || rs_b.isOpenOrMergingOnServer(sn))
4000         && (rs_p == null || rs_p.isOpenOrMergingNewOnServer(sn)))) {
4001       LOG.warn("Dropped merging! Not in state good for MERGING; rs_p="
4002         + rs_p + ", rs_a=" + rs_a + ", rs_b=" + rs_b);
4003       return false;
4004     }
4005 
4006     EventType et = rt.getEventType();
4007     if (et == EventType.RS_ZK_REQUEST_REGION_MERGE) {
4008       try {
4009         RegionMergeCoordination.RegionMergeDetails std =
4010             ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
4011                 .getRegionMergeCoordination().getDefaultDetails();
4012         ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
4013             .getRegionMergeCoordination().processRegionMergeRequest(p, hri_a, hri_b, sn, std);
4014         if (((ZkRegionMergeCoordination.ZkRegionMergeDetails) std).getZnodeVersion() == -1) {
4015           byte[] data = ZKAssign.getData(watcher, encodedName);
4016          EventType currentType = null;
4017           if (data != null) {
4018             RegionTransition newRt = RegionTransition.parseFrom(data);
4019             currentType = newRt.getEventType();
4020           }
4021           if (currentType == null || (currentType != EventType.RS_ZK_REGION_MERGED
4022               && currentType != EventType.RS_ZK_REGION_MERGING)) {
4023             LOG.warn("Failed to transition pending_merge node "
4024               + encodedName + " to merging, it's now " + currentType);
4025             return false;
4026           }
4027         }
4028       } catch (Exception e) {
4029         LOG.warn("Failed to transition pending_merge node "
4030           + encodedName + " to merging", e);
4031         return false;
4032       }
4033     }
4034 
4035     synchronized (regionStates) {
4036       regionStates.updateRegionState(hri_a, State.MERGING);
4037       regionStates.updateRegionState(hri_b, State.MERGING);
4038       regionStates.updateRegionState(p, State.MERGING_NEW, sn);
4039 
4040       if (TEST_SKIP_MERGE_HANDLING) {
4041         LOG.warn("Skipping merge message, TEST_SKIP_MERGE_HANDLING is set for merge parent: " + p);
4042         return true; // return true so that the merging node stays
4043       }
4044 
4045       if (et != EventType.RS_ZK_REGION_MERGED) {
4046         this.mergingRegions.put(encodedName,
4047           new PairOfSameType<HRegionInfo>(hri_a, hri_b));
4048       } else {
4049         this.mergingRegions.remove(encodedName);
4050         regionOffline(hri_a, State.MERGED);
4051         regionOffline(hri_b, State.MERGED);
4052         regionOnline(p, sn);
4053       }
4054     }
4055 
4056     if (et == EventType.RS_ZK_REGION_MERGED) {
4057       doMergingOfReplicas(p, hri_a, hri_b);
4058       LOG.debug("Handling MERGED event for " + encodedName + "; deleting node");
4059       // Remove region from ZK
4060       try {
4061         boolean successful = false;
4062         while (!successful) {
4063           // It's possible that the RS tickles in between the reading of the
4064           // znode and the deleting, so it's safe to retry.
4065           successful = ZKAssign.deleteNode(watcher, encodedName,
4066             EventType.RS_ZK_REGION_MERGED, sn);
4067         }
4068       } catch (KeeperException e) {
4069         if (e instanceof NoNodeException) {
4070           String znodePath = ZKUtil.joinZNode(watcher.splitLogZNode, encodedName);
4071           LOG.debug("The znode " + znodePath + " does not exist.  May be deleted already.");
4072         } else {
4073           server.abort("Error deleting MERGED node " + encodedName, e);
4074         }
4075       }
4076       LOG.info("Handled MERGED event; merged=" + p.getRegionNameAsString()
4077         + ", region_a=" + hri_a.getRegionNameAsString() + ", region_b="
4078         + hri_b.getRegionNameAsString() + ", on " + sn);
4079 
4080       // User could disable the table before master knows the new region.
4081       if (tableStateManager.isTableState(p.getTable(),
4082           ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
4083         unassign(p);
4084       }
4085     }
4086     return true;
4087   }
4088 
4089   /**
4090    * A helper to handle region splitting transition event.
4091    */
4092   private boolean handleRegionSplitting(final RegionTransition rt, final String encodedName,
4093       final String prettyPrintedRegionName, final ServerName sn) {
4094     if (!serverManager.isServerOnline(sn)) {
4095       LOG.warn("Dropped splitting! ServerName=" + sn + " unknown.");
4096       return false;
4097     }
4098     byte [] payloadOfSplitting = rt.getPayload();
4099     List<HRegionInfo> splittingRegions;
4100     try {
4101       splittingRegions = HRegionInfo.parseDelimitedFrom(
4102         payloadOfSplitting, 0, payloadOfSplitting.length);
4103     } catch (IOException e) {
4104       LOG.error("Dropped splitting! Failed reading " + rt.getEventType()
4105         + " payload for " + prettyPrintedRegionName);
4106       return false;
4107     }
4108     assert splittingRegions.size() == 2;
4109     HRegionInfo hri_a = splittingRegions.get(0);
4110     HRegionInfo hri_b = splittingRegions.get(1);
4111 
4112     RegionState rs_p = regionStates.getRegionState(encodedName);
4113     RegionState rs_a = regionStates.getRegionState(hri_a);
4114     RegionState rs_b = regionStates.getRegionState(hri_b);
4115 
4116     if (!((rs_p == null || rs_p.isOpenOrSplittingOnServer(sn))
4117         && (rs_a == null || rs_a.isOpenOrSplittingNewOnServer(sn))
4118         && (rs_b == null || rs_b.isOpenOrSplittingNewOnServer(sn)))) {
4119       LOG.warn("Dropped splitting! Not in state good for SPLITTING; rs_p="
4120         + rs_p + ", rs_a=" + rs_a + ", rs_b=" + rs_b);
4121       return false;
4122     }
4123 
4124     if (rs_p == null) {
4125       // Splitting region should be online
4126       rs_p = regionStates.updateRegionState(rt, State.OPEN);
4127       if (rs_p == null) {
4128         LOG.warn("Received splitting for region " + prettyPrintedRegionName
4129           + " from server " + sn + " but it doesn't exist anymore,"
4130           + " probably already processed its split");
4131         return false;
4132       }
4133       regionStates.regionOnline(rs_p.getRegion(), sn);
4134     }
4135 
4136     HRegionInfo p = rs_p.getRegion();
4137     EventType et = rt.getEventType();
4138     if (et == EventType.RS_ZK_REQUEST_REGION_SPLIT) {
4139       try {
4140         SplitTransactionDetails std =
4141             ((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
4142                 .getSplitTransactionCoordination().getDefaultDetails();
4143         if (((BaseCoordinatedStateManager) server.getCoordinatedStateManager())
4144             .getSplitTransactionCoordination().processTransition(p, hri_a, hri_b, sn, std) == -1) {
4145           byte[] data = ZKAssign.getData(watcher, encodedName);
4146           EventType currentType = null;
4147           if (data != null) {
4148             RegionTransition newRt = RegionTransition.parseFrom(data);
4149             currentType = newRt.getEventType();
4150           }
4151           if (currentType == null
4152               || (currentType != EventType.RS_ZK_REGION_SPLIT && currentType != EventType.RS_ZK_REGION_SPLITTING)) {
4153             LOG.warn("Failed to transition pending_split node " + encodedName
4154                 + " to splitting, it's now " + currentType);
4155             return false;
4156           }
4157         }
4158       } catch (Exception e) {
4159         LOG.warn("Failed to transition pending_split node " + encodedName + " to splitting", e);
4160         return false;
4161       }
4162     }
4163 
4164     synchronized (regionStates) {
4165       splitRegions.put(p, new PairOfSameType<HRegionInfo>(hri_a, hri_b));
4166       regionStates.updateRegionState(hri_a, State.SPLITTING_NEW, sn);
4167       regionStates.updateRegionState(hri_b, State.SPLITTING_NEW, sn);
4168       regionStates.updateRegionState(rt, State.SPLITTING);
4169 
4170       // The below is for testing ONLY!  We can't do fault injection easily, so
4171       // resort to this kinda uglyness -- St.Ack 02/25/2011.
4172       if (TEST_SKIP_SPLIT_HANDLING) {
4173         LOG.warn("Skipping split message, TEST_SKIP_SPLIT_HANDLING is set");
4174         return true; // return true so that the splitting node stays
4175       }
4176 
4177       if (et == EventType.RS_ZK_REGION_SPLIT) {
4178         regionOffline(p, State.SPLIT);
4179         regionOnline(hri_a, sn);
4180         regionOnline(hri_b, sn);
4181         splitRegions.remove(p);
4182       }
4183     }
4184 
4185     if (et == EventType.RS_ZK_REGION_SPLIT) {
4186       // split replicas
4187       doSplittingOfReplicas(rs_p.getRegion(), hri_a, hri_b);
4188       LOG.debug("Handling SPLIT event for " + encodedName + "; deleting node");
4189       // Remove region from ZK
4190       try {
4191         boolean successful = false;
4192         while (!successful) {
4193           // It's possible that the RS tickles in between the reading of the
4194           // znode and the deleting, so it's safe to retry.
4195           successful = ZKAssign.deleteNode(watcher, encodedName,
4196             EventType.RS_ZK_REGION_SPLIT, sn);
4197         }
4198       } catch (KeeperException e) {
4199         if (e instanceof NoNodeException) {
4200           String znodePath = ZKUtil.joinZNode(watcher.splitLogZNode, encodedName);
4201           LOG.debug("The znode " + znodePath + " does not exist.  May be deleted already.");
4202         } else {
4203           server.abort("Error deleting SPLIT node " + encodedName, e);
4204         }
4205       }
4206       LOG.info("Handled SPLIT event; parent=" + p.getRegionNameAsString()
4207         + ", daughter a=" + hri_a.getRegionNameAsString() + ", daughter b="
4208         + hri_b.getRegionNameAsString() + ", on " + sn);
4209 
4210       // User could disable the table before master knows the new region.
4211       if (tableStateManager.isTableState(p.getTable(),
4212           ZooKeeperProtos.Table.State.DISABLED, ZooKeeperProtos.Table.State.DISABLING)) {
4213         unassign(hri_a);
4214         unassign(hri_b);
4215       }
4216     }
4217     return true;
4218   }
4219 
4220   private void doMergingOfReplicas(HRegionInfo mergedHri, final HRegionInfo hri_a,
4221       final HRegionInfo hri_b) {
4222     // Close replicas for the original unmerged regions. create/assign new replicas
4223     // for the merged parent.
4224     List<HRegionInfo> unmergedRegions = new ArrayList<HRegionInfo>();
4225     unmergedRegions.add(hri_a);
4226     unmergedRegions.add(hri_b);
4227     Map<ServerName, List<HRegionInfo>> map = regionStates.getRegionAssignments(unmergedRegions);
4228     Collection<List<HRegionInfo>> c = map.values();
4229     for (List<HRegionInfo> l : c) {
4230       for (HRegionInfo h : l) {
4231         if (!RegionReplicaUtil.isDefaultReplica(h)) {
4232           LOG.debug("Unassigning un-merged replica " + h);
4233           unassign(h);
4234         }
4235       }
4236     }
4237     int numReplicas = 1;
4238     try {
4239       numReplicas = server.getTableDescriptors().get(mergedHri.getTable()).
4240           getRegionReplication();
4241     } catch (IOException e) {
4242       LOG.warn("Couldn't get the replication attribute of the table " + mergedHri.getTable() +
4243           " due to " + e.getMessage() + ". The assignment of replicas for the merged region " +
4244           "will not be done");
4245     }
4246     List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
4247     for (int i = 1; i < numReplicas; i++) {
4248       regions.add(RegionReplicaUtil.getRegionInfoForReplica(mergedHri, i));
4249     }
4250     try {
4251       assign(regions);
4252     } catch (IOException ioe) {
4253       LOG.warn("Couldn't assign all replica(s) of region " + mergedHri + " because of " +
4254                 ioe.getMessage());
4255     } catch (InterruptedException ie) {
4256       LOG.warn("Couldn't assign all replica(s) of region " + mergedHri+ " because of " +
4257                 ie.getMessage());
4258     }
4259     // Remove merged region's replica from AM's memory
4260     clearReplicaRegions(c);
4261   }
4262 
4263   private void doSplittingOfReplicas(final HRegionInfo parentHri, final HRegionInfo hri_a,
4264       final HRegionInfo hri_b) {
4265     // create new regions for the replica, and assign them to match with the
4266     // current replica assignments. If replica1 of parent is assigned to RS1,
4267     // the replica1s of daughters will be on the same machine
4268     int numReplicas = 1;
4269     try {
4270       numReplicas = server.getTableDescriptors().get(parentHri.getTable()).
4271           getRegionReplication();
4272     } catch (IOException e) {
4273       LOG.warn("Couldn't get the replication attribute of the table " + parentHri.getTable() +
4274           " due to " + e.getMessage() + ". The assignment of daughter replicas " +
4275           "replicas will not be done");
4276     }
4277     // unassign the old replicas
4278     List<HRegionInfo> parentRegion = new ArrayList<HRegionInfo>();
4279     parentRegion.add(parentHri);
4280     Map<ServerName, List<HRegionInfo>> currentAssign =
4281         regionStates.getRegionAssignments(parentRegion);
4282     Collection<List<HRegionInfo>> c = currentAssign.values();
4283     for (List<HRegionInfo> l : c) {
4284       for (HRegionInfo h : l) {
4285         if (!RegionReplicaUtil.isDefaultReplica(h)) {
4286           LOG.debug("Unassigning parent's replica " + h);
4287           unassign(h);
4288         }
4289       }
4290     }
4291     // assign daughter replicas
4292     Map<HRegionInfo, ServerName> map = new HashMap<HRegionInfo, ServerName>();
4293     for (int i = 1; i < numReplicas; i++) {
4294       prepareDaughterReplicaForAssignment(hri_a, parentHri, i, map);
4295       prepareDaughterReplicaForAssignment(hri_b, parentHri, i, map);
4296     }
4297     try {
4298       assign(map);
4299     } catch (IOException e) {
4300       LOG.warn("Caught exception " + e + " while trying to assign replica(s) of daughter(s)");
4301     } catch (InterruptedException e) {
4302       LOG.warn("Caught exception " + e + " while trying to assign replica(s) of daughter(s)");
4303     }
4304     // Remove parent region's replica from AM's memory
4305     clearReplicaRegions(c);
4306   }
4307 
4308   /*
4309    * Clear the replica regions after region split or merge.
4310    */
4311   private void clearReplicaRegions(Collection<List<HRegionInfo>> regionInfos) {
4312     for (List<HRegionInfo> regionInfoList : regionInfos) {
4313       for (HRegionInfo regionInfo : regionInfoList) {
4314         if (!RegionReplicaUtil.isDefaultReplica(regionInfo)) {
4315           regionStates.deleteRegion(regionInfo);
4316         }
4317       }
4318     }
4319   }
4320 
4321   private void prepareDaughterReplicaForAssignment(HRegionInfo daughterHri, HRegionInfo parentHri,
4322       int replicaId, Map<HRegionInfo, ServerName> map) {
4323     HRegionInfo parentReplica = RegionReplicaUtil.getRegionInfoForReplica(parentHri, replicaId);
4324     HRegionInfo daughterReplica = RegionReplicaUtil.getRegionInfoForReplica(daughterHri,
4325         replicaId);
4326     LOG.debug("Created replica region for daughter " + daughterReplica);
4327     ServerName sn;
4328     if ((sn = regionStates.getRegionServerOfRegion(parentReplica)) != null) {
4329       map.put(daughterReplica, sn);
4330     } else {
4331       List<ServerName> servers = serverManager.getOnlineServersList();
4332       sn = servers.get((new Random(System.currentTimeMillis())).nextInt(servers.size()));
4333       map.put(daughterReplica, sn);
4334     }
4335   }
4336 
4337   public Set<HRegionInfo> getReplicasToClose() {
4338     return replicasToClose;
4339   }
4340 
4341   /**
4342    * A region is offline.  The new state should be the specified one,
4343    * if not null.  If the specified state is null, the new state is Offline.
4344    * The specified state can be Split/Merged/Offline/null only.
4345    */
4346   private void regionOffline(final HRegionInfo regionInfo, final State state) {
4347     regionStates.regionOffline(regionInfo, state);
4348     removeClosedRegion(regionInfo);
4349     // remove the region plan as well just in case.
4350     clearRegionPlan(regionInfo);
4351     balancer.regionOffline(regionInfo);
4352 
4353     // Tell our listeners that a region was closed
4354     sendRegionClosedNotification(regionInfo);
4355     // also note that all the replicas of the primary should be closed
4356     if (state != null && state.equals(State.SPLIT)) {
4357       Collection<HRegionInfo> c = new ArrayList<HRegionInfo>(1);
4358       c.add(regionInfo);
4359       Map<ServerName, List<HRegionInfo>> map = regionStates.getRegionAssignments(c);
4360       Collection<List<HRegionInfo>> allReplicas = map.values();
4361       for (List<HRegionInfo> list : allReplicas) {
4362         replicasToClose.addAll(list);
4363       }
4364     }
4365     else if (state != null && state.equals(State.MERGED)) {
4366       Collection<HRegionInfo> c = new ArrayList<HRegionInfo>(1);
4367       c.add(regionInfo);
4368       Map<ServerName, List<HRegionInfo>> map = regionStates.getRegionAssignments(c);
4369       Collection<List<HRegionInfo>> allReplicas = map.values();
4370       for (List<HRegionInfo> list : allReplicas) {
4371         replicasToClose.addAll(list);
4372       }
4373     }
4374   }
4375 
4376   private void sendRegionOpenedNotification(final HRegionInfo regionInfo,
4377       final ServerName serverName) {
4378     if (!this.listeners.isEmpty()) {
4379       for (AssignmentListener listener : this.listeners) {
4380         listener.regionOpened(regionInfo, serverName);
4381       }
4382     }
4383   }
4384 
4385   private void sendRegionClosedNotification(final HRegionInfo regionInfo) {
4386     if (!this.listeners.isEmpty()) {
4387       for (AssignmentListener listener : this.listeners) {
4388         listener.regionClosed(regionInfo);
4389       }
4390     }
4391   }
4392 
4393   /**
4394    * Try to update some region states. If the state machine prevents
4395    * such update, an error message is returned to explain the reason.
4396    *
4397    * It's expected that in each transition there should have just one
4398    * region for opening/closing, 3 regions for splitting/merging.
4399    * These regions should be on the server that requested the change.
4400    *
4401    * Region state machine. Only these transitions
4402    * are expected to be triggered by a region server.
4403    *
4404    * On the state transition:
4405    *  (1) Open/Close should be initiated by master
4406    *      (a) Master sets the region to pending_open/pending_close
4407    *        in memory and hbase:meta after sending the request
4408    *        to the region server
4409    *      (b) Region server reports back to the master
4410    *        after open/close is done (either success/failure)
4411    *      (c) If region server has problem to report the status
4412    *        to master, it must be because the master is down or some
4413    *        temporary network issue. Otherwise, the region server should
4414    *        abort since it must be a bug. If the master is not accessible,
4415    *        the region server should keep trying until the server is
4416    *        stopped or till the status is reported to the (new) master
4417    *      (d) If region server dies in the middle of opening/closing
4418    *        a region, SSH picks it up and finishes it
4419    *      (e) If master dies in the middle, the new master recovers
4420    *        the state during initialization from hbase:meta. Region server
4421    *        can report any transition that has not been reported to
4422    *        the previous active master yet
4423    *  (2) Split/merge is initiated by region servers
4424    *      (a) To split a region, a region server sends a request
4425    *        to master to try to set a region to splitting, together with
4426    *        two daughters (to be created) to splitting new. If approved
4427    *        by the master, the splitting can then move ahead
4428    *      (b) To merge two regions, a region server sends a request to
4429    *        master to try to set the new merged region (to be created) to
4430    *        merging_new, together with two regions (to be merged) to merging.
4431    *        If it is ok with the master, the merge can then move ahead
4432    *      (c) Once the splitting/merging is done, the region server
4433    *        reports the status back to the master either success/failure.
4434    *      (d) Other scenarios should be handled similarly as for
4435    *        region open/close
4436    */
4437   protected String onRegionTransition(final ServerName serverName,
4438       final RegionStateTransition transition) {
4439     TransitionCode code = transition.getTransitionCode();
4440     HRegionInfo hri = HRegionInfo.convert(transition.getRegionInfo(0));
4441     RegionState current = regionStates.getRegionState(hri);
4442     if (LOG.isDebugEnabled()) {
4443       LOG.debug("Got transition " + code + " for "
4444         + (current != null ? current.toString() : hri.getShortNameToLog())
4445         + " from " + serverName);
4446     }
4447     String errorMsg = null;
4448     switch (code) {
4449     case OPENED:
4450       if (current != null && current.isOpened() && current.isOnServer(serverName)) {
4451         LOG.info("Region " + hri.getShortNameToLog() + " is already " + current.getState() + " on "
4452             + serverName);
4453         break;
4454       }
4455     case FAILED_OPEN:
4456       if (current == null
4457           || !current.isPendingOpenOrOpeningOnServer(serverName)) {
4458         errorMsg = hri.getShortNameToLog()
4459           + " is not pending open on " + serverName;
4460       } else if (code == TransitionCode.FAILED_OPEN) {
4461         onRegionFailedOpen(hri, serverName);
4462       } else {
4463         long openSeqNum = HConstants.NO_SEQNUM;
4464         if (transition.hasOpenSeqNum()) {
4465           openSeqNum = transition.getOpenSeqNum();
4466         }
4467         if (openSeqNum < 0) {
4468           errorMsg = "Newly opened region has invalid open seq num " + openSeqNum;
4469         } else {
4470           onRegionOpen(hri, serverName, openSeqNum);
4471         }
4472       }
4473       break;
4474 
4475     case CLOSED:
4476       if (current == null
4477           || !current.isPendingCloseOrClosingOnServer(serverName)) {
4478         errorMsg = hri.getShortNameToLog()
4479           + " is not pending close on " + serverName;
4480       } else {
4481         onRegionClosed(hri);
4482       }
4483       break;
4484 
4485     case READY_TO_SPLIT:
4486       try {
4487         regionStateListener.onRegionSplit(hri);
4488         if (!((HMaster)server).getSplitOrMergeTracker().isSplitOrMergeEnabled(
4489                 Admin.MasterSwitchType.SPLIT)) {
4490           errorMsg = "split switch is off!";
4491           break;
4492         }
4493       } catch (IOException exp) {
4494         errorMsg = StringUtils.stringifyException(exp);
4495         break;
4496       }
4497       // Break out only for errors, otherwise fall through
4498     case SPLIT_PONR:
4499     case SPLIT:
4500       errorMsg =
4501       onRegionSplit(serverName, code, hri, HRegionInfo.convert(transition.getRegionInfo(1)),
4502         HRegionInfo.convert(transition.getRegionInfo(2)));
4503       break;
4504 
4505     case SPLIT_REVERTED:
4506       errorMsg =
4507           onRegionSplitReverted(serverName, hri,
4508             HRegionInfo.convert(transition.getRegionInfo(1)),
4509             HRegionInfo.convert(transition.getRegionInfo(2)));
4510       if (org.apache.commons.lang.StringUtils.isEmpty(errorMsg)) {
4511         try {
4512           regionStateListener.onRegionSplitReverted(hri);
4513         } catch (IOException exp) {
4514           LOG.warn(StringUtils.stringifyException(exp));
4515         }
4516       }
4517       break;
4518     case READY_TO_MERGE:
4519       if (!((HMaster)server).getSplitOrMergeTracker().isSplitOrMergeEnabled(
4520               Admin.MasterSwitchType.MERGE)) {
4521         errorMsg = "merge switch is off!";
4522         break;
4523       }
4524       // Break out only for errors, otherwise fall through
4525     case MERGE_PONR:
4526     case MERGED:
4527       errorMsg = onRegionMerge(serverName, code, hri,
4528         HRegionInfo.convert(transition.getRegionInfo(1)),
4529         HRegionInfo.convert(transition.getRegionInfo(2)));
4530       if (code == TransitionCode.MERGED && org.apache.commons.lang.StringUtils.isEmpty(errorMsg)) {
4531         try {
4532           regionStateListener.onRegionMerged(hri);
4533         } catch (IOException exp) {
4534           errorMsg = StringUtils.stringifyException(exp);
4535         }
4536       }
4537       break;
4538     case MERGE_REVERTED:
4539         errorMsg = onRegionMergeReverted(serverName, code, hri,
4540                 HRegionInfo.convert(transition.getRegionInfo(1)),
4541                 HRegionInfo.convert(transition.getRegionInfo(2)));
4542       break;
4543 
4544     default:
4545       errorMsg = "Unexpected transition code " + code;
4546     }
4547     if (errorMsg != null) {
4548       LOG.error("Failed to transtion region from " + current + " to "
4549         + code + " by " + serverName + ": " + errorMsg);
4550     }
4551     return errorMsg;
4552   }
4553 
4554   /**
4555    * @return Instance of load balancer
4556    */
4557   public LoadBalancer getBalancer() {
4558     return this.balancer;
4559   }
4560 
4561   public Map<ServerName, List<HRegionInfo>>
4562     getSnapShotOfAssignment(Collection<HRegionInfo> infos) {
4563     return getRegionStates().getRegionAssignments(infos);
4564   }
4565 
4566   void setRegionStateListener(RegionStateListener listener) {
4567     this.regionStateListener = listener;
4568   }
4569 
4570   /*
4571    * This is only used for unit-testing split failures.
4572    */
4573   @VisibleForTesting
4574   public static void setTestSkipSplitHandling(boolean skipSplitHandling) {
4575     TEST_SKIP_SPLIT_HANDLING = skipSplitHandling;
4576   }
4577 
4578   /*
4579    * This is only used for unit-testing merge failures.
4580    */
4581   @VisibleForTesting
4582   public static void setTestSkipMergeHandling(boolean skipMergeHandling) {
4583     TEST_SKIP_MERGE_HANDLING = skipMergeHandling;
4584   }
4585 }