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.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.net.InetAddress;
26  import java.net.InetSocketAddress;
27  import java.net.UnknownHostException;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Comparator;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  import java.util.concurrent.TimeUnit;
38  import java.util.concurrent.atomic.AtomicInteger;
39  import java.util.concurrent.atomic.AtomicReference;
40  import java.util.regex.Pattern;
41  
42  import javax.servlet.ServletException;
43  import javax.servlet.http.HttpServlet;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  import org.apache.hadoop.conf.Configuration;
50  import org.apache.hadoop.fs.Path;
51  import org.apache.hadoop.hbase.ClusterStatus;
52  import org.apache.hadoop.hbase.CoordinatedStateException;
53  import org.apache.hadoop.hbase.CoordinatedStateManager;
54  import org.apache.hadoop.hbase.DoNotRetryIOException;
55  import org.apache.hadoop.hbase.HBaseIOException;
56  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
57  import org.apache.hadoop.hbase.HColumnDescriptor;
58  import org.apache.hadoop.hbase.HConstants;
59  import org.apache.hadoop.hbase.HRegionInfo;
60  import org.apache.hadoop.hbase.HTableDescriptor;
61  import org.apache.hadoop.hbase.MasterNotRunningException;
62  import org.apache.hadoop.hbase.MetaMigrationConvertingToPB;
63  import org.apache.hadoop.hbase.MetaTableAccessor;
64  import org.apache.hadoop.hbase.NamespaceDescriptor;
65  import org.apache.hadoop.hbase.NamespaceNotFoundException;
66  import org.apache.hadoop.hbase.PleaseHoldException;
67  import org.apache.hadoop.hbase.ProcedureInfo;
68  import org.apache.hadoop.hbase.RegionStateListener;
69  import org.apache.hadoop.hbase.Server;
70  import org.apache.hadoop.hbase.ServerLoad;
71  import org.apache.hadoop.hbase.ServerName;
72  import org.apache.hadoop.hbase.TableDescriptors;
73  import org.apache.hadoop.hbase.TableName;
74  import org.apache.hadoop.hbase.TableNotDisabledException;
75  import org.apache.hadoop.hbase.TableNotFoundException;
76  import org.apache.hadoop.hbase.UnknownRegionException;
77  import org.apache.hadoop.hbase.classification.InterfaceAudience;
78  import org.apache.hadoop.hbase.client.MetaScanner;
79  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
80  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
81  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
82  import org.apache.hadoop.hbase.client.Result;
83  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
84  import org.apache.hadoop.hbase.exceptions.DeserializationException;
85  import org.apache.hadoop.hbase.executor.ExecutorType;
86  import org.apache.hadoop.hbase.ipc.RpcServer;
87  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
88  import org.apache.hadoop.hbase.master.MasterRpcServices.BalanceSwitchMode;
89  import org.apache.hadoop.hbase.master.RegionState.State;
90  import org.apache.hadoop.hbase.master.balancer.BalancerChore;
91  import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer;
92  import org.apache.hadoop.hbase.master.balancer.ClusterStatusChore;
93  import org.apache.hadoop.hbase.master.balancer.LoadBalancerFactory;
94  import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
95  import org.apache.hadoop.hbase.master.cleaner.LogCleaner;
96  import org.apache.hadoop.hbase.master.handler.DispatchMergingRegionHandler;
97  import org.apache.hadoop.hbase.master.normalizer.RegionNormalizer;
98  import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerChore;
99  import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerFactory;
100 import org.apache.hadoop.hbase.master.procedure.AddColumnFamilyProcedure;
101 import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure;
102 import org.apache.hadoop.hbase.master.procedure.DeleteColumnFamilyProcedure;
103 import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure;
104 import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure;
105 import org.apache.hadoop.hbase.master.procedure.EnableTableProcedure;
106 import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
107 import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
108 import org.apache.hadoop.hbase.master.procedure.MasterProcedureScheduler.ProcedureEvent;
109 import org.apache.hadoop.hbase.master.procedure.ModifyColumnFamilyProcedure;
110 import org.apache.hadoop.hbase.master.procedure.ModifyTableProcedure;
111 import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch;
112 import org.apache.hadoop.hbase.master.procedure.ProcedureSyncWait;
113 import org.apache.hadoop.hbase.master.procedure.TruncateTableProcedure;
114 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
115 import org.apache.hadoop.hbase.monitoring.MemoryBoundedLogMessageBuffer;
116 import org.apache.hadoop.hbase.monitoring.MonitoredTask;
117 import org.apache.hadoop.hbase.monitoring.TaskMonitor;
118 import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan;
119 import org.apache.hadoop.hbase.procedure.MasterProcedureManagerHost;
120 import org.apache.hadoop.hbase.procedure.flush.MasterFlushTableProcedureManager;
121 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
122 import org.apache.hadoop.hbase.procedure2.store.wal.WALProcedureStore;
123 import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
124 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionServerInfo;
125 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
126 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode;
127 import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
128 import org.apache.hadoop.hbase.regionserver.DefaultStoreEngine;
129 import org.apache.hadoop.hbase.regionserver.HRegionServer;
130 import org.apache.hadoop.hbase.regionserver.HStore;
131 import org.apache.hadoop.hbase.regionserver.RSRpcServices;
132 import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
133 import org.apache.hadoop.hbase.regionserver.RegionSplitPolicy;
134 import org.apache.hadoop.hbase.regionserver.compactions.ExploringCompactionPolicy;
135 import org.apache.hadoop.hbase.regionserver.compactions.FIFOCompactionPolicy;
136 import org.apache.hadoop.hbase.replication.regionserver.Replication;
137 import org.apache.hadoop.hbase.security.UserProvider;
138 import org.apache.hadoop.hbase.util.Addressing;
139 import org.apache.hadoop.hbase.util.Bytes;
140 import org.apache.hadoop.hbase.util.CompressionTest;
141 import org.apache.hadoop.hbase.util.ConfigUtil;
142 import org.apache.hadoop.hbase.util.EncryptionTest;
143 import org.apache.hadoop.hbase.util.FSUtils;
144 import org.apache.hadoop.hbase.util.HFileArchiveUtil;
145 import org.apache.hadoop.hbase.util.HasThread;
146 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
147 import org.apache.hadoop.hbase.util.IdLock;
148 import org.apache.hadoop.hbase.util.Pair;
149 import org.apache.hadoop.hbase.util.Threads;
150 import org.apache.hadoop.hbase.util.VersionInfo;
151 import org.apache.hadoop.hbase.zookeeper.DrainingServerTracker;
152 import org.apache.hadoop.hbase.zookeeper.LoadBalancerTracker;
153 import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
154 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
155 import org.apache.hadoop.hbase.zookeeper.RegionNormalizerTracker;
156 import org.apache.hadoop.hbase.zookeeper.RegionServerTracker;
157 import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
158 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
159 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
160 import org.apache.zookeeper.KeeperException;
161 import org.mortbay.jetty.Connector;
162 import org.mortbay.jetty.nio.SelectChannelConnector;
163 import org.mortbay.jetty.servlet.Context;
164 
165 import com.google.common.annotations.VisibleForTesting;
166 import com.google.common.collect.Maps;
167 import com.google.protobuf.Descriptors;
168 import com.google.protobuf.Service;
169 
170 /**
171  * HMaster is the "master server" for HBase. An HBase cluster has one active
172  * master.  If many masters are started, all compete.  Whichever wins goes on to
173  * run the cluster.  All others park themselves in their constructor until
174  * master or cluster shutdown or until the active master loses its lease in
175  * zookeeper.  Thereafter, all running master jostle to take over master role.
176  *
177  * <p>The Master can be asked shutdown the cluster. See {@link #shutdown()}.  In
178  * this case it will tell all regionservers to go down and then wait on them
179  * all reporting in that they are down.  This master will then shut itself down.
180  *
181  * <p>You can also shutdown just this master.  Call {@link #stopMaster()}.
182  *
183  * @see org.apache.zookeeper.Watcher
184  */
185 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
186 @SuppressWarnings("deprecation")
187 public class HMaster extends HRegionServer implements MasterServices, Server {
188   private static final Log LOG = LogFactory.getLog(HMaster.class.getName());
189 
190   /**
191    * Protection against zombie master. Started once Master accepts active responsibility and
192    * starts taking over responsibilities. Allows a finite time window before giving up ownership.
193    */
194   private static class InitializationMonitor extends HasThread {
195     /** The amount of time in milliseconds to sleep before checking initialization status. */
196     public static final String TIMEOUT_KEY = "hbase.master.initializationmonitor.timeout";
197     public static final long TIMEOUT_DEFAULT = TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
198 
199     /**
200      * When timeout expired and initialization has not complete, call {@link System#exit(int)} when
201      * true, do nothing otherwise.
202      */
203     public static final String HALT_KEY = "hbase.master.initializationmonitor.haltontimeout";
204     public static final boolean HALT_DEFAULT = false;
205 
206     private final HMaster master;
207     private final long timeout;
208     private final boolean haltOnTimeout;
209 
210     /** Creates a Thread that monitors the {@link #isInitialized()} state. */
211     InitializationMonitor(HMaster master) {
212       super("MasterInitializationMonitor");
213       this.master = master;
214       this.timeout = master.getConfiguration().getLong(TIMEOUT_KEY, TIMEOUT_DEFAULT);
215       this.haltOnTimeout = master.getConfiguration().getBoolean(HALT_KEY, HALT_DEFAULT);
216       this.setDaemon(true);
217     }
218 
219     @Override
220     public void run() {
221       try {
222         while (!master.isStopped() && master.isActiveMaster()) {
223           Thread.sleep(timeout);
224           if (master.isInitialized()) {
225             LOG.debug("Initialization completed within allotted tolerance. Monitor exiting.");
226           } else {
227             LOG.error("Master failed to complete initialization after " + timeout + "ms. Please"
228                 + " consider submitting a bug report including a thread dump of this process.");
229             if (haltOnTimeout) {
230               LOG.error("Zombie Master exiting. Thread dump to stdout");
231               Threads.printThreadInfo(System.out, "Zombie HMaster");
232               System.exit(-1);
233             }
234           }
235         }
236       } catch (InterruptedException ie) {
237         LOG.trace("InitMonitor thread interrupted. Existing.");
238       }
239     }
240   }
241 
242   // MASTER is name of the webapp and the attribute name used stuffing this
243   //instance into web context.
244   public static final String MASTER = "master";
245 
246   // Manager and zk listener for master election
247   private final ActiveMasterManager activeMasterManager;
248   // Region server tracker
249   RegionServerTracker regionServerTracker;
250   // Draining region server tracker
251   private DrainingServerTracker drainingServerTracker;
252   // Tracker for load balancer state
253   LoadBalancerTracker loadBalancerTracker;
254 
255   // Tracker for region normalizer state
256   private RegionNormalizerTracker regionNormalizerTracker;
257 
258   /** Namespace stuff */
259   private TableNamespaceManager tableNamespaceManager;
260 
261   // Metrics for the HMaster
262   final MetricsMaster metricsMaster;
263   // file system manager for the master FS operations
264   private MasterFileSystem fileSystemManager;
265 
266   // server manager to deal with region server info
267   volatile ServerManager serverManager;
268 
269   // manager of assignment nodes in zookeeper
270   AssignmentManager assignmentManager;
271 
272   // buffer for "fatal error" notices from region servers
273   // in the cluster. This is only used for assisting
274   // operations/debugging.
275   MemoryBoundedLogMessageBuffer rsFatals;
276 
277   // flag set after we become the active master (used for testing)
278   private volatile boolean isActiveMaster = false;
279 
280   // flag set after we complete initialization once active,
281   // it is not private since it's used in unit tests
282   private final ProcedureEvent initialized = new ProcedureEvent("master initialized");
283 
284   // flag set after master services are started,
285   // initialization may have not completed yet.
286   volatile boolean serviceStarted = false;
287 
288   // flag set after we complete assignMeta.
289   private final ProcedureEvent serverCrashProcessingEnabled =
290     new ProcedureEvent("server crash processing");
291 
292   LoadBalancer balancer;
293   private RegionNormalizer normalizer;
294   private BalancerChore balancerChore;
295   private RegionNormalizerChore normalizerChore;
296   private ClusterStatusChore clusterStatusChore;
297   private ClusterStatusPublisher clusterStatusPublisherChore = null;
298 
299   CatalogJanitor catalogJanitorChore;
300   private LogCleaner logCleaner;
301   private HFileCleaner hfileCleaner;
302   private ExpiredMobFileCleanerChore expiredMobFileCleanerChore;
303   private MobFileCompactionChore mobFileCompactChore;
304   MasterMobFileCompactionThread mobFileCompactThread;
305   // used to synchronize the mobFileCompactionStates
306   private final IdLock mobFileCompactionLock = new IdLock();
307   // save the information of mob file compactions in tables.
308   // the key is table name, the value is the number of compactions in that table.
309   private Map<TableName, AtomicInteger> mobFileCompactionStates = Maps.newConcurrentMap();
310 
311   MasterCoprocessorHost cpHost;
312 
313   private final boolean preLoadTableDescriptors;
314 
315   // Time stamps for when a hmaster became active
316   private long masterActiveTime;
317 
318   //should we check the compression codec type at master side, default true, HBASE-6370
319   private final boolean masterCheckCompression;
320 
321   //should we check encryption settings at master side, default true
322   private final boolean masterCheckEncryption;
323 
324   Map<String, Service> coprocessorServiceHandlers = Maps.newHashMap();
325 
326   // monitor for snapshot of hbase tables
327   SnapshotManager snapshotManager;
328   // monitor for distributed procedures
329   MasterProcedureManagerHost mpmHost;
330 
331   // it is assigned after 'initialized' guard set to true, so should be volatile
332   private volatile MasterQuotaManager quotaManager;
333 
334   private ProcedureExecutor<MasterProcedureEnv> procedureExecutor;
335   private WALProcedureStore procedureStore;
336 
337   /** flag used in test cases in order to simulate RS failures during master initialization */
338   private volatile boolean initializationBeforeMetaAssignment = false;
339 
340   /** jetty server for master to redirect requests to regionserver infoServer */
341   private org.mortbay.jetty.Server masterJettyServer;
342 
343   public static class RedirectServlet extends HttpServlet {
344     private static final long serialVersionUID = 2894774810058302472L;
345     private static int regionServerInfoPort;
346 
347     @Override
348     public void doGet(HttpServletRequest request,
349         HttpServletResponse response) throws ServletException, IOException {
350       String redirectUrl = request.getScheme() + "://"
351         + request.getServerName() + ":" + regionServerInfoPort
352         + request.getRequestURI();
353       response.sendRedirect(redirectUrl);
354     }
355   }
356 
357   /**
358    * Initializes the HMaster. The steps are as follows:
359    * <p>
360    * <ol>
361    * <li>Initialize the local HRegionServer
362    * <li>Start the ActiveMasterManager.
363    * </ol>
364    * <p>
365    * Remaining steps of initialization occur in
366    * #finishActiveMasterInitialization(MonitoredTask) after
367    * the master becomes the active one.
368    *
369    * @throws InterruptedException
370    * @throws KeeperException
371    * @throws IOException
372    */
373   public HMaster(final Configuration conf, CoordinatedStateManager csm)
374       throws IOException, KeeperException, InterruptedException {
375     super(conf, csm);
376     this.rsFatals = new MemoryBoundedLogMessageBuffer(
377       conf.getLong("hbase.master.buffer.for.rs.fatals", 1*1024*1024));
378 
379     LOG.info("hbase.rootdir=" + FSUtils.getRootDir(this.conf) +
380       ", hbase.cluster.distributed=" + this.conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, false));
381 
382     // Disable usage of meta replicas in the master
383     this.conf.setBoolean(HConstants.USE_META_REPLICAS, false);
384 
385     Replication.decorateMasterConfiguration(this.conf);
386 
387     // Hack! Maps DFSClient => Master for logs.  HDFS made this
388     // config param for task trackers, but we can piggyback off of it.
389     if (this.conf.get("mapreduce.task.attempt.id") == null) {
390       this.conf.set("mapreduce.task.attempt.id", "hb_m_" + this.serverName.toString());
391     }
392 
393     // should we check the compression codec type at master side, default true, HBASE-6370
394     this.masterCheckCompression = conf.getBoolean("hbase.master.check.compression", true);
395 
396     // should we check encryption settings at master side, default true
397     this.masterCheckEncryption = conf.getBoolean("hbase.master.check.encryption", true);
398 
399     this.metricsMaster = new MetricsMaster(new MetricsMasterWrapperImpl(this));
400 
401     // preload table descriptor at startup
402     this.preLoadTableDescriptors = conf.getBoolean("hbase.master.preload.tabledescriptors", true);
403 
404     // Do we publish the status?
405 
406     boolean shouldPublish = conf.getBoolean(HConstants.STATUS_PUBLISHED,
407         HConstants.STATUS_PUBLISHED_DEFAULT);
408     Class<? extends ClusterStatusPublisher.Publisher> publisherClass =
409         conf.getClass(ClusterStatusPublisher.STATUS_PUBLISHER_CLASS,
410             ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS,
411             ClusterStatusPublisher.Publisher.class);
412 
413     if (shouldPublish) {
414       if (publisherClass == null) {
415         LOG.warn(HConstants.STATUS_PUBLISHED + " is true, but " +
416             ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS +
417             " is not set - not publishing status");
418       } else {
419         clusterStatusPublisherChore = new ClusterStatusPublisher(this, conf, publisherClass);
420         getChoreService().scheduleChore(clusterStatusPublisherChore);
421       }
422     }
423 
424     // Some unit tests don't need a cluster, so no zookeeper at all
425     if (!conf.getBoolean("hbase.testing.nocluster", false)) {
426       activeMasterManager = new ActiveMasterManager(zooKeeper, this.serverName, this);
427       int infoPort = putUpJettyServer();
428       startActiveMasterManager(infoPort);
429     } else {
430       activeMasterManager = null;
431     }
432   }
433 
434   // return the actual infoPort, -1 means disable info server.
435   private int putUpJettyServer() throws IOException {
436     if (!conf.getBoolean("hbase.master.infoserver.redirect", true)) {
437       return -1;
438     }
439     int infoPort = conf.getInt("hbase.master.info.port.orig",
440       HConstants.DEFAULT_MASTER_INFOPORT);
441     // -1 is for disabling info server, so no redirecting
442     if (infoPort < 0 || infoServer == null) {
443       return -1;
444     }
445     String addr = conf.get("hbase.master.info.bindAddress", "0.0.0.0");
446     if (!Addressing.isLocalAddress(InetAddress.getByName(addr))) {
447       String msg =
448           "Failed to start redirecting jetty server. Address " + addr
449               + " does not belong to this host. Correct configuration parameter: "
450               + "hbase.master.info.bindAddress";
451       LOG.error(msg);
452       throw new IOException(msg);
453     }
454 
455     RedirectServlet.regionServerInfoPort = infoServer.getPort();
456     if(RedirectServlet.regionServerInfoPort == infoPort) {
457       return infoPort;
458     }
459     masterJettyServer = new org.mortbay.jetty.Server();
460     Connector connector = new SelectChannelConnector();
461     connector.setHost(addr);
462     connector.setPort(infoPort);
463     masterJettyServer.addConnector(connector);
464     masterJettyServer.setStopAtShutdown(true);
465     Context context = new Context(masterJettyServer, "/", Context.NO_SESSIONS);
466     context.addServlet(RedirectServlet.class, "/*");
467     try {
468       masterJettyServer.start();
469     } catch (Exception e) {
470       throw new IOException("Failed to start redirecting jetty server", e);
471     }
472     return connector.getLocalPort();
473   }
474 
475   /**
476    * For compatibility, if failed with regionserver credentials, try the master one
477    */
478   @Override
479   protected void login(UserProvider user, String host) throws IOException {
480     try {
481       super.login(user, host);
482     } catch (IOException ie) {
483       user.login("hbase.master.keytab.file",
484         "hbase.master.kerberos.principal", host);
485     }
486   }
487 
488   /**
489    * If configured to put regions on active master,
490    * wait till a backup master becomes active.
491    * Otherwise, loop till the server is stopped or aborted.
492    */
493   @Override
494   protected void waitForMasterActive(){
495     boolean tablesOnMaster = BaseLoadBalancer.tablesOnMaster(conf);
496     while (!(tablesOnMaster && isActiveMaster)
497         && !isStopped() && !isAborted()) {
498       sleeper.sleep();
499     }
500   }
501 
502   @VisibleForTesting
503   public MasterRpcServices getMasterRpcServices() {
504     return (MasterRpcServices)rpcServices;
505   }
506 
507   public boolean balanceSwitch(final boolean b) throws IOException {
508     return getMasterRpcServices().switchBalancer(b, BalanceSwitchMode.ASYNC);
509   }
510 
511   @Override
512   protected String getProcessName() {
513     return MASTER;
514   }
515 
516   @Override
517   protected boolean canCreateBaseZNode() {
518     return true;
519   }
520 
521   @Override
522   protected boolean canUpdateTableDescriptor() {
523     return true;
524   }
525 
526   @Override
527   protected RSRpcServices createRpcServices() throws IOException {
528     return new MasterRpcServices(this);
529   }
530 
531   @Override
532   protected void configureInfoServer() {
533     infoServer.addServlet("master-status", "/master-status", MasterStatusServlet.class);
534     infoServer.setAttribute(MASTER, this);
535     if (BaseLoadBalancer.tablesOnMaster(conf)) {
536       super.configureInfoServer();
537     }
538   }
539 
540   @Override
541   protected Class<? extends HttpServlet> getDumpServlet() {
542     return MasterDumpServlet.class;
543   }
544 
545   /**
546    * Emit the HMaster metrics, such as region in transition metrics.
547    * Surrounding in a try block just to be sure metrics doesn't abort HMaster.
548    */
549   @Override
550   protected void doMetrics() {
551     try {
552       if (assignmentManager != null) {
553         assignmentManager.updateRegionsInTransitionMetrics();
554       }
555     } catch (Throwable e) {
556       LOG.error("Couldn't update metrics: " + e.getMessage());
557     }
558   }
559 
560   MetricsMaster getMasterMetrics() {
561     return metricsMaster;
562   }
563 
564   /**
565    * Initialize all ZK based system trackers.
566    * @throws IOException
567    * @throws InterruptedException
568    * @throws KeeperException
569    * @throws CoordinatedStateException
570    */
571   void initializeZKBasedSystemTrackers() throws IOException,
572       InterruptedException, KeeperException, CoordinatedStateException {
573     this.balancer = LoadBalancerFactory.getLoadBalancer(conf);
574     this.normalizer = RegionNormalizerFactory.getRegionNormalizer(conf);
575     this.normalizer.setMasterServices(this);
576     this.loadBalancerTracker = new LoadBalancerTracker(zooKeeper, this);
577     this.loadBalancerTracker.start();
578     this.regionNormalizerTracker = new RegionNormalizerTracker(zooKeeper, this);
579     this.regionNormalizerTracker.start();
580     this.assignmentManager = new AssignmentManager(this, serverManager,
581       this.balancer, this.service, this.metricsMaster,
582       this.tableLockManager);
583     zooKeeper.registerListenerFirst(assignmentManager);
584 
585     this.regionServerTracker = new RegionServerTracker(zooKeeper, this,
586         this.serverManager);
587     this.regionServerTracker.start();
588 
589     this.drainingServerTracker = new DrainingServerTracker(zooKeeper, this,
590       this.serverManager);
591     this.drainingServerTracker.start();
592 
593     // Set the cluster as up.  If new RSs, they'll be waiting on this before
594     // going ahead with their startup.
595     boolean wasUp = this.clusterStatusTracker.isClusterUp();
596     if (!wasUp) this.clusterStatusTracker.setClusterUp();
597 
598     LOG.info("Server active/primary master=" + this.serverName +
599         ", sessionid=0x" +
600         Long.toHexString(this.zooKeeper.getRecoverableZooKeeper().getSessionId()) +
601         ", setting cluster-up flag (Was=" + wasUp + ")");
602 
603     // create/initialize the snapshot manager and other procedure managers
604     this.snapshotManager = new SnapshotManager();
605     this.mpmHost = new MasterProcedureManagerHost();
606     this.mpmHost.register(this.snapshotManager);
607     this.mpmHost.register(new MasterFlushTableProcedureManager());
608     this.mpmHost.loadProcedures(conf);
609     this.mpmHost.initialize(this, this.metricsMaster);
610   }
611 
612   /**
613    * Finish initialization of HMaster after becoming the primary master.
614    *
615    * <ol>
616    * <li>Initialize master components - file system manager, server manager,
617    *     assignment manager, region server tracker, etc</li>
618    * <li>Start necessary service threads - balancer, catalog janior,
619    *     executor services, etc</li>
620    * <li>Set cluster as UP in ZooKeeper</li>
621    * <li>Wait for RegionServers to check-in</li>
622    * <li>Split logs and perform data recovery, if necessary</li>
623    * <li>Ensure assignment of meta/namespace regions<li>
624    * <li>Handle either fresh cluster start or master failover</li>
625    * </ol>
626    *
627    * @throws IOException
628    * @throws InterruptedException
629    * @throws KeeperException
630    * @throws CoordinatedStateException
631    */
632   private void finishActiveMasterInitialization(MonitoredTask status)
633       throws IOException, InterruptedException, KeeperException, CoordinatedStateException {
634 
635     isActiveMaster = true;
636     Thread zombieDetector = new Thread(new InitializationMonitor(this));
637     zombieDetector.start();
638 
639     /*
640      * We are active master now... go initialize components we need to run.
641      * Note, there may be dross in zk from previous runs; it'll get addressed
642      * below after we determine if cluster startup or failover.
643      */
644 
645     status.setStatus("Initializing Master file system");
646 
647     this.masterActiveTime = System.currentTimeMillis();
648     // TODO: Do this using Dependency Injection, using PicoContainer, Guice or Spring.
649     this.fileSystemManager = new MasterFileSystem(this, this);
650 
651     // enable table descriptors cache
652     this.tableDescriptors.setCacheOn();
653     // set the META's descriptor to the correct replication
654     this.tableDescriptors.get(TableName.META_TABLE_NAME).setRegionReplication(
655         conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM));
656     // warm-up HTDs cache on master initialization
657     if (preLoadTableDescriptors) {
658       status.setStatus("Pre-loading table descriptors");
659       this.tableDescriptors.getAll();
660     }
661 
662     // publish cluster ID
663     status.setStatus("Publishing Cluster ID in ZooKeeper");
664     ZKClusterId.setClusterId(this.zooKeeper, fileSystemManager.getClusterId());
665     this.serverManager = createServerManager(this, this);
666 
667     setupClusterConnection();
668 
669     // Invalidate all write locks held previously
670     this.tableLockManager.reapWriteLocks();
671 
672     status.setStatus("Initializing ZK system trackers");
673     initializeZKBasedSystemTrackers();
674 
675     // initialize master side coprocessors before we start handling requests
676     status.setStatus("Initializing master coprocessors");
677     this.cpHost = new MasterCoprocessorHost(this, this.conf);
678 
679     // start up all service threads.
680     status.setStatus("Initializing master service threads");
681     startServiceThreads();
682 
683     // Wake up this server to check in
684     sleeper.skipSleepCycle();
685 
686     // Wait for region servers to report in
687     this.serverManager.waitForRegionServers(status);
688     // Check zk for region servers that are up but didn't register
689     for (ServerName sn: this.regionServerTracker.getOnlineServers()) {
690       // The isServerOnline check is opportunistic, correctness is handled inside
691       if (!this.serverManager.isServerOnline(sn)
692           && serverManager.checkAndRecordNewServer(sn, ServerLoad.EMPTY_SERVERLOAD)) {
693         LOG.info("Registered server found up in zk but who has not yet reported in: " + sn);
694       }
695     }
696 
697     // get a list for previously failed RS which need log splitting work
698     // we recover hbase:meta region servers inside master initialization and
699     // handle other failed servers in SSH in order to start up master node ASAP
700     Set<ServerName> previouslyFailedServers =
701       this.fileSystemManager.getFailedServersFromLogFolders();
702 
703     // log splitting for hbase:meta server
704     ServerName oldMetaServerLocation = metaTableLocator.getMetaRegionLocation(this.getZooKeeper());
705     if (oldMetaServerLocation != null && previouslyFailedServers.contains(oldMetaServerLocation)) {
706       splitMetaLogBeforeAssignment(oldMetaServerLocation);
707       // Note: we can't remove oldMetaServerLocation from previousFailedServers list because it
708       // may also host user regions
709     }
710     Set<ServerName> previouslyFailedMetaRSs = getPreviouselyFailedMetaServersFromZK();
711     // need to use union of previouslyFailedMetaRSs recorded in ZK and previouslyFailedServers
712     // instead of previouslyFailedMetaRSs alone to address the following two situations:
713     // 1) the chained failure situation(recovery failed multiple times in a row).
714     // 2) master get killed right before it could delete the recovering hbase:meta from ZK while the
715     // same server still has non-meta wals to be replayed so that
716     // removeStaleRecoveringRegionsFromZK can't delete the stale hbase:meta region
717     // Passing more servers into splitMetaLog is all right. If a server doesn't have hbase:meta wal,
718     // there is no op for the server.
719     previouslyFailedMetaRSs.addAll(previouslyFailedServers);
720 
721     this.initializationBeforeMetaAssignment = true;
722 
723     // Wait for regionserver to finish initialization.
724     if (BaseLoadBalancer.tablesOnMaster(conf)) {
725       waitForServerOnline();
726     }
727 
728     //initialize load balancer
729     this.balancer.setClusterStatus(getClusterStatus());
730     this.balancer.setMasterServices(this);
731     this.balancer.initialize();
732 
733     // Check if master is shutting down because of some issue
734     // in initializing the regionserver or the balancer.
735     if (isStopped()) return;
736 
737     // Make sure meta assigned before proceeding.
738     status.setStatus("Assigning Meta Region");
739     assignMeta(status, previouslyFailedMetaRSs, HRegionInfo.DEFAULT_REPLICA_ID);
740     // check if master is shutting down because above assignMeta could return even hbase:meta isn't
741     // assigned when master is shutting down
742     if (isStopped()) return;
743 
744     status.setStatus("Submitting log splitting work for previously failed region servers");
745     // Master has recovered hbase:meta region server and we put
746     // other failed region servers in a queue to be handled later by SSH
747     for (ServerName tmpServer : previouslyFailedServers) {
748       this.serverManager.processDeadServer(tmpServer, true);
749     }
750 
751     // Update meta with new PB serialization if required. i.e migrate all HRI to PB serialization
752     // in meta. This must happen before we assign all user regions or else the assignment will fail.
753     if (this.conf.getBoolean("hbase.MetaMigrationConvertingToPB", true)) {
754       MetaMigrationConvertingToPB.updateMetaIfNecessary(this);
755     }
756 
757     // Fix up assignment manager status
758     status.setStatus("Starting assignment manager");
759     this.assignmentManager.joinCluster();
760 
761     // set cluster status again after user regions are assigned
762     this.balancer.setClusterStatus(getClusterStatus());
763 
764     // Start balancer and meta catalog janitor after meta and regions have been assigned.
765     status.setStatus("Starting balancer and catalog janitor");
766     this.clusterStatusChore = new ClusterStatusChore(this, balancer);
767     getChoreService().scheduleChore(clusterStatusChore);
768     this.balancerChore = new BalancerChore(this);
769     getChoreService().scheduleChore(balancerChore);
770     this.normalizerChore = new RegionNormalizerChore(this);
771     getChoreService().scheduleChore(normalizerChore);
772     this.catalogJanitorChore = new CatalogJanitor(this, this);
773     getChoreService().scheduleChore(catalogJanitorChore);
774 
775     status.setStatus("Starting namespace manager");
776     initNamespace();
777 
778     if (this.cpHost != null) {
779       try {
780         this.cpHost.preMasterInitialization();
781       } catch (IOException e) {
782         LOG.error("Coprocessor preMasterInitialization() hook failed", e);
783       }
784     }
785 
786     status.markComplete("Initialization successful");
787     LOG.info("Master has completed initialization");
788     configurationManager.registerObserver(this.balancer);
789 
790     // Set master as 'initialized'.
791     setInitialized(true);
792 
793     status.setStatus("Starting quota manager");
794     initQuotaManager();
795 
796     // assign the meta replicas
797     Set<ServerName> EMPTY_SET = new HashSet<ServerName>();
798     int numReplicas = conf.getInt(HConstants.META_REPLICAS_NUM,
799            HConstants.DEFAULT_META_REPLICA_NUM);
800     for (int i = 1; i < numReplicas; i++) {
801       assignMeta(status, EMPTY_SET, i);
802     }
803     unassignExcessMetaReplica(zooKeeper, numReplicas);
804 
805     // clear the dead servers with same host name and port of online server because we are not
806     // removing dead server with same hostname and port of rs which is trying to check in before
807     // master initialization. See HBASE-5916.
808     this.serverManager.clearDeadServersWithSameHostNameAndPortOfOnlineServer();
809 
810     // Check and set the znode ACLs if needed in case we are overtaking a non-secure configuration
811     status.setStatus("Checking ZNode ACLs");
812     zooKeeper.checkAndSetZNodeAcls();
813 
814     status.setStatus("Calling postStartMaster coprocessors");
815     
816     this.expiredMobFileCleanerChore = new ExpiredMobFileCleanerChore(this);
817     getChoreService().scheduleChore(this.expiredMobFileCleanerChore);
818     this.mobFileCompactChore = new MobFileCompactionChore(this);
819     getChoreService().scheduleChore(this.mobFileCompactChore);
820     this.mobFileCompactThread = new MasterMobFileCompactionThread(this);
821 
822     if (this.cpHost != null) {
823       // don't let cp initialization errors kill the master
824       try {
825         this.cpHost.postStartMaster();
826       } catch (IOException ioe) {
827         LOG.error("Coprocessor postStartMaster() hook failed", ioe);
828       }
829     }
830 
831     zombieDetector.interrupt();
832   }
833 
834   private void initQuotaManager() throws IOException {
835     quotaManager = new MasterQuotaManager(this);
836     this.assignmentManager.setRegionStateListener((RegionStateListener) quotaManager);
837     quotaManager.start();
838   }
839 
840   /**
841    * Create a {@link ServerManager} instance.
842    * @param master
843    * @param services
844    * @return An instance of {@link ServerManager}
845    * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
846    * @throws IOException
847    */
848   ServerManager createServerManager(final Server master,
849       final MasterServices services)
850   throws IOException {
851     // We put this out here in a method so can do a Mockito.spy and stub it out
852     // w/ a mocked up ServerManager.
853     return new ServerManager(master, services);
854   }
855 
856   private void unassignExcessMetaReplica(ZooKeeperWatcher zkw, int numMetaReplicasConfigured) {
857     // unassign the unneeded replicas (for e.g., if the previous master was configured
858     // with a replication of 3 and now it is 2, we need to unassign the 1 unneeded replica)
859     try {
860       List<String> metaReplicaZnodes = zooKeeper.getMetaReplicaNodes();
861       for (String metaReplicaZnode : metaReplicaZnodes) {
862         int replicaId = zooKeeper.getMetaReplicaIdFromZnode(metaReplicaZnode);
863         if (replicaId >= numMetaReplicasConfigured) {
864           RegionState r = MetaTableLocator.getMetaRegionState(zkw, replicaId);
865           LOG.info("Closing excess replica of meta region " + r.getRegion());
866           // send a close and wait for a max of 30 seconds
867           ServerManager.closeRegionSilentlyAndWait(getConnection(), r.getServerName(),
868               r.getRegion(), 30000);
869           ZKUtil.deleteNode(zkw, zkw.getZNodeForReplica(replicaId));
870         }
871       }
872     } catch (Exception ex) {
873       // ignore the exception since we don't want the master to be wedged due to potential
874       // issues in the cleanup of the extra regions. We can do that cleanup via hbck or manually
875       LOG.warn("Ignoring exception " + ex);
876     }
877   }
878 
879   /**
880    * Check <code>hbase:meta</code> is assigned. If not, assign it.
881    * @param status MonitoredTask
882    * @param previouslyFailedMetaRSs
883    * @param replicaId
884    * @throws InterruptedException
885    * @throws IOException
886    * @throws KeeperException
887    */
888   void assignMeta(MonitoredTask status, Set<ServerName> previouslyFailedMetaRSs, int replicaId)
889       throws InterruptedException, IOException, KeeperException {
890     // Work on meta region
891     int assigned = 0;
892     long timeout = this.conf.getLong("hbase.catalog.verification.timeout", 1000);
893     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
894       status.setStatus("Assigning hbase:meta region");
895     } else {
896       status.setStatus("Assigning hbase:meta region, replicaId " + replicaId);
897     }
898     // Get current meta state from zk.
899     RegionStates regionStates = assignmentManager.getRegionStates();
900     RegionState metaState = MetaTableLocator.getMetaRegionState(getZooKeeper(), replicaId);
901     HRegionInfo hri = RegionReplicaUtil.getRegionInfoForReplica(HRegionInfo.FIRST_META_REGIONINFO,
902         replicaId);
903     ServerName currentMetaServer = metaState.getServerName();
904     if (!ConfigUtil.useZKForAssignment(conf)) {
905       regionStates.createRegionState(hri, metaState.getState(),
906         currentMetaServer, null);
907     } else {
908       regionStates.createRegionState(hri);
909     }
910     boolean rit = this.assignmentManager.
911       processRegionInTransitionAndBlockUntilAssigned(hri);
912     boolean metaRegionLocation = metaTableLocator.verifyMetaRegionLocation(
913       this.getConnection(), this.getZooKeeper(), timeout, replicaId);
914     if (!metaRegionLocation || !metaState.isOpened()) {
915       // Meta location is not verified. It should be in transition, or offline.
916       // We will wait for it to be assigned in enableSSHandWaitForMeta below.
917       assigned++;
918       if (!ConfigUtil.useZKForAssignment(conf)) {
919         assignMetaZkLess(regionStates, metaState, timeout, previouslyFailedMetaRSs);
920       } else if (!rit) {
921         // Assign meta since not already in transition
922         if (currentMetaServer != null) {
923           // If the meta server is not known to be dead or online,
924           // just split the meta log, and don't expire it since this
925           // could be a full cluster restart. Otherwise, we will think
926           // this is a failover and lose previous region locations.
927           // If it is really a failover case, AM will find out in rebuilding
928           // user regions. Otherwise, we are good since all logs are split
929           // or known to be replayed before user regions are assigned.
930           if (serverManager.isServerOnline(currentMetaServer)) {
931             LOG.info("Forcing expire of " + currentMetaServer);
932             serverManager.expireServer(currentMetaServer);
933           }
934           if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) {
935             splitMetaLogBeforeAssignment(currentMetaServer);
936             previouslyFailedMetaRSs.add(currentMetaServer);
937           }
938         }
939         assignmentManager.assignMeta(hri);
940       }
941     } else {
942       // Region already assigned. We didn't assign it. Add to in-memory state.
943       regionStates.updateRegionState(
944         HRegionInfo.FIRST_META_REGIONINFO, State.OPEN, currentMetaServer);
945       this.assignmentManager.regionOnline(
946         HRegionInfo.FIRST_META_REGIONINFO, currentMetaServer);
947     }
948 
949     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) enableMeta(TableName.META_TABLE_NAME);
950 
951     if ((RecoveryMode.LOG_REPLAY == this.getMasterFileSystem().getLogRecoveryMode())
952         && (!previouslyFailedMetaRSs.isEmpty())) {
953       // replay WAL edits mode need new hbase:meta RS is assigned firstly
954       status.setStatus("replaying log for Meta Region");
955       this.fileSystemManager.splitMetaLog(previouslyFailedMetaRSs);
956     }
957 
958     // Make sure a hbase:meta location is set. We need to enable SSH here since
959     // if the meta region server is died at this time, we need it to be re-assigned
960     // by SSH so that system tables can be assigned.
961     // No need to wait for meta is assigned = 0 when meta is just verified.
962     if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) enableCrashedServerProcessing(assigned != 0);
963     LOG.info("hbase:meta with replicaId " + replicaId + " assigned=" + assigned + ", rit=" + rit +
964       ", location=" + metaTableLocator.getMetaRegionLocation(this.getZooKeeper(), replicaId));
965     status.setStatus("META assigned.");
966   }
967 
968   private void assignMetaZkLess(RegionStates regionStates, RegionState regionState, long timeout,
969       Set<ServerName> previouslyFailedRs) throws IOException, KeeperException {
970     ServerName currentServer = regionState.getServerName();
971     if (serverManager.isServerOnline(currentServer)) {
972       LOG.info("Meta was in transition on " + currentServer);
973       assignmentManager.processRegionInTransitionZkLess();
974     } else {
975       if (currentServer != null) {
976         if (regionState.getRegion().getReplicaId() == HRegionInfo.DEFAULT_REPLICA_ID) {
977           splitMetaLogBeforeAssignment(currentServer);
978           regionStates.logSplit(HRegionInfo.FIRST_META_REGIONINFO);
979           previouslyFailedRs.add(currentServer);
980         }
981       }
982       LOG.info("Re-assigning hbase:meta, it was on " + currentServer);
983       regionStates.updateRegionState(regionState.getRegion(), State.OFFLINE);
984       assignmentManager.assignMeta(regionState.getRegion());
985     }
986   }
987 
988   void initNamespace() throws IOException {
989     //create namespace manager
990     tableNamespaceManager = new TableNamespaceManager(this);
991     tableNamespaceManager.start();
992   }
993 
994   boolean isCatalogJanitorEnabled() {
995     return catalogJanitorChore != null ?
996       catalogJanitorChore.getEnabled() : false;
997   }
998 
999   private void splitMetaLogBeforeAssignment(ServerName currentMetaServer) throws IOException {
1000     if (RecoveryMode.LOG_REPLAY == this.getMasterFileSystem().getLogRecoveryMode()) {
1001       // In log replay mode, we mark hbase:meta region as recovering in ZK
1002       Set<HRegionInfo> regions = new HashSet<HRegionInfo>();
1003       regions.add(HRegionInfo.FIRST_META_REGIONINFO);
1004       this.fileSystemManager.prepareLogReplay(currentMetaServer, regions);
1005     } else {
1006       // In recovered.edits mode: create recovered edits file for hbase:meta server
1007       this.fileSystemManager.splitMetaLog(currentMetaServer);
1008     }
1009   }
1010 
1011   private void enableCrashedServerProcessing(final boolean waitForMeta)
1012   throws IOException, InterruptedException {
1013     // If crashed server processing is disabled, we enable it and expire those dead but not expired
1014     // servers. This is required so that if meta is assigning to a server which dies after
1015     // assignMeta starts assignment, ServerCrashProcedure can re-assign it. Otherwise, we will be
1016     // stuck here waiting forever if waitForMeta is specified.
1017     if (!isServerCrashProcessingEnabled()) {
1018       setServerCrashProcessingEnabled(true);
1019       this.serverManager.processQueuedDeadServers();
1020     }
1021 
1022     if (waitForMeta) {
1023       metaTableLocator.waitMetaRegionLocation(this.getZooKeeper());
1024       // Above check waits for general meta availability but this does not
1025       // guarantee that the transition has completed
1026       this.assignmentManager.waitForAssignment(HRegionInfo.FIRST_META_REGIONINFO);
1027     }
1028   }
1029 
1030   private void enableMeta(TableName metaTableName) {
1031     if (!this.assignmentManager.getTableStateManager().isTableState(metaTableName,
1032         ZooKeeperProtos.Table.State.ENABLED)) {
1033       this.assignmentManager.setEnabledTable(metaTableName);
1034     }
1035   }
1036 
1037   /**
1038    * This function returns a set of region server names under hbase:meta recovering region ZK node
1039    * @return Set of meta server names which were recorded in ZK
1040    * @throws KeeperException
1041    */
1042   private Set<ServerName> getPreviouselyFailedMetaServersFromZK() throws KeeperException {
1043     Set<ServerName> result = new HashSet<ServerName>();
1044     String metaRecoveringZNode = ZKUtil.joinZNode(zooKeeper.recoveringRegionsZNode,
1045       HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
1046     List<String> regionFailedServers = ZKUtil.listChildrenNoWatch(zooKeeper, metaRecoveringZNode);
1047     if (regionFailedServers == null) return result;
1048 
1049     for(String failedServer : regionFailedServers) {
1050       ServerName server = ServerName.parseServerName(failedServer);
1051       result.add(server);
1052     }
1053     return result;
1054   }
1055 
1056   @Override
1057   public TableDescriptors getTableDescriptors() {
1058     return this.tableDescriptors;
1059   }
1060 
1061   @Override
1062   public ServerManager getServerManager() {
1063     return this.serverManager;
1064   }
1065 
1066   @Override
1067   public MasterFileSystem getMasterFileSystem() {
1068     return this.fileSystemManager;
1069   }
1070 
1071   /*
1072    * Start up all services. If any of these threads gets an unhandled exception
1073    * then they just die with a logged message.  This should be fine because
1074    * in general, we do not expect the master to get such unhandled exceptions
1075    *  as OOMEs; it should be lightly loaded. See what HRegionServer does if
1076    *  need to install an unexpected exception handler.
1077    */
1078   private void startServiceThreads() throws IOException{
1079    // Start the executor service pools
1080    this.service.startExecutorService(ExecutorType.MASTER_OPEN_REGION,
1081       conf.getInt("hbase.master.executor.openregion.threads", 5));
1082    this.service.startExecutorService(ExecutorType.MASTER_CLOSE_REGION,
1083       conf.getInt("hbase.master.executor.closeregion.threads", 5));
1084    this.service.startExecutorService(ExecutorType.MASTER_SERVER_OPERATIONS,
1085       conf.getInt("hbase.master.executor.serverops.threads", 5));
1086    this.service.startExecutorService(ExecutorType.MASTER_META_SERVER_OPERATIONS,
1087       conf.getInt("hbase.master.executor.serverops.threads", 5));
1088    this.service.startExecutorService(ExecutorType.M_LOG_REPLAY_OPS,
1089       conf.getInt("hbase.master.executor.logreplayops.threads", 10));
1090 
1091    // We depend on there being only one instance of this executor running
1092    // at a time.  To do concurrency, would need fencing of enable/disable of
1093    // tables.
1094    // Any time changing this maxThreads to > 1, pls see the comment at
1095    // AccessController#postCreateTableHandler
1096    this.service.startExecutorService(ExecutorType.MASTER_TABLE_OPERATIONS, 1);
1097    startProcedureExecutor();
1098 
1099    // Start log cleaner thread
1100    int cleanerInterval = conf.getInt("hbase.master.cleaner.interval", 60 * 1000);
1101    this.logCleaner =
1102       new LogCleaner(cleanerInterval,
1103          this, conf, getMasterFileSystem().getFileSystem(),
1104          getMasterFileSystem().getOldLogDir());
1105     getChoreService().scheduleChore(logCleaner);
1106 
1107    //start the hfile archive cleaner thread
1108     Path archiveDir = HFileArchiveUtil.getArchivePath(conf);
1109     this.hfileCleaner = new HFileCleaner(cleanerInterval, this, conf, getMasterFileSystem()
1110         .getFileSystem(), archiveDir);
1111     getChoreService().scheduleChore(hfileCleaner);
1112     serviceStarted = true;
1113     if (LOG.isTraceEnabled()) {
1114       LOG.trace("Started service threads");
1115     }
1116   }
1117 
1118   @Override
1119   protected void sendShutdownInterrupt() {
1120     super.sendShutdownInterrupt();
1121     stopProcedureExecutor();
1122   }
1123 
1124   @Override
1125   protected void stopServiceThreads() {
1126     if (masterJettyServer != null) {
1127       LOG.info("Stopping master jetty server");
1128       try {
1129         masterJettyServer.stop();
1130       } catch (Exception e) {
1131         LOG.error("Failed to stop master jetty server", e);
1132       }
1133     }
1134     super.stopServiceThreads();
1135     stopChores();
1136 
1137     // Wait for all the remaining region servers to report in IFF we were
1138     // running a cluster shutdown AND we were NOT aborting.
1139     if (!isAborted() && this.serverManager != null &&
1140         this.serverManager.isClusterShutdown()) {
1141       this.serverManager.letRegionServersShutdown();
1142     }
1143     if (LOG.isDebugEnabled()) {
1144       LOG.debug("Stopping service threads");
1145     }
1146     // Clean up and close up shop
1147     if (this.logCleaner != null) this.logCleaner.cancel(true);
1148     if (this.hfileCleaner != null) this.hfileCleaner.cancel(true);
1149     if (this.quotaManager != null) this.quotaManager.stop();
1150     if (this.activeMasterManager != null) this.activeMasterManager.stop();
1151     if (this.serverManager != null) this.serverManager.stop();
1152     if (this.assignmentManager != null) this.assignmentManager.stop();
1153     if (this.fileSystemManager != null) this.fileSystemManager.stop();
1154     if (this.mpmHost != null) this.mpmHost.stop("server shutting down.");
1155   }
1156 
1157   private void startProcedureExecutor() throws IOException {
1158     final MasterProcedureEnv procEnv = new MasterProcedureEnv(this);
1159     final Path logDir = new Path(fileSystemManager.getRootDir(),
1160         MasterProcedureConstants.MASTER_PROCEDURE_LOGDIR);
1161 
1162     procedureStore = new WALProcedureStore(conf, fileSystemManager.getFileSystem(), logDir,
1163         new MasterProcedureEnv.WALStoreLeaseRecovery(this));
1164     procedureStore.registerListener(new MasterProcedureEnv.MasterProcedureStoreListener(this));
1165     procedureExecutor = new ProcedureExecutor(conf, procEnv, procedureStore,
1166         procEnv.getProcedureQueue());
1167 
1168     final int numThreads = conf.getInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS,
1169         Math.max(Runtime.getRuntime().availableProcessors(),
1170           MasterProcedureConstants.DEFAULT_MIN_MASTER_PROCEDURE_THREADS));
1171     final boolean abortOnCorruption = conf.getBoolean(
1172         MasterProcedureConstants.EXECUTOR_ABORT_ON_CORRUPTION,
1173         MasterProcedureConstants.DEFAULT_EXECUTOR_ABORT_ON_CORRUPTION);
1174     procedureStore.start(numThreads);
1175     procedureExecutor.start(numThreads, abortOnCorruption);
1176   }
1177 
1178   private void stopProcedureExecutor() {
1179     if (procedureExecutor != null) {
1180       procedureExecutor.stop();
1181     }
1182 
1183     if (procedureStore != null) {
1184       procedureStore.stop(isAborted());
1185     }
1186   }
1187 
1188   private void stopChores() {
1189     if (this.expiredMobFileCleanerChore != null) {
1190       this.expiredMobFileCleanerChore.cancel(true);
1191     }
1192     if (this.mobFileCompactChore != null) {
1193       this.mobFileCompactChore.cancel(true);
1194     }
1195     if (this.balancerChore != null) {
1196       this.balancerChore.cancel(true);
1197     }
1198     if (this.normalizerChore != null) {
1199       this.normalizerChore.cancel(true);
1200     }
1201     if (this.clusterStatusChore != null) {
1202       this.clusterStatusChore.cancel(true);
1203     }
1204     if (this.catalogJanitorChore != null) {
1205       this.catalogJanitorChore.cancel(true);
1206     }
1207     if (this.clusterStatusPublisherChore != null){
1208       clusterStatusPublisherChore.cancel(true);
1209     }
1210     if (this.mobFileCompactThread != null) {
1211       this.mobFileCompactThread.close();
1212     }
1213   }
1214 
1215   /**
1216    * @return Get remote side's InetAddress
1217    * @throws UnknownHostException
1218    */
1219   InetAddress getRemoteInetAddress(final int port,
1220       final long serverStartCode) throws UnknownHostException {
1221     // Do it out here in its own little method so can fake an address when
1222     // mocking up in tests.
1223     InetAddress ia = RpcServer.getRemoteIp();
1224 
1225     // The call could be from the local regionserver,
1226     // in which case, there is no remote address.
1227     if (ia == null && serverStartCode == startcode) {
1228       InetSocketAddress isa = rpcServices.getSocketAddress();
1229       if (isa != null && isa.getPort() == port) {
1230         ia = isa.getAddress();
1231       }
1232     }
1233     return ia;
1234   }
1235 
1236   /**
1237    * @return Maximum time we should run balancer for
1238    */
1239   private int getBalancerCutoffTime() {
1240     int balancerCutoffTime =
1241       getConfiguration().getInt("hbase.balancer.max.balancing", -1);
1242     if (balancerCutoffTime == -1) {
1243       // No time period set so create one
1244       int balancerPeriod =
1245         getConfiguration().getInt("hbase.balancer.period", 300000);
1246       balancerCutoffTime = balancerPeriod;
1247       // If nonsense period, set it to balancerPeriod
1248       if (balancerCutoffTime <= 0) balancerCutoffTime = balancerPeriod;
1249     }
1250     return balancerCutoffTime;
1251   }
1252 
1253   public boolean balance() throws IOException {
1254     // if master not initialized, don't run balancer.
1255     if (!isInitialized()) {
1256       LOG.debug("Master has not been initialized, don't run balancer.");
1257       return false;
1258     }
1259     // Do this call outside of synchronized block.
1260     int maximumBalanceTime = getBalancerCutoffTime();
1261     synchronized (this.balancer) {
1262       // If balance not true, don't run balancer.
1263       if (!this.loadBalancerTracker.isBalancerOn()) return false;
1264       // Only allow one balance run at at time.
1265       if (this.assignmentManager.getRegionStates().isRegionsInTransition()) {
1266         Map<String, RegionState> regionsInTransition =
1267           this.assignmentManager.getRegionStates().getRegionsInTransition();
1268         LOG.debug("Not running balancer because " + regionsInTransition.size() +
1269           " region(s) in transition: " + org.apache.commons.lang.StringUtils.
1270             abbreviate(regionsInTransition.toString(), 256));
1271         return false;
1272       }
1273       if (this.serverManager.areDeadServersInProgress()) {
1274         LOG.debug("Not running balancer because processing dead regionserver(s): " +
1275           this.serverManager.getDeadServers());
1276         return false;
1277       }
1278 
1279       if (this.cpHost != null) {
1280         try {
1281           if (this.cpHost.preBalance()) {
1282             LOG.debug("Coprocessor bypassing balancer request");
1283             return false;
1284           }
1285         } catch (IOException ioe) {
1286           LOG.error("Error invoking master coprocessor preBalance()", ioe);
1287           return false;
1288         }
1289       }
1290 
1291       Map<TableName, Map<ServerName, List<HRegionInfo>>> assignmentsByTable =
1292         this.assignmentManager.getRegionStates().getAssignmentsByTable();
1293 
1294       List<RegionPlan> plans = new ArrayList<RegionPlan>();
1295       //Give the balancer the current cluster state.
1296       this.balancer.setClusterStatus(getClusterStatus());
1297       for (Map<ServerName, List<HRegionInfo>> assignments : assignmentsByTable.values()) {
1298         List<RegionPlan> partialPlans = this.balancer.balanceCluster(assignments);
1299         if (partialPlans != null) plans.addAll(partialPlans);
1300       }
1301       long cutoffTime = System.currentTimeMillis() + maximumBalanceTime;
1302       int rpCount = 0;  // number of RegionPlans balanced so far
1303       long totalRegPlanExecTime = 0;
1304       if (plans != null && !plans.isEmpty()) {
1305         for (RegionPlan plan: plans) {
1306           LOG.info("balance " + plan);
1307           long balStartTime = System.currentTimeMillis();
1308           //TODO: bulk assign
1309           this.assignmentManager.balance(plan);
1310           totalRegPlanExecTime += System.currentTimeMillis()-balStartTime;
1311           rpCount++;
1312           if (rpCount < plans.size() &&
1313               // if performing next balance exceeds cutoff time, exit the loop
1314               (System.currentTimeMillis() + (totalRegPlanExecTime / rpCount)) > cutoffTime) {
1315             //TODO: After balance, there should not be a cutoff time (keeping it as a security net for now)
1316             LOG.debug("No more balancing till next balance run; maximumBalanceTime=" +
1317               maximumBalanceTime);
1318             break;
1319           }
1320         }
1321       }
1322       if (this.cpHost != null) {
1323         try {
1324           this.cpHost.postBalance(rpCount < plans.size() ? plans.subList(0, rpCount) : plans);
1325         } catch (IOException ioe) {
1326           // balancing already succeeded so don't change the result
1327           LOG.error("Error invoking master coprocessor postBalance()", ioe);
1328         }
1329       }
1330     }
1331     // If LoadBalancer did not generate any plans, it means the cluster is already balanced.
1332     // Return true indicating a success.
1333     return true;
1334   }
1335 
1336   /**
1337    * Perform normalization of cluster (invoked by {@link RegionNormalizerChore}).
1338    *
1339    * @return true if normalization step was performed successfully, false otherwise
1340    *   (specifically, if HMaster hasn't been initialized properly or normalization
1341    *   is globally disabled)
1342    * @throws IOException
1343    * @throws CoordinatedStateException
1344    */
1345   public boolean normalizeRegions() throws IOException, CoordinatedStateException {
1346     if (!isInitialized()) {
1347       LOG.debug("Master has not been initialized, don't run region normalizer.");
1348       return false;
1349     }
1350 
1351     if (!this.regionNormalizerTracker.isNormalizerOn()) {
1352       LOG.debug("Region normalization is disabled, don't run region normalizer.");
1353       return false;
1354     }
1355 
1356     synchronized (this.normalizer) {
1357       // Don't run the normalizer concurrently
1358       List<TableName> allEnabledTables = new ArrayList<>(
1359         this.assignmentManager.getTableStateManager().getTablesInStates(
1360           ZooKeeperProtos.Table.State.ENABLED));
1361 
1362       Collections.shuffle(allEnabledTables);
1363 
1364       for (TableName table : allEnabledTables) {
1365         if (quotaManager.getNamespaceQuotaManager() != null &&
1366             quotaManager.getNamespaceQuotaManager().getState(table.getNamespaceAsString()) != null){
1367           LOG.debug("Skipping normalizing " + table + " since its namespace has quota");
1368           continue;
1369         }
1370         if (table.isSystemTable() || (getTableDescriptors().get(table) != null &&
1371             !getTableDescriptors().get(table).isNormalizationEnabled())) {
1372           LOG.debug("Skipping normalization for table: " + table + ", as it's either system"
1373               + " table or doesn't have auto normalization turned on");
1374           continue;
1375         }
1376         List<NormalizationPlan> plans = this.normalizer.computePlanForTable(table);
1377         if (plans != null) {
1378           for (NormalizationPlan plan : plans) {
1379             plan.execute(clusterConnection.getAdmin());
1380           }
1381         }
1382       }
1383     }
1384     // If Region did not generate any plans, it means the cluster is already balanced.
1385     // Return true indicating a success.
1386     return true;
1387   }
1388 
1389   /**
1390    * @return Client info for use as prefix on an audit log string; who did an action
1391    */
1392   String getClientIdAuditPrefix() {
1393     return "Client=" + RpcServer.getRequestUserName() + "/" + RpcServer.getRemoteAddress();
1394   }
1395 
1396   /**
1397    * Switch for the background CatalogJanitor thread.
1398    * Used for testing.  The thread will continue to run.  It will just be a noop
1399    * if disabled.
1400    * @param b If false, the catalog janitor won't do anything.
1401    */
1402   public void setCatalogJanitorEnabled(final boolean b) {
1403     this.catalogJanitorChore.setEnabled(b);
1404   }
1405 
1406   @Override
1407   public void dispatchMergingRegions(final HRegionInfo region_a,
1408       final HRegionInfo region_b, final boolean forcible) throws IOException {
1409     checkInitialized();
1410     this.service.submit(new DispatchMergingRegionHandler(this,
1411       this.catalogJanitorChore, region_a, region_b, forcible));
1412   }
1413 
1414   void move(final byte[] encodedRegionName,
1415       final byte[] destServerName) throws HBaseIOException {
1416     RegionState regionState = assignmentManager.getRegionStates().
1417       getRegionState(Bytes.toString(encodedRegionName));
1418     if (regionState == null) {
1419       throw new UnknownRegionException(Bytes.toStringBinary(encodedRegionName));
1420     }
1421 
1422     HRegionInfo hri = regionState.getRegion();
1423     ServerName dest;
1424     if (destServerName == null || destServerName.length == 0) {
1425       LOG.info("Passed destination servername is null/empty so " +
1426         "choosing a server at random");
1427       final List<ServerName> destServers = this.serverManager.createDestinationServersList(
1428         regionState.getServerName());
1429       dest = balancer.randomAssignment(hri, destServers);
1430       if (dest == null) {
1431         LOG.debug("Unable to determine a plan to assign " + hri);
1432         return;
1433       }
1434     } else {
1435       dest = ServerName.valueOf(Bytes.toString(destServerName));
1436       if (dest.equals(serverName) && balancer instanceof BaseLoadBalancer
1437           && !((BaseLoadBalancer)balancer).shouldBeOnMaster(hri)) {
1438         // To avoid unnecessary region moving later by balancer. Don't put user
1439         // regions on master. Regions on master could be put on other region
1440         // server intentionally by test however.
1441         LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1442           + " to avoid unnecessary region moving later by load balancer,"
1443           + " because it should not be on master");
1444         return;
1445       }
1446     }
1447 
1448     if (dest.equals(regionState.getServerName())) {
1449       LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1450         + " because region already assigned to the same server " + dest + ".");
1451       return;
1452     }
1453 
1454     // Now we can do the move
1455     RegionPlan rp = new RegionPlan(hri, regionState.getServerName(), dest);
1456 
1457     try {
1458       checkInitialized();
1459       if (this.cpHost != null) {
1460         if (this.cpHost.preMove(hri, rp.getSource(), rp.getDestination())) {
1461           return;
1462         }
1463       }
1464       // warmup the region on the destination before initiating the move. this call
1465       // is synchronous and takes some time. doing it before the source region gets
1466       // closed
1467       serverManager.sendRegionWarmup(rp.getDestination(), hri);
1468 
1469       LOG.info(getClientIdAuditPrefix() + " move " + rp + ", running balancer");
1470       this.assignmentManager.balance(rp);
1471       if (this.cpHost != null) {
1472         this.cpHost.postMove(hri, rp.getSource(), rp.getDestination());
1473       }
1474     } catch (IOException ioe) {
1475       if (ioe instanceof HBaseIOException) {
1476         throw (HBaseIOException)ioe;
1477       }
1478       throw new HBaseIOException(ioe);
1479     }
1480   }
1481 
1482   @Override
1483   public long createTable(
1484       final HTableDescriptor hTableDescriptor,
1485       final byte [][] splitKeys,
1486       final long nonceGroup,
1487       final long nonce) throws IOException {
1488     if (isStopped()) {
1489       throw new MasterNotRunningException();
1490     }
1491 
1492     String namespace = hTableDescriptor.getTableName().getNamespaceAsString();
1493     ensureNamespaceExists(namespace);
1494 
1495     HRegionInfo[] newRegions = ModifyRegionUtils.createHRegionInfos(hTableDescriptor, splitKeys);
1496     checkInitialized();
1497     sanityCheckTableDescriptor(hTableDescriptor);
1498 
1499     if (cpHost != null) {
1500       cpHost.preCreateTable(hTableDescriptor, newRegions);
1501     }
1502     LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor);
1503 
1504     // TODO: We can handle/merge duplicate requests, and differentiate the case of
1505     //       TableExistsException by saying if the schema is the same or not.
1506     ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
1507     long procId = this.procedureExecutor.submitProcedure(
1508       new CreateTableProcedure(
1509         procedureExecutor.getEnvironment(), hTableDescriptor, newRegions, latch),
1510       nonceGroup,
1511       nonce);
1512     latch.await();
1513 
1514     if (cpHost != null) {
1515       cpHost.postCreateTable(hTableDescriptor, newRegions);
1516     }
1517 
1518     return procId;
1519   }
1520 
1521   /**
1522    * Checks whether the table conforms to some sane limits, and configured
1523    * values (compression, etc) work. Throws an exception if something is wrong.
1524    * @throws IOException
1525    */
1526   private void sanityCheckTableDescriptor(final HTableDescriptor htd) throws IOException {
1527     final String CONF_KEY = "hbase.table.sanity.checks";
1528     boolean logWarn = false;
1529     if (!conf.getBoolean(CONF_KEY, true)) {
1530       logWarn = true;
1531     }
1532     String tableVal = htd.getConfigurationValue(CONF_KEY);
1533     if (tableVal != null && !Boolean.valueOf(tableVal)) {
1534       logWarn = true;
1535     }
1536 
1537     // check max file size
1538     long maxFileSizeLowerLimit = 2 * 1024 * 1024L; // 2M is the default lower limit
1539     long maxFileSize = htd.getMaxFileSize();
1540     if (maxFileSize < 0) {
1541       maxFileSize = conf.getLong(HConstants.HREGION_MAX_FILESIZE, maxFileSizeLowerLimit);
1542     }
1543     if (maxFileSize < conf.getLong("hbase.hregion.max.filesize.limit", maxFileSizeLowerLimit)) {
1544       String message = "MAX_FILESIZE for table descriptor or "
1545           + "\"hbase.hregion.max.filesize\" (" + maxFileSize
1546           + ") is too small, which might cause over splitting into unmanageable "
1547           + "number of regions.";
1548       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1549     }
1550 
1551     // check flush size
1552     long flushSizeLowerLimit = 1024 * 1024L; // 1M is the default lower limit
1553     long flushSize = htd.getMemStoreFlushSize();
1554     if (flushSize < 0) {
1555       flushSize = conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, flushSizeLowerLimit);
1556     }
1557     if (flushSize < conf.getLong("hbase.hregion.memstore.flush.size.limit", flushSizeLowerLimit)) {
1558       String message = "MEMSTORE_FLUSHSIZE for table descriptor or "
1559           + "\"hbase.hregion.memstore.flush.size\" ("+flushSize+") is too small, which might cause"
1560           + " very frequent flushing.";
1561       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1562     }
1563 
1564     // check that coprocessors and other specified plugin classes can be loaded
1565     try {
1566       checkClassLoading(conf, htd);
1567     } catch (Exception ex) {
1568       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, ex.getMessage(), null);
1569     }
1570 
1571     // check compression can be loaded
1572     try {
1573       checkCompression(htd);
1574     } catch (IOException e) {
1575       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
1576     }
1577 
1578     // check encryption can be loaded
1579     try {
1580       checkEncryption(conf, htd);
1581     } catch (IOException e) {
1582       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
1583     }
1584     // Verify compaction policy
1585     try{
1586       checkCompactionPolicy(conf, htd);
1587     } catch(IOException e){
1588       warnOrThrowExceptionForFailure(false, CONF_KEY, e.getMessage(), e);
1589     }
1590     // check that we have at least 1 CF
1591     if (htd.getColumnFamilies().length == 0) {
1592       String message = "Table should have at least one column family.";
1593       warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1594     }
1595 
1596     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1597       if (hcd.getTimeToLive() <= 0) {
1598         String message = "TTL for column family " + hcd.getNameAsString() + " must be positive.";
1599         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1600       }
1601 
1602       // check blockSize
1603       if (hcd.getBlocksize() < 1024 || hcd.getBlocksize() > 16 * 1024 * 1024) {
1604         String message = "Block size for column family " + hcd.getNameAsString()
1605             + "  must be between 1K and 16MB.";
1606         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1607       }
1608 
1609       // check versions
1610       if (hcd.getMinVersions() < 0) {
1611         String message = "Min versions for column family " + hcd.getNameAsString()
1612           + "  must be positive.";
1613         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1614       }
1615       // max versions already being checked
1616 
1617       // HBASE-13776 Setting illegal versions for HColumnDescriptor
1618       //  does not throw IllegalArgumentException
1619       // check minVersions <= maxVerions
1620       if (hcd.getMinVersions() > hcd.getMaxVersions()) {
1621         String message = "Min versions for column family " + hcd.getNameAsString()
1622             + " must be less than the Max versions.";
1623         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1624       }
1625 
1626       // check replication scope
1627       if (hcd.getScope() < 0) {
1628         String message = "Replication scope for column family "
1629           + hcd.getNameAsString() + "  must be positive.";
1630         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1631       }
1632 
1633       // check data replication factor, it can be 0(default value) when user has not explicitly
1634       // set the value, in this case we use default replication factor set in the file system.
1635       if (hcd.getDFSReplication() < 0) {
1636         String message = "HFile Replication for column family " + hcd.getNameAsString()
1637             + "  must be greater than zero.";
1638         warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
1639       }
1640 
1641       // TODO: should we check coprocessors and encryption ?
1642     }
1643   }
1644 
1645   private void checkCompactionPolicy(Configuration conf, HTableDescriptor htd)
1646       throws IOException {
1647     // FIFO compaction has some requirements
1648     // Actually FCP ignores periodic major compactions
1649     String className =
1650         htd.getConfigurationValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
1651     if (className == null) {
1652       className =
1653           conf.get(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY,
1654             ExploringCompactionPolicy.class.getName());
1655     }
1656 
1657     int blockingFileCount = HStore.DEFAULT_BLOCKING_STOREFILE_COUNT;
1658     String sv = htd.getConfigurationValue(HStore.BLOCKING_STOREFILES_KEY);
1659     if (sv != null) {
1660       blockingFileCount = Integer.parseInt(sv);
1661     } else {
1662       blockingFileCount = conf.getInt(HStore.BLOCKING_STOREFILES_KEY, blockingFileCount);
1663     }
1664 
1665     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1666       String compactionPolicy =
1667           hcd.getConfigurationValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
1668       if (compactionPolicy == null) {
1669         compactionPolicy = className;
1670       }
1671       if (!compactionPolicy.equals(FIFOCompactionPolicy.class.getName())) {
1672         continue;
1673       }
1674       // FIFOCompaction
1675       String message = null;
1676 
1677       // 1. Check TTL
1678       if (hcd.getTimeToLive() == HColumnDescriptor.DEFAULT_TTL) {
1679         message = "Default TTL is not supported for FIFO compaction";
1680         throw new IOException(message);
1681       }
1682 
1683       // 2. Check min versions
1684       if (hcd.getMinVersions() > 0) {
1685         message = "MIN_VERSION > 0 is not supported for FIFO compaction";
1686         throw new IOException(message);
1687       }
1688 
1689       // 3. blocking file count
1690       String sbfc = htd.getConfigurationValue(HStore.BLOCKING_STOREFILES_KEY);
1691       if (sbfc != null) {
1692         blockingFileCount = Integer.parseInt(sbfc);
1693       }
1694       if (blockingFileCount < 1000) {
1695         message =
1696             "blocking file count '" + HStore.BLOCKING_STOREFILES_KEY + "' " + blockingFileCount
1697                 + " is below recommended minimum of 1000";
1698         throw new IOException(message);
1699       }
1700     }
1701   }
1702 
1703   // HBASE-13350 - Helper method to log warning on sanity check failures if checks disabled.
1704   private static void warnOrThrowExceptionForFailure(boolean logWarn, String confKey,
1705       String message, Exception cause) throws IOException {
1706     if (!logWarn) {
1707       throw new DoNotRetryIOException(message + " Set " + confKey +
1708           " to false at conf or table descriptor if you want to bypass sanity checks", cause);
1709     }
1710     LOG.warn(message);
1711   }
1712 
1713   private void startActiveMasterManager(int infoPort) throws KeeperException {
1714     String backupZNode = ZKUtil.joinZNode(
1715       zooKeeper.backupMasterAddressesZNode, serverName.toString());
1716     /*
1717     * Add a ZNode for ourselves in the backup master directory since we
1718     * may not become the active master. If so, we want the actual active
1719     * master to know we are backup masters, so that it won't assign
1720     * regions to us if so configured.
1721     *
1722     * If we become the active master later, ActiveMasterManager will delete
1723     * this node explicitly.  If we crash before then, ZooKeeper will delete
1724     * this node for us since it is ephemeral.
1725     */
1726     LOG.info("Adding backup master ZNode " + backupZNode);
1727     if (!MasterAddressTracker.setMasterAddress(zooKeeper, backupZNode,
1728         serverName, infoPort)) {
1729       LOG.warn("Failed create of " + backupZNode + " by " + serverName);
1730     }
1731 
1732     activeMasterManager.setInfoPort(infoPort);
1733     // Start a thread to try to become the active master, so we won't block here
1734     Threads.setDaemonThreadRunning(new Thread(new Runnable() {
1735       @Override
1736       public void run() {
1737         int timeout = conf.getInt(HConstants.ZK_SESSION_TIMEOUT,
1738           HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
1739         // If we're a backup master, stall until a primary to writes his address
1740         if (conf.getBoolean(HConstants.MASTER_TYPE_BACKUP,
1741           HConstants.DEFAULT_MASTER_TYPE_BACKUP)) {
1742           LOG.debug("HMaster started in backup mode. "
1743             + "Stalling until master znode is written.");
1744           // This will only be a minute or so while the cluster starts up,
1745           // so don't worry about setting watches on the parent znode
1746           while (!activeMasterManager.hasActiveMaster()) {
1747             LOG.debug("Waiting for master address ZNode to be written "
1748               + "(Also watching cluster state node)");
1749             Threads.sleep(timeout);
1750           }
1751         }
1752         MonitoredTask status = TaskMonitor.get().createStatus("Master startup");
1753         status.setDescription("Master startup");
1754         try {
1755           if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) {
1756             finishActiveMasterInitialization(status);
1757           }
1758         } catch (Throwable t) {
1759           status.setStatus("Failed to become active: " + t.getMessage());
1760           LOG.fatal("Failed to become active master", t);
1761           // HBASE-5680: Likely hadoop23 vs hadoop 20.x/1.x incompatibility
1762           if (t instanceof NoClassDefFoundError &&
1763             t.getMessage()
1764               .contains("org/apache/hadoop/hdfs/protocol/HdfsConstants$SafeModeAction")) {
1765             // improved error message for this special case
1766             abort("HBase is having a problem with its Hadoop jars.  You may need to "
1767               + "recompile HBase against Hadoop version "
1768               + org.apache.hadoop.util.VersionInfo.getVersion()
1769               + " or change your hadoop jars to start properly", t);
1770           } else {
1771             abort("Unhandled exception. Starting shutdown.", t);
1772           }
1773         } finally {
1774           status.cleanup();
1775         }
1776       }
1777     }, getServerName().toShortString() + ".activeMasterManager"));
1778   }
1779 
1780   private void checkCompression(final HTableDescriptor htd)
1781   throws IOException {
1782     if (!this.masterCheckCompression) return;
1783     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1784       checkCompression(hcd);
1785     }
1786   }
1787 
1788   private void checkCompression(final HColumnDescriptor hcd)
1789   throws IOException {
1790     if (!this.masterCheckCompression) return;
1791     CompressionTest.testCompression(hcd.getCompression());
1792     CompressionTest.testCompression(hcd.getCompactionCompression());
1793   }
1794 
1795   private void checkEncryption(final Configuration conf, final HTableDescriptor htd)
1796   throws IOException {
1797     if (!this.masterCheckEncryption) return;
1798     for (HColumnDescriptor hcd : htd.getColumnFamilies()) {
1799       checkEncryption(conf, hcd);
1800     }
1801   }
1802 
1803   private void checkEncryption(final Configuration conf, final HColumnDescriptor hcd)
1804   throws IOException {
1805     if (!this.masterCheckEncryption) return;
1806     EncryptionTest.testEncryption(conf, hcd.getEncryptionType(), hcd.getEncryptionKey());
1807   }
1808 
1809   private void checkClassLoading(final Configuration conf, final HTableDescriptor htd)
1810   throws IOException {
1811     RegionSplitPolicy.getSplitPolicyClass(htd, conf);
1812     RegionCoprocessorHost.testTableCoprocessorAttrs(conf, htd);
1813   }
1814 
1815   private static boolean isCatalogTable(final TableName tableName) {
1816     return tableName.equals(TableName.META_TABLE_NAME);
1817   }
1818 
1819   @Override
1820   public long deleteTable(
1821       final TableName tableName,
1822       final long nonceGroup,
1823       final long nonce) throws IOException {
1824     checkInitialized();
1825     if (cpHost != null) {
1826       cpHost.preDeleteTable(tableName);
1827     }
1828     LOG.info(getClientIdAuditPrefix() + " delete " + tableName);
1829 
1830     // TODO: We can handle/merge duplicate request
1831     ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
1832     long procId = this.procedureExecutor.submitProcedure(
1833       new DeleteTableProcedure(procedureExecutor.getEnvironment(), tableName, latch),
1834       nonceGroup,
1835       nonce);
1836     latch.await();
1837 
1838     if (cpHost != null) {
1839       cpHost.postDeleteTable(tableName);
1840     }
1841 
1842     return procId;
1843   }
1844 
1845   @Override
1846   public void truncateTable(
1847       final TableName tableName,
1848       final boolean preserveSplits,
1849       final long nonceGroup,
1850       final long nonce) throws IOException {
1851     checkInitialized();
1852     if (cpHost != null) {
1853       cpHost.preTruncateTable(tableName);
1854     }
1855     LOG.info(getClientIdAuditPrefix() + " truncate " + tableName);
1856 
1857     long procId = this.procedureExecutor.submitProcedure(
1858       new TruncateTableProcedure(procedureExecutor.getEnvironment(), tableName, preserveSplits),
1859       nonceGroup,
1860       nonce);
1861     ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
1862 
1863     if (cpHost != null) {
1864       cpHost.postTruncateTable(tableName);
1865     }
1866   }
1867 
1868   @Override
1869   public void addColumn(
1870       final TableName tableName,
1871       final HColumnDescriptor columnDescriptor,
1872       final long nonceGroup,
1873       final long nonce)
1874       throws IOException {
1875     checkInitialized();
1876     checkCompression(columnDescriptor);
1877     checkEncryption(conf, columnDescriptor);
1878     if (cpHost != null) {
1879       if (cpHost.preAddColumn(tableName, columnDescriptor)) {
1880         return;
1881       }
1882     }
1883     // Execute the operation synchronously - wait for the operation to complete before continuing.
1884     long procId = this.procedureExecutor.submitProcedure(
1885       new AddColumnFamilyProcedure(procedureExecutor.getEnvironment(), tableName, columnDescriptor),
1886       nonceGroup,
1887       nonce);
1888     ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
1889     if (cpHost != null) {
1890       cpHost.postAddColumn(tableName, columnDescriptor);
1891     }
1892   }
1893 
1894   @Override
1895   public void modifyColumn(
1896       final TableName tableName,
1897       final HColumnDescriptor descriptor,
1898       final long nonceGroup,
1899       final long nonce)
1900       throws IOException {
1901     checkInitialized();
1902     checkCompression(descriptor);
1903     checkEncryption(conf, descriptor);
1904     if (cpHost != null) {
1905       if (cpHost.preModifyColumn(tableName, descriptor)) {
1906         return;
1907       }
1908     }
1909     LOG.info(getClientIdAuditPrefix() + " modify " + descriptor);
1910 
1911     // Execute the operation synchronously - wait for the operation to complete before continuing.
1912     long procId = this.procedureExecutor.submitProcedure(
1913       new ModifyColumnFamilyProcedure(procedureExecutor.getEnvironment(), tableName, descriptor),
1914       nonceGroup,
1915       nonce);
1916     ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
1917 
1918     if (cpHost != null) {
1919       cpHost.postModifyColumn(tableName, descriptor);
1920     }
1921   }
1922 
1923   @Override
1924   public void deleteColumn(
1925       final TableName tableName,
1926       final byte[] columnName,
1927       final long nonceGroup,
1928       final long nonce)
1929       throws IOException {
1930     checkInitialized();
1931     if (cpHost != null) {
1932       if (cpHost.preDeleteColumn(tableName, columnName)) {
1933         return;
1934       }
1935     }
1936     LOG.info(getClientIdAuditPrefix() + " delete " + Bytes.toString(columnName));
1937 
1938     // Execute the operation synchronously - wait for the operation to complete before continuing.
1939     long procId = this.procedureExecutor.submitProcedure(
1940       new DeleteColumnFamilyProcedure(procedureExecutor.getEnvironment(), tableName, columnName),
1941       nonceGroup,
1942       nonce);
1943     ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
1944 
1945     if (cpHost != null) {
1946       cpHost.postDeleteColumn(tableName, columnName);
1947     }
1948   }
1949 
1950   @Override
1951   public long enableTable(
1952       final TableName tableName,
1953       final long nonceGroup,
1954       final long nonce) throws IOException {
1955     checkInitialized();
1956     if (cpHost != null) {
1957       cpHost.preEnableTable(tableName);
1958     }
1959     LOG.info(getClientIdAuditPrefix() + " enable " + tableName);
1960 
1961     // Execute the operation asynchronously - client will check the progress of the operation
1962     final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createLatch();
1963     long procId = this.procedureExecutor.submitProcedure(
1964       new EnableTableProcedure(procedureExecutor.getEnvironment(), tableName, false, prepareLatch),
1965       nonceGroup,
1966       nonce);
1967     // Before returning to client, we want to make sure that the table is prepared to be
1968     // enabled (the table is locked and the table state is set).
1969     //
1970     // Note: if the procedure throws exception, we will catch it and rethrow.
1971     prepareLatch.await();
1972 
1973     if (cpHost != null) {
1974       cpHost.postEnableTable(tableName);
1975     }
1976 
1977     return procId;
1978   }
1979 
1980   @Override
1981   public long disableTable(
1982       final TableName tableName,
1983       final long nonceGroup,
1984       final long nonce) throws IOException {
1985     checkInitialized();
1986     if (cpHost != null) {
1987       cpHost.preDisableTable(tableName);
1988     }
1989     LOG.info(getClientIdAuditPrefix() + " disable " + tableName);
1990 
1991     // Execute the operation asynchronously - client will check the progress of the operation
1992     final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createLatch();
1993     // Execute the operation asynchronously - client will check the progress of the operation
1994     long procId = this.procedureExecutor.submitProcedure(
1995       new DisableTableProcedure(procedureExecutor.getEnvironment(), tableName, false, prepareLatch),
1996       nonceGroup,
1997       nonce);
1998     // Before returning to client, we want to make sure that the table is prepared to be
1999     // enabled (the table is locked and the table state is set).
2000     //
2001     // Note: if the procedure throws exception, we will catch it and rethrow.
2002     prepareLatch.await();
2003 
2004     if (cpHost != null) {
2005       cpHost.postDisableTable(tableName);
2006     }
2007 
2008     return procId;
2009   }
2010 
2011   /**
2012    * Return the region and current deployment for the region containing
2013    * the given row. If the region cannot be found, returns null. If it
2014    * is found, but not currently deployed, the second element of the pair
2015    * may be null.
2016    */
2017   @VisibleForTesting // Used by TestMaster.
2018   Pair<HRegionInfo, ServerName> getTableRegionForRow(
2019       final TableName tableName, final byte [] rowKey)
2020   throws IOException {
2021     final AtomicReference<Pair<HRegionInfo, ServerName>> result =
2022       new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
2023 
2024     MetaScannerVisitor visitor =
2025       new MetaScannerVisitorBase() {
2026         @Override
2027         public boolean processRow(Result data) throws IOException {
2028           if (data == null || data.size() <= 0) {
2029             return true;
2030           }
2031           Pair<HRegionInfo, ServerName> pair = HRegionInfo.getHRegionInfoAndServerName(data);
2032           if (pair == null) {
2033             return false;
2034           }
2035           if (!pair.getFirst().getTable().equals(tableName)) {
2036             return false;
2037           }
2038           result.set(pair);
2039           return true;
2040         }
2041     };
2042 
2043     MetaScanner.metaScan(clusterConnection, visitor, tableName, rowKey, 1);
2044     return result.get();
2045   }
2046 
2047   @Override
2048   public void modifyTable(
2049       final TableName tableName,
2050       final HTableDescriptor descriptor,
2051       final long nonceGroup,
2052       final long nonce)
2053       throws IOException {
2054     checkInitialized();
2055     sanityCheckTableDescriptor(descriptor);
2056     if (cpHost != null) {
2057       cpHost.preModifyTable(tableName, descriptor);
2058     }
2059 
2060     LOG.info(getClientIdAuditPrefix() + " modify " + tableName);
2061 
2062     // Execute the operation synchronously - wait for the operation completes before continuing.
2063     long procId = this.procedureExecutor.submitProcedure(
2064       new ModifyTableProcedure(procedureExecutor.getEnvironment(), descriptor),
2065       nonceGroup,
2066       nonce);
2067 
2068     ProcedureSyncWait.waitForProcedureToComplete(procedureExecutor, procId);
2069 
2070     if (cpHost != null) {
2071       cpHost.postModifyTable(tableName, descriptor);
2072     }
2073   }
2074 
2075   @Override
2076   public void checkTableModifiable(final TableName tableName)
2077       throws IOException, TableNotFoundException, TableNotDisabledException {
2078     if (isCatalogTable(tableName)) {
2079       throw new IOException("Can't modify catalog tables");
2080     }
2081     if (!MetaTableAccessor.tableExists(getConnection(), tableName)) {
2082       throw new TableNotFoundException(tableName);
2083     }
2084     if (!getAssignmentManager().getTableStateManager().
2085         isTableState(tableName, ZooKeeperProtos.Table.State.DISABLED)) {
2086       throw new TableNotDisabledException(tableName);
2087     }
2088   }
2089 
2090   /**
2091    * @return cluster status
2092    */
2093   public ClusterStatus getClusterStatus() throws InterruptedIOException {
2094     // Build Set of backup masters from ZK nodes
2095     List<String> backupMasterStrings;
2096     try {
2097       backupMasterStrings = ZKUtil.listChildrenNoWatch(this.zooKeeper,
2098         this.zooKeeper.backupMasterAddressesZNode);
2099     } catch (KeeperException e) {
2100       LOG.warn(this.zooKeeper.prefix("Unable to list backup servers"), e);
2101       backupMasterStrings = null;
2102     }
2103 
2104     List<ServerName> backupMasters = null;
2105     if (backupMasterStrings != null && !backupMasterStrings.isEmpty()) {
2106       backupMasters = new ArrayList<ServerName>(backupMasterStrings.size());
2107       for (String s: backupMasterStrings) {
2108         try {
2109           byte [] bytes;
2110           try {
2111             bytes = ZKUtil.getData(this.zooKeeper, ZKUtil.joinZNode(
2112                 this.zooKeeper.backupMasterAddressesZNode, s));
2113           } catch (InterruptedException e) {
2114             throw new InterruptedIOException();
2115           }
2116           if (bytes != null) {
2117             ServerName sn;
2118             try {
2119               sn = ServerName.parseFrom(bytes);
2120             } catch (DeserializationException e) {
2121               LOG.warn("Failed parse, skipping registering backup server", e);
2122               continue;
2123             }
2124             backupMasters.add(sn);
2125           }
2126         } catch (KeeperException e) {
2127           LOG.warn(this.zooKeeper.prefix("Unable to get information about " +
2128                    "backup servers"), e);
2129         }
2130       }
2131       Collections.sort(backupMasters, new Comparator<ServerName>() {
2132         @Override
2133         public int compare(ServerName s1, ServerName s2) {
2134           return s1.getServerName().compareTo(s2.getServerName());
2135         }});
2136     }
2137 
2138     String clusterId = fileSystemManager != null ?
2139       fileSystemManager.getClusterId().toString() : null;
2140     Map<String, RegionState> regionsInTransition = assignmentManager != null ?
2141       assignmentManager.getRegionStates().getRegionsInTransition() : null;
2142     String[] coprocessors = cpHost != null ? getMasterCoprocessors() : null;
2143     boolean balancerOn = loadBalancerTracker != null ?
2144       loadBalancerTracker.isBalancerOn() : false;
2145     Map<ServerName, ServerLoad> onlineServers = null;
2146     Set<ServerName> deadServers = null;
2147     if (serverManager != null) {
2148       deadServers = serverManager.getDeadServers().copyServerNames();
2149       onlineServers = serverManager.getOnlineServers();
2150     }
2151     return new ClusterStatus(VersionInfo.getVersion(), clusterId,
2152       onlineServers, deadServers, serverName, backupMasters,
2153       regionsInTransition, coprocessors, balancerOn);
2154   }
2155 
2156   /**
2157    * The set of loaded coprocessors is stored in a static set. Since it's
2158    * statically allocated, it does not require that HMaster's cpHost be
2159    * initialized prior to accessing it.
2160    * @return a String representation of the set of names of the loaded
2161    * coprocessors.
2162    */
2163   public static String getLoadedCoprocessors() {
2164     return CoprocessorHost.getLoadedCoprocessors().toString();
2165   }
2166 
2167   /**
2168    * @return timestamp in millis when HMaster was started.
2169    */
2170   public long getMasterStartTime() {
2171     return startcode;
2172   }
2173 
2174   /**
2175    * @return timestamp in millis when HMaster became the active master.
2176    */
2177   public long getMasterActiveTime() {
2178     return masterActiveTime;
2179   }
2180 
2181   public int getNumWALFiles() {
2182     return procedureStore != null ? procedureStore.getActiveLogs().size() : 0;
2183   }
2184 
2185   public WALProcedureStore getWalProcedureStore() {
2186     return procedureStore;
2187   }
2188 
2189   public int getRegionServerInfoPort(final ServerName sn) {
2190     RegionServerInfo info = this.regionServerTracker.getRegionServerInfo(sn);
2191     if (info == null || info.getInfoPort() == 0) {
2192       return conf.getInt(HConstants.REGIONSERVER_INFO_PORT,
2193         HConstants.DEFAULT_REGIONSERVER_INFOPORT);
2194     }
2195     return info.getInfoPort();
2196   }
2197 
2198   public String getRegionServerVersion(final ServerName sn) {
2199     RegionServerInfo info = this.regionServerTracker.getRegionServerInfo(sn);
2200     if (info != null && info.hasVersionInfo()) {
2201       return info.getVersionInfo().getVersion();
2202     }
2203     return "Unknown";
2204   }
2205 
2206   /**
2207    * @return array of coprocessor SimpleNames.
2208    */
2209   public String[] getMasterCoprocessors() {
2210     Set<String> masterCoprocessors = getMasterCoprocessorHost().getCoprocessors();
2211     return masterCoprocessors.toArray(new String[masterCoprocessors.size()]);
2212   }
2213 
2214   @Override
2215   public void abort(final String msg, final Throwable t) {
2216     if (isAborted() || isStopped()) {
2217       return;
2218     }
2219     if (cpHost != null) {
2220       // HBASE-4014: dump a list of loaded coprocessors.
2221       LOG.fatal("Master server abort: loaded coprocessors are: " +
2222           getLoadedCoprocessors());
2223     }
2224     if (t != null) LOG.fatal(msg, t);
2225     stop(msg);
2226   }
2227 
2228   @Override
2229   public ZooKeeperWatcher getZooKeeper() {
2230     return zooKeeper;
2231   }
2232 
2233   @Override
2234   public MasterCoprocessorHost getMasterCoprocessorHost() {
2235     return cpHost;
2236   }
2237 
2238   @Override
2239   public MasterQuotaManager getMasterQuotaManager() {
2240     return quotaManager;
2241   }
2242 
2243   @Override
2244   public ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
2245     return procedureExecutor;
2246   }
2247 
2248   @Override
2249   public ServerName getServerName() {
2250     return this.serverName;
2251   }
2252 
2253   @Override
2254   public AssignmentManager getAssignmentManager() {
2255     return this.assignmentManager;
2256   }
2257 
2258   public MemoryBoundedLogMessageBuffer getRegionServerFatalLogBuffer() {
2259     return rsFatals;
2260   }
2261 
2262   public void shutdown() {
2263     if (cpHost != null) {
2264       try {
2265         cpHost.preShutdown();
2266       } catch (IOException ioe) {
2267         LOG.error("Error call master coprocessor preShutdown()", ioe);
2268       }
2269     }
2270 
2271     if (this.serverManager != null) {
2272       this.serverManager.shutdownCluster();
2273     }
2274     if (this.clusterStatusTracker != null){
2275       try {
2276         this.clusterStatusTracker.setClusterDown();
2277       } catch (KeeperException e) {
2278         LOG.error("ZooKeeper exception trying to set cluster as down in ZK", e);
2279       }
2280     }
2281   }
2282 
2283   public void stopMaster() {
2284     if (cpHost != null) {
2285       try {
2286         cpHost.preStopMaster();
2287       } catch (IOException ioe) {
2288         LOG.error("Error call master coprocessor preStopMaster()", ioe);
2289       }
2290     }
2291     stop("Stopped by " + Thread.currentThread().getName());
2292   }
2293 
2294   void checkServiceStarted() throws ServerNotRunningYetException {
2295     if (!serviceStarted) {
2296       throw new ServerNotRunningYetException("Server is not running yet");
2297     }
2298   }
2299 
2300   void checkInitialized() throws PleaseHoldException, ServerNotRunningYetException {
2301     checkServiceStarted();
2302     if (!isInitialized()) {
2303       throw new PleaseHoldException("Master is initializing");
2304     }
2305   }
2306 
2307   void checkNamespaceManagerReady() throws IOException {
2308     checkInitialized();
2309     if (tableNamespaceManager == null ||
2310         !tableNamespaceManager.isTableAvailableAndInitialized()) {
2311       throw new IOException("Table Namespace Manager not ready yet, try again later");
2312     }
2313   }
2314   /**
2315    * Report whether this master is currently the active master or not.
2316    * If not active master, we are parked on ZK waiting to become active.
2317    *
2318    * This method is used for testing.
2319    *
2320    * @return true if active master, false if not.
2321    */
2322   public boolean isActiveMaster() {
2323     return isActiveMaster;
2324   }
2325 
2326   /**
2327    * Report whether this master has completed with its initialization and is
2328    * ready.  If ready, the master is also the active master.  A standby master
2329    * is never ready.
2330    *
2331    * This method is used for testing.
2332    *
2333    * @return true if master is ready to go, false if not.
2334    */
2335   @Override
2336   public boolean isInitialized() {
2337     return initialized.isReady();
2338   }
2339 
2340   @VisibleForTesting
2341   public void setInitialized(boolean isInitialized) {
2342     procedureExecutor.getEnvironment().setEventReady(initialized, isInitialized);
2343   }
2344 
2345   public ProcedureEvent getInitializedEvent() {
2346     return initialized;
2347   }
2348 
2349   /**
2350    * ServerCrashProcessingEnabled is set false before completing assignMeta to prevent processing
2351    * of crashed servers.
2352    * @return true if assignMeta has completed;
2353    */
2354   @Override
2355   public boolean isServerCrashProcessingEnabled() {
2356     return serverCrashProcessingEnabled.isReady();
2357   }
2358 
2359   @VisibleForTesting
2360   public void setServerCrashProcessingEnabled(final boolean b) {
2361     procedureExecutor.getEnvironment().setEventReady(serverCrashProcessingEnabled, b);
2362   }
2363 
2364   public ProcedureEvent getServerCrashProcessingEnabledEvent() {
2365     return serverCrashProcessingEnabled;
2366   }
2367 
2368   /**
2369    * Report whether this master has started initialization and is about to do meta region assignment
2370    * @return true if master is in initialization &amp; about to assign hbase:meta regions
2371    */
2372   public boolean isInitializationStartsMetaRegionAssignment() {
2373     return this.initializationBeforeMetaAssignment;
2374   }
2375 
2376   public void assignRegion(HRegionInfo hri) {
2377     assignmentManager.assign(hri, true);
2378   }
2379 
2380   /**
2381    * Compute the average load across all region servers.
2382    * Currently, this uses a very naive computation - just uses the number of
2383    * regions being served, ignoring stats about number of requests.
2384    * @return the average load
2385    */
2386   public double getAverageLoad() {
2387     if (this.assignmentManager == null) {
2388       return 0;
2389     }
2390 
2391     RegionStates regionStates = this.assignmentManager.getRegionStates();
2392     if (regionStates == null) {
2393       return 0;
2394     }
2395     return regionStates.getAverageLoad();
2396   }
2397 
2398   @Override
2399   public boolean registerService(Service instance) {
2400     /*
2401      * No stacking of instances is allowed for a single service name
2402      */
2403     Descriptors.ServiceDescriptor serviceDesc = instance.getDescriptorForType();
2404     if (coprocessorServiceHandlers.containsKey(serviceDesc.getFullName())) {
2405       LOG.error("Coprocessor service "+serviceDesc.getFullName()+
2406           " already registered, rejecting request from "+instance
2407       );
2408       return false;
2409     }
2410 
2411     coprocessorServiceHandlers.put(serviceDesc.getFullName(), instance);
2412     if (LOG.isDebugEnabled()) {
2413       LOG.debug("Registered master coprocessor service: service="+serviceDesc.getFullName());
2414     }
2415     return true;
2416   }
2417 
2418   /**
2419    * Utility for constructing an instance of the passed HMaster class.
2420    * @param masterClass
2421    * @param conf
2422    * @return HMaster instance.
2423    */
2424   public static HMaster constructMaster(Class<? extends HMaster> masterClass,
2425       final Configuration conf, final CoordinatedStateManager cp)  {
2426     try {
2427       Constructor<? extends HMaster> c =
2428         masterClass.getConstructor(Configuration.class, CoordinatedStateManager.class);
2429       return c.newInstance(conf, cp);
2430     } catch(Exception e) {
2431       Throwable error = e;
2432       if (e instanceof InvocationTargetException &&
2433           ((InvocationTargetException)e).getTargetException() != null) {
2434         error = ((InvocationTargetException)e).getTargetException();
2435       }
2436       throw new RuntimeException("Failed construction of Master: " + masterClass.toString() + ". "
2437         , error);
2438     }
2439   }
2440 
2441   /**
2442    * @see org.apache.hadoop.hbase.master.HMasterCommandLine
2443    */
2444   public static void main(String [] args) {
2445     VersionInfo.logVersion();
2446     new HMasterCommandLine(HMaster.class).doMain(args);
2447   }
2448 
2449   public HFileCleaner getHFileCleaner() {
2450     return this.hfileCleaner;
2451   }
2452 
2453   /**
2454    * Exposed for TESTING!
2455    * @return the underlying snapshot manager
2456    */
2457   public SnapshotManager getSnapshotManagerForTesting() {
2458     return this.snapshotManager;
2459   }
2460 
2461   @Override
2462   public void createNamespace(NamespaceDescriptor descriptor) throws IOException {
2463     TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
2464     checkNamespaceManagerReady();
2465     if (cpHost != null) {
2466       if (cpHost.preCreateNamespace(descriptor)) {
2467         return;
2468       }
2469     }
2470     LOG.info(getClientIdAuditPrefix() + " creating " + descriptor);
2471     tableNamespaceManager.create(descriptor);
2472     if (cpHost != null) {
2473       cpHost.postCreateNamespace(descriptor);
2474     }
2475   }
2476 
2477   @Override
2478   public void modifyNamespace(NamespaceDescriptor descriptor) throws IOException {
2479     TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
2480     checkNamespaceManagerReady();
2481     if (cpHost != null) {
2482       if (cpHost.preModifyNamespace(descriptor)) {
2483         return;
2484       }
2485     }
2486     LOG.info(getClientIdAuditPrefix() + " modify " + descriptor);
2487     tableNamespaceManager.update(descriptor);
2488     if (cpHost != null) {
2489       cpHost.postModifyNamespace(descriptor);
2490     }
2491   }
2492 
2493   @Override
2494   public void deleteNamespace(String name) throws IOException {
2495     checkNamespaceManagerReady();
2496     if (cpHost != null) {
2497       if (cpHost.preDeleteNamespace(name)) {
2498         return;
2499       }
2500     }
2501     LOG.info(getClientIdAuditPrefix() + " delete " + name);
2502     tableNamespaceManager.remove(name);
2503     if (cpHost != null) {
2504       cpHost.postDeleteNamespace(name);
2505     }
2506   }
2507 
2508   /**
2509    * Ensure that the specified namespace exists, otherwise throws a NamespaceNotFoundException
2510    *
2511    * @param name the namespace to check
2512    * @throws IOException if the namespace manager is not ready yet.
2513    * @throws NamespaceNotFoundException if the namespace does not exists
2514    */
2515   private void ensureNamespaceExists(final String name)
2516       throws IOException, NamespaceNotFoundException {
2517     checkNamespaceManagerReady();
2518     NamespaceDescriptor nsd = tableNamespaceManager.get(name);
2519     if (nsd == null) {
2520       throw new NamespaceNotFoundException(name);
2521     }
2522   }
2523 
2524   @Override
2525   public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException {
2526     checkNamespaceManagerReady();
2527 
2528     if (cpHost != null) {
2529       cpHost.preGetNamespaceDescriptor(name);
2530     }
2531 
2532     NamespaceDescriptor nsd = tableNamespaceManager.get(name);
2533     if (nsd == null) {
2534       throw new NamespaceNotFoundException(name);
2535     }
2536 
2537     if (cpHost != null) {
2538       cpHost.postGetNamespaceDescriptor(nsd);
2539     }
2540 
2541     return nsd;
2542   }
2543 
2544   @Override
2545   public List<NamespaceDescriptor> listNamespaceDescriptors() throws IOException {
2546     checkNamespaceManagerReady();
2547 
2548     final List<NamespaceDescriptor> descriptors = new ArrayList<NamespaceDescriptor>();
2549     boolean bypass = false;
2550     if (cpHost != null) {
2551       bypass = cpHost.preListNamespaceDescriptors(descriptors);
2552     }
2553 
2554     if (!bypass) {
2555       descriptors.addAll(tableNamespaceManager.list());
2556 
2557       if (cpHost != null) {
2558         cpHost.postListNamespaceDescriptors(descriptors);
2559       }
2560     }
2561     return descriptors;
2562   }
2563 
2564   @Override
2565   public boolean abortProcedure(final long procId, final boolean mayInterruptIfRunning)
2566       throws IOException {
2567     if (cpHost != null) {
2568       cpHost.preAbortProcedure(this.procedureExecutor, procId);
2569     }
2570 
2571     final boolean result = this.procedureExecutor.abort(procId, mayInterruptIfRunning);
2572 
2573     if (cpHost != null) {
2574       cpHost.postAbortProcedure();
2575     }
2576 
2577     return result;
2578   }
2579 
2580   @Override
2581   public List<ProcedureInfo> listProcedures() throws IOException {
2582     if (cpHost != null) {
2583       cpHost.preListProcedures();
2584     }
2585 
2586     final List<ProcedureInfo> procInfoList = this.procedureExecutor.listProcedures();
2587 
2588     if (cpHost != null) {
2589       cpHost.postListProcedures(procInfoList);
2590     }
2591 
2592     return procInfoList;
2593   }
2594 
2595   @Override
2596   public List<HTableDescriptor> listTableDescriptorsByNamespace(String name) throws IOException {
2597     ensureNamespaceExists(name);
2598     return listTableDescriptors(name, null, null, true);
2599   }
2600 
2601   @Override
2602   public List<TableName> listTableNamesByNamespace(String name) throws IOException {
2603     ensureNamespaceExists(name);
2604     return listTableNames(name, null, true);
2605   }
2606 
2607   /**
2608    * Returns the list of table descriptors that match the specified request
2609    *
2610    * @param namespace the namespace to query, or null if querying for all
2611    * @param regex The regular expression to match against, or null if querying for all
2612    * @param tableNameList the list of table names, or null if querying for all
2613    * @param includeSysTables False to match only against userspace tables
2614    * @return the list of table descriptors
2615    */
2616   public List<HTableDescriptor> listTableDescriptors(final String namespace, final String regex,
2617       final List<TableName> tableNameList, final boolean includeSysTables)
2618       throws IOException {
2619     final List<HTableDescriptor> descriptors = new ArrayList<HTableDescriptor>();
2620 
2621     boolean bypass = false;
2622     if (cpHost != null) {
2623       bypass = cpHost.preGetTableDescriptors(tableNameList, descriptors);
2624       // method required for AccessController.
2625       bypass |= cpHost.preGetTableDescriptors(tableNameList, descriptors, regex);
2626     }
2627 
2628     if (!bypass) {
2629       if (tableNameList == null || tableNameList.size() == 0) {
2630         // request for all TableDescriptors
2631         Collection<HTableDescriptor> htds;
2632         if (namespace != null && namespace.length() > 0) {
2633           htds = tableDescriptors.getByNamespace(namespace).values();
2634         } else {
2635           htds = tableDescriptors.getAll().values();
2636         }
2637 
2638         for (HTableDescriptor desc: htds) {
2639           if (includeSysTables || !desc.getTableName().isSystemTable()) {
2640             descriptors.add(desc);
2641           }
2642         }
2643       } else {
2644         for (TableName s: tableNameList) {
2645           HTableDescriptor desc = tableDescriptors.get(s);
2646           if (desc != null) {
2647             descriptors.add(desc);
2648           }
2649         }
2650       }
2651 
2652       // Retains only those matched by regular expression.
2653       if (regex != null) {
2654         filterTablesByRegex(descriptors, Pattern.compile(regex));
2655       }
2656 
2657       if (cpHost != null) {
2658         cpHost.postGetTableDescriptors(descriptors);
2659         // method required for AccessController.
2660         cpHost.postGetTableDescriptors(tableNameList, descriptors, regex);
2661       }
2662     }
2663     return descriptors;
2664   }
2665 
2666   /**
2667    * Returns the list of table names that match the specified request
2668    * @param regex The regular expression to match against, or null if querying for all
2669    * @param namespace the namespace to query, or null if querying for all
2670    * @param includeSysTables False to match only against userspace tables
2671    * @return the list of table names
2672    */
2673   public List<TableName> listTableNames(final String namespace, final String regex,
2674       final boolean includeSysTables) throws IOException {
2675     final List<HTableDescriptor> descriptors = new ArrayList<HTableDescriptor>();
2676 
2677     boolean bypass = false;
2678     if (cpHost != null) {
2679       bypass = cpHost.preGetTableNames(descriptors, regex);
2680     }
2681 
2682     if (!bypass) {
2683       // get all descriptors
2684       Collection<HTableDescriptor> htds;
2685       if (namespace != null && namespace.length() > 0) {
2686         htds = tableDescriptors.getByNamespace(namespace).values();
2687       } else {
2688         htds = tableDescriptors.getAll().values();
2689       }
2690 
2691       for (HTableDescriptor htd: htds) {
2692         if (includeSysTables || !htd.getTableName().isSystemTable()) {
2693           descriptors.add(htd);
2694         }
2695       }
2696 
2697       // Retains only those matched by regular expression.
2698       if (regex != null) {
2699         filterTablesByRegex(descriptors, Pattern.compile(regex));
2700       }
2701 
2702       if (cpHost != null) {
2703         cpHost.postGetTableNames(descriptors, regex);
2704       }
2705     }
2706 
2707     List<TableName> result = new ArrayList<TableName>(descriptors.size());
2708     for (HTableDescriptor htd: descriptors) {
2709       result.add(htd.getTableName());
2710     }
2711     return result;
2712   }
2713 
2714 
2715   /**
2716    * Removes the table descriptors that don't match the pattern.
2717    * @param descriptors list of table descriptors to filter
2718    * @param pattern the regex to use
2719    */
2720   private static void filterTablesByRegex(final Collection<HTableDescriptor> descriptors,
2721       final Pattern pattern) {
2722     final String defaultNS = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
2723     Iterator<HTableDescriptor> itr = descriptors.iterator();
2724     while (itr.hasNext()) {
2725       HTableDescriptor htd = itr.next();
2726       String tableName = htd.getTableName().getNameAsString();
2727       boolean matched = pattern.matcher(tableName).matches();
2728       if (!matched && htd.getTableName().getNamespaceAsString().equals(defaultNS)) {
2729         matched = pattern.matcher(defaultNS + TableName.NAMESPACE_DELIM + tableName).matches();
2730       }
2731       if (!matched) {
2732         itr.remove();
2733       }
2734     }
2735   }
2736 
2737   @Override
2738   public long getLastMajorCompactionTimestamp(TableName table) throws IOException {
2739     return getClusterStatus().getLastMajorCompactionTsForTable(table);
2740   }
2741 
2742   @Override
2743   public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
2744     return getClusterStatus().getLastMajorCompactionTsForRegion(regionName);
2745   }
2746 
2747   /**
2748    * Queries the state of the {@link LoadBalancerTracker}. If the balancer is not initialized,
2749    * false is returned.
2750    *
2751    * @return The state of the load balancer, or false if the load balancer isn't defined.
2752    */
2753   public boolean isBalancerOn() {
2754     if (null == loadBalancerTracker) return false;
2755     return loadBalancerTracker.isBalancerOn();
2756   }
2757 
2758   /**
2759    * Queries the state of the {@link RegionNormalizerTracker}. If it's not initialized,
2760    * false is returned.
2761    */
2762    public boolean isNormalizerOn() {
2763     if (null == regionNormalizerTracker) {
2764       return false;
2765     }
2766     return regionNormalizerTracker.isNormalizerOn();
2767   }
2768 
2769   /**
2770    * Fetch the configured {@link LoadBalancer} class name. If none is set, a default is returned.
2771    *
2772    * @return The name of the {@link LoadBalancer} in use.
2773    */
2774   public String getLoadBalancerClassName() {
2775     return conf.get(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, LoadBalancerFactory
2776         .getDefaultLoadBalancerClass().getName());
2777   }
2778 
2779   /**
2780    * @return RegionNormalizerTracker instance
2781    */
2782   public RegionNormalizerTracker getRegionNormalizerTracker() {
2783     return regionNormalizerTracker;
2784   }
2785 
2786   /**
2787    * Gets the mob file compaction state for a specific table.
2788    * Whether all the mob files are selected is known during the compaction execution, but
2789    * the statistic is done just before compaction starts, it is hard to know the compaction
2790    * type at that time, so the rough statistics are chosen for the mob file compaction. Only two
2791    * compaction states are available, CompactionState.MAJOR_AND_MINOR and CompactionState.NONE.
2792    * @param tableName The current table name.
2793    * @return If a given table is in mob file compaction now.
2794    */
2795   public CompactionState getMobCompactionState(TableName tableName) {
2796     AtomicInteger compactionsCount = mobFileCompactionStates.get(tableName);
2797     if (compactionsCount != null && compactionsCount.get() != 0) {
2798       return CompactionState.MAJOR_AND_MINOR;
2799     }
2800     return CompactionState.NONE;
2801   }
2802 
2803   public void reportMobFileCompactionStart(TableName tableName) throws IOException {
2804     IdLock.Entry lockEntry = null;
2805     try {
2806       lockEntry = mobFileCompactionLock.getLockEntry(tableName.hashCode());
2807       AtomicInteger compactionsCount = mobFileCompactionStates.get(tableName);
2808       if (compactionsCount == null) {
2809         compactionsCount = new AtomicInteger(0);
2810         mobFileCompactionStates.put(tableName, compactionsCount);
2811       }
2812       compactionsCount.incrementAndGet();
2813     } finally {
2814       if (lockEntry != null) {
2815         mobFileCompactionLock.releaseLockEntry(lockEntry);
2816       }
2817     }
2818   }
2819 
2820   public void reportMobFileCompactionEnd(TableName tableName) throws IOException {
2821     IdLock.Entry lockEntry = null;
2822     try {
2823       lockEntry = mobFileCompactionLock.getLockEntry(tableName.hashCode());
2824       AtomicInteger compactionsCount = mobFileCompactionStates.get(tableName);
2825       if (compactionsCount != null) {
2826         int count = compactionsCount.decrementAndGet();
2827         // remove the entry if the count is 0.
2828         if (count == 0) {
2829           mobFileCompactionStates.remove(tableName);
2830         }
2831       }
2832     } finally {
2833       if (lockEntry != null) {
2834         mobFileCompactionLock.releaseLockEntry(lockEntry);
2835       }
2836     }
2837   }
2838 }