001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master;
019
020import static org.apache.hadoop.hbase.HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS;
021
022import com.google.protobuf.Descriptors;
023import com.google.protobuf.Service;
024import java.io.IOException;
025import java.io.InterruptedIOException;
026import java.lang.reflect.Constructor;
027import java.lang.reflect.InvocationTargetException;
028import java.net.InetAddress;
029import java.net.InetSocketAddress;
030import java.net.UnknownHostException;
031import java.util.ArrayList;
032import java.util.Arrays;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.Comparator;
036import java.util.EnumSet;
037import java.util.HashMap;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Map;
041import java.util.Map.Entry;
042import java.util.Objects;
043import java.util.Optional;
044import java.util.Set;
045import java.util.concurrent.ExecutionException;
046import java.util.concurrent.Future;
047import java.util.concurrent.TimeUnit;
048import java.util.concurrent.TimeoutException;
049import java.util.concurrent.atomic.AtomicInteger;
050import java.util.function.Function;
051import java.util.regex.Pattern;
052import java.util.stream.Collectors;
053import javax.servlet.ServletException;
054import javax.servlet.http.HttpServlet;
055import javax.servlet.http.HttpServletRequest;
056import javax.servlet.http.HttpServletResponse;
057
058import org.apache.commons.lang3.StringUtils;
059import org.apache.hadoop.conf.Configuration;
060import org.apache.hadoop.fs.Path;
061import org.apache.hadoop.hbase.ClusterId;
062import org.apache.hadoop.hbase.ClusterMetrics;
063import org.apache.hadoop.hbase.ClusterMetrics.Option;
064import org.apache.hadoop.hbase.ClusterMetricsBuilder;
065import org.apache.hadoop.hbase.DoNotRetryIOException;
066import org.apache.hadoop.hbase.HBaseIOException;
067import org.apache.hadoop.hbase.HBaseInterfaceAudience;
068import org.apache.hadoop.hbase.HConstants;
069import org.apache.hadoop.hbase.InvalidFamilyOperationException;
070import org.apache.hadoop.hbase.MasterNotRunningException;
071import org.apache.hadoop.hbase.MetaTableAccessor;
072import org.apache.hadoop.hbase.NamespaceDescriptor;
073import org.apache.hadoop.hbase.PleaseHoldException;
074import org.apache.hadoop.hbase.ServerMetrics;
075import org.apache.hadoop.hbase.ServerName;
076import org.apache.hadoop.hbase.TableDescriptors;
077import org.apache.hadoop.hbase.TableName;
078import org.apache.hadoop.hbase.TableNotDisabledException;
079import org.apache.hadoop.hbase.TableNotFoundException;
080import org.apache.hadoop.hbase.UnknownRegionException;
081import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
082import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
083import org.apache.hadoop.hbase.client.MasterSwitchType;
084import org.apache.hadoop.hbase.client.RegionInfo;
085import org.apache.hadoop.hbase.client.RegionInfoBuilder;
086import org.apache.hadoop.hbase.client.TableDescriptor;
087import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
088import org.apache.hadoop.hbase.client.TableState;
089import org.apache.hadoop.hbase.client.VersionInfoUtil;
090import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
091import org.apache.hadoop.hbase.exceptions.DeserializationException;
092import org.apache.hadoop.hbase.exceptions.MergeRegionException;
093import org.apache.hadoop.hbase.executor.ExecutorType;
094import org.apache.hadoop.hbase.favored.FavoredNodesManager;
095import org.apache.hadoop.hbase.favored.FavoredNodesPromoter;
096import org.apache.hadoop.hbase.http.InfoServer;
097import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
098import org.apache.hadoop.hbase.ipc.RpcServer;
099import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
100import org.apache.hadoop.hbase.log.HBaseMarkers;
101import org.apache.hadoop.hbase.master.MasterRpcServices.BalanceSwitchMode;
102import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
103import org.apache.hadoop.hbase.master.assignment.MergeTableRegionsProcedure;
104import org.apache.hadoop.hbase.master.assignment.RegionStates;
105import org.apache.hadoop.hbase.master.assignment.RegionStates.RegionStateNode;
106import org.apache.hadoop.hbase.master.balancer.BalancerChore;
107import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer;
108import org.apache.hadoop.hbase.master.balancer.ClusterStatusChore;
109import org.apache.hadoop.hbase.master.balancer.LoadBalancerFactory;
110import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
111import org.apache.hadoop.hbase.master.cleaner.LogCleaner;
112import org.apache.hadoop.hbase.master.cleaner.ReplicationZKNodeCleaner;
113import org.apache.hadoop.hbase.master.cleaner.ReplicationZKNodeCleanerChore;
114import org.apache.hadoop.hbase.master.locking.LockManager;
115import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan;
116import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan.PlanType;
117import org.apache.hadoop.hbase.master.normalizer.RegionNormalizer;
118import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerChore;
119import org.apache.hadoop.hbase.master.normalizer.RegionNormalizerFactory;
120import org.apache.hadoop.hbase.master.procedure.CreateTableProcedure;
121import org.apache.hadoop.hbase.master.procedure.DeleteNamespaceProcedure;
122import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure;
123import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure;
124import org.apache.hadoop.hbase.master.procedure.EnableTableProcedure;
125import org.apache.hadoop.hbase.master.procedure.InitMetaProcedure;
126import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
127import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
128import org.apache.hadoop.hbase.master.procedure.MasterProcedureScheduler;
129import org.apache.hadoop.hbase.master.procedure.MasterProcedureUtil;
130import org.apache.hadoop.hbase.master.procedure.ModifyTableProcedure;
131import org.apache.hadoop.hbase.master.procedure.ProcedurePrepareLatch;
132import org.apache.hadoop.hbase.master.procedure.ServerCrashProcedure;
133import org.apache.hadoop.hbase.master.procedure.TruncateTableProcedure;
134import org.apache.hadoop.hbase.master.replication.ReplicationManager;
135import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
136import org.apache.hadoop.hbase.mob.MobConstants;
137import org.apache.hadoop.hbase.monitoring.MemoryBoundedLogMessageBuffer;
138import org.apache.hadoop.hbase.monitoring.MonitoredTask;
139import org.apache.hadoop.hbase.monitoring.TaskMonitor;
140import org.apache.hadoop.hbase.procedure.MasterProcedureManagerHost;
141import org.apache.hadoop.hbase.procedure.flush.MasterFlushTableProcedureManager;
142import org.apache.hadoop.hbase.procedure2.LockedResource;
143import org.apache.hadoop.hbase.procedure2.Procedure;
144import org.apache.hadoop.hbase.procedure2.ProcedureEvent;
145import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
146import org.apache.hadoop.hbase.procedure2.store.ProcedureStore.ProcedureStoreListener;
147import org.apache.hadoop.hbase.procedure2.store.wal.WALProcedureStore;
148import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
149import org.apache.hadoop.hbase.quotas.MasterQuotasObserver;
150import org.apache.hadoop.hbase.quotas.QuotaObserverChore;
151import org.apache.hadoop.hbase.quotas.QuotaUtil;
152import org.apache.hadoop.hbase.quotas.SnapshotQuotaObserverChore;
153import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshotNotifier;
154import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshotNotifierFactory;
155import org.apache.hadoop.hbase.regionserver.DefaultStoreEngine;
156import org.apache.hadoop.hbase.regionserver.HRegionServer;
157import org.apache.hadoop.hbase.regionserver.HStore;
158import org.apache.hadoop.hbase.regionserver.RSRpcServices;
159import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
160import org.apache.hadoop.hbase.regionserver.RegionSplitPolicy;
161import org.apache.hadoop.hbase.regionserver.compactions.ExploringCompactionPolicy;
162import org.apache.hadoop.hbase.regionserver.compactions.FIFOCompactionPolicy;
163import org.apache.hadoop.hbase.replication.ReplicationException;
164import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
165import org.apache.hadoop.hbase.replication.ReplicationPeerDescription;
166import org.apache.hadoop.hbase.replication.ReplicationUtils;
167import org.apache.hadoop.hbase.replication.master.ReplicationHFileCleaner;
168import org.apache.hadoop.hbase.replication.master.ReplicationLogCleaner;
169import org.apache.hadoop.hbase.replication.master.ReplicationPeerConfigUpgrader;
170import org.apache.hadoop.hbase.security.AccessDeniedException;
171import org.apache.hadoop.hbase.security.UserProvider;
172import org.apache.hadoop.hbase.trace.TraceUtil;
173import org.apache.hadoop.hbase.util.Addressing;
174import org.apache.hadoop.hbase.util.Bytes;
175import org.apache.hadoop.hbase.util.CompressionTest;
176import org.apache.hadoop.hbase.util.EncryptionTest;
177import org.apache.hadoop.hbase.util.HBaseFsck;
178import org.apache.hadoop.hbase.util.HFileArchiveUtil;
179import org.apache.hadoop.hbase.util.HasThread;
180import org.apache.hadoop.hbase.util.IdLock;
181import org.apache.hadoop.hbase.util.ModifyRegionUtils;
182import org.apache.hadoop.hbase.util.Pair;
183import org.apache.hadoop.hbase.util.RetryCounter;
184import org.apache.hadoop.hbase.util.RetryCounterFactory;
185import org.apache.hadoop.hbase.util.Threads;
186import org.apache.hadoop.hbase.util.VersionInfo;
187import org.apache.hadoop.hbase.zookeeper.LoadBalancerTracker;
188import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
189import org.apache.hadoop.hbase.zookeeper.RegionNormalizerTracker;
190import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
191import org.apache.hadoop.hbase.zookeeper.ZKUtil;
192import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
193import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
194import org.apache.yetus.audience.InterfaceAudience;
195import org.apache.zookeeper.KeeperException;
196import org.eclipse.jetty.server.Server;
197import org.eclipse.jetty.server.ServerConnector;
198import org.eclipse.jetty.servlet.ServletHolder;
199import org.eclipse.jetty.webapp.WebAppContext;
200import org.slf4j.Logger;
201import org.slf4j.LoggerFactory;
202
203import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
204import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
205import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
206
207import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
208import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
209import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
210import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceViolationPolicy;
211import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription;
212import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos;
213
214/**
215 * HMaster is the "master server" for HBase. An HBase cluster has one active
216 * master.  If many masters are started, all compete.  Whichever wins goes on to
217 * run the cluster.  All others park themselves in their constructor until
218 * master or cluster shutdown or until the active master loses its lease in
219 * zookeeper.  Thereafter, all running master jostle to take over master role.
220 *
221 * <p>The Master can be asked shutdown the cluster. See {@link #shutdown()}.  In
222 * this case it will tell all regionservers to go down and then wait on them
223 * all reporting in that they are down.  This master will then shut itself down.
224 *
225 * <p>You can also shutdown just this master.  Call {@link #stopMaster()}.
226 *
227 * @see org.apache.zookeeper.Watcher
228 */
229@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
230@SuppressWarnings("deprecation")
231public class HMaster extends HRegionServer implements MasterServices {
232  private static Logger LOG = LoggerFactory.getLogger(HMaster.class);
233
234  /**
235   * Protection against zombie master. Started once Master accepts active responsibility and
236   * starts taking over responsibilities. Allows a finite time window before giving up ownership.
237   */
238  private static class InitializationMonitor extends HasThread {
239    /** The amount of time in milliseconds to sleep before checking initialization status. */
240    public static final String TIMEOUT_KEY = "hbase.master.initializationmonitor.timeout";
241    public static final long TIMEOUT_DEFAULT = TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
242
243    /**
244     * When timeout expired and initialization has not complete, call {@link System#exit(int)} when
245     * true, do nothing otherwise.
246     */
247    public static final String HALT_KEY = "hbase.master.initializationmonitor.haltontimeout";
248    public static final boolean HALT_DEFAULT = false;
249
250    private final HMaster master;
251    private final long timeout;
252    private final boolean haltOnTimeout;
253
254    /** Creates a Thread that monitors the {@link #isInitialized()} state. */
255    InitializationMonitor(HMaster master) {
256      super("MasterInitializationMonitor");
257      this.master = master;
258      this.timeout = master.getConfiguration().getLong(TIMEOUT_KEY, TIMEOUT_DEFAULT);
259      this.haltOnTimeout = master.getConfiguration().getBoolean(HALT_KEY, HALT_DEFAULT);
260      this.setDaemon(true);
261    }
262
263    @Override
264    public void run() {
265      try {
266        while (!master.isStopped() && master.isActiveMaster()) {
267          Thread.sleep(timeout);
268          if (master.isInitialized()) {
269            LOG.debug("Initialization completed within allotted tolerance. Monitor exiting.");
270          } else {
271            LOG.error("Master failed to complete initialization after " + timeout + "ms. Please"
272                + " consider submitting a bug report including a thread dump of this process.");
273            if (haltOnTimeout) {
274              LOG.error("Zombie Master exiting. Thread dump to stdout");
275              Threads.printThreadInfo(System.out, "Zombie HMaster");
276              System.exit(-1);
277            }
278          }
279        }
280      } catch (InterruptedException ie) {
281        LOG.trace("InitMonitor thread interrupted. Existing.");
282      }
283    }
284  }
285
286  // MASTER is name of the webapp and the attribute name used stuffing this
287  //instance into web context.
288  public static final String MASTER = "master";
289
290  // Manager and zk listener for master election
291  private final ActiveMasterManager activeMasterManager;
292  // Region server tracker
293  private RegionServerTracker regionServerTracker;
294  // Draining region server tracker
295  private DrainingServerTracker drainingServerTracker;
296  // Tracker for load balancer state
297  LoadBalancerTracker loadBalancerTracker;
298
299  // Tracker for split and merge state
300  private SplitOrMergeTracker splitOrMergeTracker;
301
302  // Tracker for region normalizer state
303  private RegionNormalizerTracker regionNormalizerTracker;
304
305  private ClusterSchemaService clusterSchemaService;
306
307  public static final String HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS =
308    "hbase.master.wait.on.service.seconds";
309  public static final int DEFAULT_HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS = 5 * 60;
310
311  // Metrics for the HMaster
312  final MetricsMaster metricsMaster;
313  // file system manager for the master FS operations
314  private MasterFileSystem fileSystemManager;
315  private MasterWalManager walManager;
316
317  // server manager to deal with region server info
318  private volatile ServerManager serverManager;
319
320  // manager of assignment nodes in zookeeper
321  private AssignmentManager assignmentManager;
322
323  // manager of replication
324  private ReplicationManager replicationManager;
325
326  // buffer for "fatal error" notices from region servers
327  // in the cluster. This is only used for assisting
328  // operations/debugging.
329  MemoryBoundedLogMessageBuffer rsFatals;
330
331  // flag set after we become the active master (used for testing)
332  private volatile boolean activeMaster = false;
333
334  // flag set after we complete initialization once active,
335  // it is not private since it's used in unit tests
336  private final ProcedureEvent initialized = new ProcedureEvent("master initialized");
337
338  // flag set after master services are started,
339  // initialization may have not completed yet.
340  volatile boolean serviceStarted = false;
341
342  // Maximum time we should run balancer for
343  private final int maxBlancingTime;
344  // Maximum percent of regions in transition when balancing
345  private final double maxRitPercent;
346
347  private final LockManager lockManager = new LockManager(this);
348
349  private LoadBalancer balancer;
350  private RegionNormalizer normalizer;
351  private BalancerChore balancerChore;
352  private RegionNormalizerChore normalizerChore;
353  private ClusterStatusChore clusterStatusChore;
354  private ClusterStatusPublisher clusterStatusPublisherChore = null;
355
356  CatalogJanitor catalogJanitorChore;
357
358  private ReplicationZKNodeCleanerChore replicationZKNodeCleanerChore;
359  private LogCleaner logCleaner;
360  private HFileCleaner hfileCleaner;
361  private ExpiredMobFileCleanerChore expiredMobFileCleanerChore;
362  private MobCompactionChore mobCompactChore;
363  private MasterMobCompactionThread mobCompactThread;
364  // used to synchronize the mobCompactionStates
365  private final IdLock mobCompactionLock = new IdLock();
366  // save the information of mob compactions in tables.
367  // the key is table name, the value is the number of compactions in that table.
368  private Map<TableName, AtomicInteger> mobCompactionStates = Maps.newConcurrentMap();
369
370  MasterCoprocessorHost cpHost;
371
372  private final boolean preLoadTableDescriptors;
373
374  // Time stamps for when a hmaster became active
375  private long masterActiveTime;
376
377  // Time stamp for when HMaster finishes becoming Active Master
378  private long masterFinishedInitializationTime;
379
380  //should we check the compression codec type at master side, default true, HBASE-6370
381  private final boolean masterCheckCompression;
382
383  //should we check encryption settings at master side, default true
384  private final boolean masterCheckEncryption;
385
386  Map<String, Service> coprocessorServiceHandlers = Maps.newHashMap();
387
388  // monitor for snapshot of hbase tables
389  SnapshotManager snapshotManager;
390  // monitor for distributed procedures
391  private MasterProcedureManagerHost mpmHost;
392
393  // it is assigned after 'initialized' guard set to true, so should be volatile
394  private volatile MasterQuotaManager quotaManager;
395  private SpaceQuotaSnapshotNotifier spaceQuotaSnapshotNotifier;
396  private QuotaObserverChore quotaObserverChore;
397  private SnapshotQuotaObserverChore snapshotQuotaChore;
398
399  private ProcedureExecutor<MasterProcedureEnv> procedureExecutor;
400  private WALProcedureStore procedureStore;
401
402  // handle table states
403  private TableStateManager tableStateManager;
404
405  private long splitPlanCount;
406  private long mergePlanCount;
407
408  /* Handle favored nodes information */
409  private FavoredNodesManager favoredNodesManager;
410
411  /** jetty server for master to redirect requests to regionserver infoServer */
412  private Server masterJettyServer;
413
414  // Determine if we should do normal startup or minimal "single-user" mode with no region
415  // servers and no user tables. Useful for repair and recovery of hbase:meta
416  private final boolean maintenanceMode;
417  static final String MAINTENANCE_MODE = "hbase.master.maintenance_mode";
418
419  public static class RedirectServlet extends HttpServlet {
420    private static final long serialVersionUID = 2894774810058302473L;
421    private final int regionServerInfoPort;
422    private final String regionServerHostname;
423
424    /**
425     * @param infoServer that we're trying to send all requests to
426     * @param hostname may be null. if given, will be used for redirects instead of host from client.
427     */
428    public RedirectServlet(InfoServer infoServer, String hostname) {
429       regionServerInfoPort = infoServer.getPort();
430       regionServerHostname = hostname;
431    }
432
433    @Override
434    public void doGet(HttpServletRequest request,
435        HttpServletResponse response) throws ServletException, IOException {
436      String redirectHost = regionServerHostname;
437      if(redirectHost == null) {
438        redirectHost = request.getServerName();
439        if(!Addressing.isLocalAddress(InetAddress.getByName(redirectHost))) {
440          LOG.warn("Couldn't resolve '" + redirectHost + "' as an address local to this node and '" +
441              MASTER_HOSTNAME_KEY + "' is not set; client will get a HTTP 400 response. If " +
442              "your HBase deployment relies on client accessible names that the region server process " +
443              "can't resolve locally, then you should set the previously mentioned configuration variable " +
444              "to an appropriate hostname.");
445          // no sending client provided input back to the client, so the goal host is just in the logs.
446          response.sendError(400, "Request was to a host that I can't resolve for any of the network interfaces on " +
447              "this node. If this is due to an intermediary such as an HTTP load balancer or other proxy, your HBase " +
448              "administrator can set '" + MASTER_HOSTNAME_KEY + "' to point to the correct hostname.");
449          return;
450        }
451      }
452      // TODO this scheme should come from looking at the scheme registered in the infoserver's http server for the
453      // host and port we're using, but it's buried way too deep to do that ATM.
454      String redirectUrl = request.getScheme() + "://"
455        + redirectHost + ":" + regionServerInfoPort
456        + request.getRequestURI();
457      response.sendRedirect(redirectUrl);
458    }
459  }
460
461  /**
462   * Initializes the HMaster. The steps are as follows:
463   * <p>
464   * <ol>
465   * <li>Initialize the local HRegionServer
466   * <li>Start the ActiveMasterManager.
467   * </ol>
468   * <p>
469   * Remaining steps of initialization occur in
470   * #finishActiveMasterInitialization(MonitoredTask) after
471   * the master becomes the active one.
472   */
473  public HMaster(final Configuration conf)
474      throws IOException, KeeperException {
475    super(conf);
476    TraceUtil.initTracer(conf);
477    try {
478      if (conf.getBoolean(MAINTENANCE_MODE, false)) {
479        LOG.info("Detected {}=true via configuration.", MAINTENANCE_MODE);
480        maintenanceMode = true;
481      } else if (Boolean.getBoolean(MAINTENANCE_MODE)) {
482        LOG.info("Detected {}=true via environment variables.", MAINTENANCE_MODE);
483        maintenanceMode = true;
484      } else {
485        maintenanceMode = false;
486      }
487
488      this.rsFatals = new MemoryBoundedLogMessageBuffer(
489          conf.getLong("hbase.master.buffer.for.rs.fatals", 1 * 1024 * 1024));
490      LOG.info("hbase.rootdir=" + getRootDir() +
491          ", hbase.cluster.distributed=" + this.conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, false));
492
493      // Disable usage of meta replicas in the master
494      this.conf.setBoolean(HConstants.USE_META_REPLICAS, false);
495
496      decorateMasterConfiguration(this.conf);
497
498      // Hack! Maps DFSClient => Master for logs.  HDFS made this
499      // config param for task trackers, but we can piggyback off of it.
500      if (this.conf.get("mapreduce.task.attempt.id") == null) {
501        this.conf.set("mapreduce.task.attempt.id", "hb_m_" + this.serverName.toString());
502      }
503
504      // should we check the compression codec type at master side, default true, HBASE-6370
505      this.masterCheckCompression = conf.getBoolean("hbase.master.check.compression", true);
506
507      // should we check encryption settings at master side, default true
508      this.masterCheckEncryption = conf.getBoolean("hbase.master.check.encryption", true);
509
510      this.metricsMaster = new MetricsMaster(new MetricsMasterWrapperImpl(this));
511
512      // preload table descriptor at startup
513      this.preLoadTableDescriptors = conf.getBoolean("hbase.master.preload.tabledescriptors", true);
514
515      this.maxBlancingTime = getMaxBalancingTime();
516      this.maxRitPercent = conf.getDouble(HConstants.HBASE_MASTER_BALANCER_MAX_RIT_PERCENT,
517          HConstants.DEFAULT_HBASE_MASTER_BALANCER_MAX_RIT_PERCENT);
518
519      // Do we publish the status?
520
521      boolean shouldPublish = conf.getBoolean(HConstants.STATUS_PUBLISHED,
522          HConstants.STATUS_PUBLISHED_DEFAULT);
523      Class<? extends ClusterStatusPublisher.Publisher> publisherClass =
524          conf.getClass(ClusterStatusPublisher.STATUS_PUBLISHER_CLASS,
525              ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS,
526              ClusterStatusPublisher.Publisher.class);
527
528      if (shouldPublish) {
529        if (publisherClass == null) {
530          LOG.warn(HConstants.STATUS_PUBLISHED + " is true, but " +
531              ClusterStatusPublisher.DEFAULT_STATUS_PUBLISHER_CLASS +
532              " is not set - not publishing status");
533        } else {
534          clusterStatusPublisherChore = new ClusterStatusPublisher(this, conf, publisherClass);
535          getChoreService().scheduleChore(clusterStatusPublisherChore);
536        }
537      }
538
539      // Some unit tests don't need a cluster, so no zookeeper at all
540      if (!conf.getBoolean("hbase.testing.nocluster", false)) {
541        this.activeMasterManager = new ActiveMasterManager(zooKeeper, this.serverName, this);
542      } else {
543        this.activeMasterManager = null;
544      }
545    } catch (Throwable t) {
546      // Make sure we log the exception. HMaster is often started via reflection and the
547      // cause of failed startup is lost.
548      LOG.error("Failed construction of Master", t);
549      throw t;
550    }
551  }
552
553  @Override
554  protected String getUseThisHostnameInstead(Configuration conf) {
555    return conf.get(MASTER_HOSTNAME_KEY);
556  }
557
558  // Main run loop. Calls through to the regionserver run loop AFTER becoming active Master; will
559  // block in here until then.
560  @Override
561  public void run() {
562    try {
563      if (!conf.getBoolean("hbase.testing.nocluster", false)) {
564        Threads.setDaemonThreadRunning(new Thread(() -> {
565          try {
566            int infoPort = putUpJettyServer();
567            startActiveMasterManager(infoPort);
568          } catch (Throwable t) {
569            // Make sure we log the exception.
570            String error = "Failed to become Active Master";
571            LOG.error(error, t);
572            // Abort should have been called already.
573            if (!isAborted()) {
574              abort(error, t);
575            }
576          }
577        }));
578      }
579      // Fall in here even if we have been aborted. Need to run the shutdown services and
580      // the super run call will do this for us.
581      super.run();
582    } finally {
583      if (this.clusterSchemaService != null) {
584        // If on way out, then we are no longer active master.
585        this.clusterSchemaService.stopAsync();
586        try {
587          this.clusterSchemaService.awaitTerminated(
588              getConfiguration().getInt(HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS,
589              DEFAULT_HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS), TimeUnit.SECONDS);
590        } catch (TimeoutException te) {
591          LOG.warn("Failed shutdown of clusterSchemaService", te);
592        }
593      }
594      this.activeMaster = false;
595    }
596  }
597
598  // return the actual infoPort, -1 means disable info server.
599  private int putUpJettyServer() throws IOException {
600    if (!conf.getBoolean("hbase.master.infoserver.redirect", true)) {
601      return -1;
602    }
603    final int infoPort = conf.getInt("hbase.master.info.port.orig",
604      HConstants.DEFAULT_MASTER_INFOPORT);
605    // -1 is for disabling info server, so no redirecting
606    if (infoPort < 0 || infoServer == null) {
607      return -1;
608    }
609    if(infoPort == infoServer.getPort()) {
610      return infoPort;
611    }
612    final String addr = conf.get("hbase.master.info.bindAddress", "0.0.0.0");
613    if (!Addressing.isLocalAddress(InetAddress.getByName(addr))) {
614      String msg =
615          "Failed to start redirecting jetty server. Address " + addr
616              + " does not belong to this host. Correct configuration parameter: "
617              + "hbase.master.info.bindAddress";
618      LOG.error(msg);
619      throw new IOException(msg);
620    }
621
622    // TODO I'm pretty sure we could just add another binding to the InfoServer run by
623    // the RegionServer and have it run the RedirectServlet instead of standing up
624    // a second entire stack here.
625    masterJettyServer = new Server();
626    final ServerConnector connector = new ServerConnector(masterJettyServer);
627    connector.setHost(addr);
628    connector.setPort(infoPort);
629    masterJettyServer.addConnector(connector);
630    masterJettyServer.setStopAtShutdown(true);
631
632    final String redirectHostname =
633        StringUtils.isBlank(useThisHostnameInstead) ? null : useThisHostnameInstead;
634
635    final RedirectServlet redirect = new RedirectServlet(infoServer, redirectHostname);
636    final WebAppContext context = new WebAppContext(null, "/", null, null, null, null, WebAppContext.NO_SESSIONS);
637    context.addServlet(new ServletHolder(redirect), "/*");
638    context.setServer(masterJettyServer);
639
640    try {
641      masterJettyServer.start();
642    } catch (Exception e) {
643      throw new IOException("Failed to start redirecting jetty server", e);
644    }
645    return connector.getLocalPort();
646  }
647
648  @Override
649  protected Function<TableDescriptorBuilder, TableDescriptorBuilder> getMetaTableObserver() {
650    return builder -> builder.setRegionReplication(conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM));
651  }
652  /**
653   * For compatibility, if failed with regionserver credentials, try the master one
654   */
655  @Override
656  protected void login(UserProvider user, String host) throws IOException {
657    try {
658      super.login(user, host);
659    } catch (IOException ie) {
660      user.login("hbase.master.keytab.file",
661        "hbase.master.kerberos.principal", host);
662    }
663  }
664
665  /**
666   * If configured to put regions on active master,
667   * wait till a backup master becomes active.
668   * Otherwise, loop till the server is stopped or aborted.
669   */
670  @Override
671  protected void waitForMasterActive(){
672    if (maintenanceMode) {
673      return;
674    }
675    boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(conf);
676    while (!(tablesOnMaster && activeMaster) && !isStopped() && !isAborted()) {
677      sleeper.sleep();
678    }
679  }
680
681  @VisibleForTesting
682  public MasterRpcServices getMasterRpcServices() {
683    return (MasterRpcServices)rpcServices;
684  }
685
686  public boolean balanceSwitch(final boolean b) throws IOException {
687    return getMasterRpcServices().switchBalancer(b, BalanceSwitchMode.ASYNC);
688  }
689
690  @Override
691  protected String getProcessName() {
692    return MASTER;
693  }
694
695  @Override
696  protected boolean canCreateBaseZNode() {
697    return true;
698  }
699
700  @Override
701  protected boolean canUpdateTableDescriptor() {
702    return true;
703  }
704
705  @Override
706  protected RSRpcServices createRpcServices() throws IOException {
707    return new MasterRpcServices(this);
708  }
709
710  @Override
711  protected void configureInfoServer() {
712    infoServer.addServlet("master-status", "/master-status", MasterStatusServlet.class);
713    infoServer.setAttribute(MASTER, this);
714    if (LoadBalancer.isTablesOnMaster(conf)) {
715      super.configureInfoServer();
716    }
717  }
718
719  @Override
720  protected Class<? extends HttpServlet> getDumpServlet() {
721    return MasterDumpServlet.class;
722  }
723
724  @Override
725  public MetricsMaster getMasterMetrics() {
726    return metricsMaster;
727  }
728
729  /**
730   * Initialize all ZK based system trackers. But do not include {@link RegionServerTracker}, it
731   * should have already been initialized along with {@link ServerManager}.
732   * Will be overridden in tests.
733   */
734  @VisibleForTesting
735  protected void initializeZKBasedSystemTrackers()
736      throws IOException, InterruptedException, KeeperException {
737    this.balancer = LoadBalancerFactory.getLoadBalancer(conf);
738    this.normalizer = RegionNormalizerFactory.getRegionNormalizer(conf);
739    this.normalizer.setMasterServices(this);
740    this.normalizer.setMasterRpcServices((MasterRpcServices)rpcServices);
741    this.loadBalancerTracker = new LoadBalancerTracker(zooKeeper, this);
742    this.loadBalancerTracker.start();
743
744    this.regionNormalizerTracker = new RegionNormalizerTracker(zooKeeper, this);
745    this.regionNormalizerTracker.start();
746
747    this.splitOrMergeTracker = new SplitOrMergeTracker(zooKeeper, conf, this);
748    this.splitOrMergeTracker.start();
749
750    this.replicationManager = new ReplicationManager(conf, zooKeeper, this);
751
752    this.drainingServerTracker = new DrainingServerTracker(zooKeeper, this, this.serverManager);
753    this.drainingServerTracker.start();
754
755    // Set the cluster as up.  If new RSs, they'll be waiting on this before
756    // going ahead with their startup.
757    boolean wasUp = this.clusterStatusTracker.isClusterUp();
758    if (!wasUp) this.clusterStatusTracker.setClusterUp();
759
760    LOG.info("Active/primary master=" + this.serverName +
761        ", sessionid=0x" +
762        Long.toHexString(this.zooKeeper.getRecoverableZooKeeper().getSessionId()) +
763        ", setting cluster-up flag (Was=" + wasUp + ")");
764
765    // create/initialize the snapshot manager and other procedure managers
766    this.snapshotManager = new SnapshotManager();
767    this.mpmHost = new MasterProcedureManagerHost();
768    this.mpmHost.register(this.snapshotManager);
769    this.mpmHost.register(new MasterFlushTableProcedureManager());
770    this.mpmHost.loadProcedures(conf);
771    this.mpmHost.initialize(this, this.metricsMaster);
772  }
773
774  /**
775   * Finish initialization of HMaster after becoming the primary master.
776   *
777   * The startup order is a bit complicated but very important, do not change it unless you know
778   * what you are doing.
779   * <li>Initialize file system based components - file system manager, wal manager, table
780   * descriptors, etc</li>
781   * <li>Publish cluster id</li>
782   * <li>Here comes the most complicated part - initialize server manager, assignment manager and
783   * region server tracker
784   * <ol type='i'>
785   * <li>Create server manager</li>
786   * <li>Create procedure executor, load the procedures, but do not start workers. We will start it
787   * later after we finish scheduling SCPs to avoid scheduling duplicated SCPs for the same
788   * server</li>
789   * <li>Create assignment manager and start it, load the meta region state, but do not load data
790   * from meta region</li>
791   * <li>Start region server tracker, construct the online servers set and find out dead servers and
792   * schedule SCP for them. The online servers will be constructed by scanning zk, and we will also
793   * scan the wal directory to find out possible live region servers, and the differences between
794   * these two sets are the dead servers</li>
795   * </ol>
796   * </li>
797   * <li>If this is a new deploy, schedule a InitMetaProcedure to initialize meta</li>
798   * <li>Start necessary service threads - balancer, catalog janior, executor services, and also the
799   * procedure executor, etc. Notice that the balancer must be created first as assignment manager
800   * may use it when assigning regions.</li>
801   * <li>Wait for meta to be initialized if necesssary, start table state manager.</li>
802   * <li>Wait for enough region servers to check-in</li>
803   * <li>Let assignment manager load data from meta and construct region states</li>
804   * <li>Start all other things such as chore services, etc</li>
805   * <p/>
806   * Notice that now we will not schedule a special procedure to make meta online(unless the first
807   * time where meta has not been created yet), we will rely on SCP to bring meta online.
808   */
809  private void finishActiveMasterInitialization(MonitoredTask status)
810      throws IOException, InterruptedException, KeeperException {
811
812    Thread zombieDetector = new Thread(new InitializationMonitor(this),
813        "ActiveMasterInitializationMonitor-" + System.currentTimeMillis());
814    zombieDetector.setDaemon(true);
815    zombieDetector.start();
816
817    /*
818     * We are active master now... go initialize components we need to run.
819     */
820    status.setStatus("Initializing Master file system");
821
822    this.masterActiveTime = System.currentTimeMillis();
823    // TODO: Do this using Dependency Injection, using PicoContainer, Guice or Spring.
824
825    // Only initialize the MemStoreLAB when master carry table
826    if (LoadBalancer.isTablesOnMaster(conf)) {
827      initializeMemStoreChunkCreator();
828    }
829    this.fileSystemManager = new MasterFileSystem(conf);
830    this.walManager = new MasterWalManager(this);
831
832    // enable table descriptors cache
833    this.tableDescriptors.setCacheOn();
834
835    // warm-up HTDs cache on master initialization
836    if (preLoadTableDescriptors) {
837      status.setStatus("Pre-loading table descriptors");
838      this.tableDescriptors.getAll();
839    }
840
841    // Publish cluster ID; set it in Master too. The superclass RegionServer does this later but
842    // only after it has checked in with the Master. At least a few tests ask Master for clusterId
843    // before it has called its run method and before RegionServer has done the reportForDuty.
844    ClusterId clusterId = fileSystemManager.getClusterId();
845    status.setStatus("Publishing Cluster ID " + clusterId + " in ZooKeeper");
846    ZKClusterId.setClusterId(this.zooKeeper, fileSystemManager.getClusterId());
847    this.clusterId = clusterId.toString();
848
849    // Precaution. Put in place the old hbck1 lock file to fence out old hbase1s running their
850    // hbck1s against an hbase2 cluster; it could do damage. To skip this behavior, set
851    // hbase.write.hbck1.lock.file to false.
852    if (this.conf.getBoolean("hbase.write.hbck1.lock.file", true)) {
853      HBaseFsck.checkAndMarkRunningHbck(this.conf,
854          HBaseFsck.createLockRetryCounterFactory(this.conf).create());
855    }
856
857    status.setStatus("Initialze ServerManager and schedule SCP for crash servers");
858    this.serverManager = createServerManager(this);
859    createProcedureExecutor();
860    // Create Assignment Manager
861    this.assignmentManager = new AssignmentManager(this);
862    this.assignmentManager.start();
863    // Start RegionServerTracker with listing of servers found with exiting SCPs -- these should
864    // be registered in the deadServers set -- and with the list of servernames out on the
865    // filesystem that COULD BE 'alive' (we'll schedule SCPs for each and let SCP figure it out).
866    // We also pass dirs that are already 'splitting'... so we can do some checks down in tracker.
867    // TODO: Generate the splitting and live Set in one pass instead of two as we currently do.
868    this.regionServerTracker = new RegionServerTracker(zooKeeper, this, this.serverManager);
869    this.regionServerTracker.start(
870      procedureExecutor.getProcedures().stream().filter(p -> p instanceof ServerCrashProcedure)
871        .map(p -> ((ServerCrashProcedure) p).getServerName()).collect(Collectors.toSet()),
872      walManager.getLiveServersFromWALDir(), walManager.getSplittingServersFromWALDir());
873    // This manager will be started AFTER hbase:meta is confirmed on line.
874    // hbase.mirror.table.state.to.zookeeper is so hbase1 clients can connect. They read table
875    // state from zookeeper while hbase2 reads it from hbase:meta. Disable if no hbase1 clients.
876    this.tableStateManager =
877        this.conf.getBoolean(MirroringTableStateManager.MIRROR_TABLE_STATE_TO_ZK_KEY, true)?
878        new MirroringTableStateManager(this):
879        new TableStateManager(this);
880
881    status.setStatus("Initializing ZK system trackers");
882    initializeZKBasedSystemTrackers();
883    // Set ourselves as active Master now our claim has succeeded up in zk.
884    this.activeMaster = true;
885
886    // This is for backwards compatibility
887    // See HBASE-11393
888    status.setStatus("Update TableCFs node in ZNode");
889    ReplicationPeerConfigUpgrader tableCFsUpdater = new ReplicationPeerConfigUpgrader(zooKeeper,
890            conf, this.clusterConnection);
891    tableCFsUpdater.copyTableCFs();
892
893    if (!maintenanceMode) {
894      // Add the Observer to delete quotas on table deletion before starting all CPs by
895      // default with quota support, avoiding if user specifically asks to not load this Observer.
896      if (QuotaUtil.isQuotaEnabled(conf)) {
897        updateConfigurationForQuotasObserver(conf);
898      }
899      // initialize master side coprocessors before we start handling requests
900      status.setStatus("Initializing master coprocessors");
901      this.cpHost = new MasterCoprocessorHost(this, this.conf);
902    }
903
904    // Checking if meta needs initializing.
905    status.setStatus("Initializing meta table if this is a new deploy");
906    InitMetaProcedure initMetaProc = null;
907    // Print out state of hbase:meta on startup; helps debugging.
908    RegionState rs = this.assignmentManager.getRegionStates().
909        getRegionState(RegionInfoBuilder.FIRST_META_REGIONINFO);
910    LOG.info("hbase:meta {}", rs);
911    if (rs.isOffline()) {
912      Optional<InitMetaProcedure> optProc = procedureExecutor.getProcedures().stream()
913        .filter(p -> p instanceof InitMetaProcedure).map(o -> (InitMetaProcedure) o).findAny();
914      initMetaProc = optProc.orElseGet(() -> {
915        // schedule an init meta procedure if meta has not been deployed yet
916        InitMetaProcedure temp = new InitMetaProcedure();
917        procedureExecutor.submitProcedure(temp);
918        return temp;
919      });
920    }
921    if (this.balancer instanceof FavoredNodesPromoter) {
922      favoredNodesManager = new FavoredNodesManager(this);
923    }
924
925    // initialize load balancer
926    this.balancer.setMasterServices(this);
927    this.balancer.setClusterMetrics(getClusterMetricsWithoutCoprocessor());
928    this.balancer.initialize();
929
930    // start up all service threads.
931    status.setStatus("Initializing master service threads");
932    startServiceThreads();
933    // wait meta to be initialized after we start procedure executor
934    if (initMetaProc != null) {
935      initMetaProc.await();
936    }
937    // Wake up this server to check in
938    sleeper.skipSleepCycle();
939
940    // Wait for region servers to report in.
941    // With this as part of master initialization, it precludes our being able to start a single
942    // server that is both Master and RegionServer. Needs more thought. TODO.
943    String statusStr = "Wait for region servers to report in";
944    status.setStatus(statusStr);
945    LOG.info(Objects.toString(status));
946    waitForRegionServers(status);
947
948    // Check if master is shutting down because issue initializing regionservers or balancer.
949    if (isStopped()) {
950      return;
951    }
952
953    status.setStatus("Starting assignment manager");
954    // FIRST HBASE:META READ!!!!
955    // The below cannot make progress w/o hbase:meta being online.
956    // This is the FIRST attempt at going to hbase:meta. Meta on-lining is going on in background
957    // as procedures run -- in particular SCPs for crashed servers... One should put up hbase:meta
958    // if it is down. It may take a while to come online. So, wait here until meta if for sure
959    // available. Thats what waitUntilMetaOnline does.
960    if (!waitUntilMetaOnline()) {
961      return;
962    }
963    this.assignmentManager.joinCluster();
964    // The below depends on hbase:meta being online.
965    this.tableStateManager.start();
966    // Below has to happen after tablestatemanager has started in the case where this hbase-2.x
967    // is being started over an hbase-1.x dataset. tablestatemanager runs a migration as part
968    // of its 'start' moving table state from zookeeper to hbase:meta. This migration needs to
969    // complete before we do this next step processing offline regions else it fails reading
970    // table states messing up master launch (namespace table, etc., are not assigned).
971    this.assignmentManager.processOfflineRegions();
972    // Initialize after meta is up as below scans meta
973    if (favoredNodesManager != null && !maintenanceMode) {
974      SnapshotOfRegionAssignmentFromMeta snapshotOfRegionAssignment =
975          new SnapshotOfRegionAssignmentFromMeta(getConnection());
976      snapshotOfRegionAssignment.initialize();
977      favoredNodesManager.initialize(snapshotOfRegionAssignment);
978    }
979
980    // set cluster status again after user regions are assigned
981    this.balancer.setClusterMetrics(getClusterMetricsWithoutCoprocessor());
982
983    // Start balancer and meta catalog janitor after meta and regions have been assigned.
984    status.setStatus("Starting balancer and catalog janitor");
985    this.clusterStatusChore = new ClusterStatusChore(this, balancer);
986    getChoreService().scheduleChore(clusterStatusChore);
987    this.balancerChore = new BalancerChore(this);
988    getChoreService().scheduleChore(balancerChore);
989    this.normalizerChore = new RegionNormalizerChore(this);
990    getChoreService().scheduleChore(normalizerChore);
991    this.catalogJanitorChore = new CatalogJanitor(this);
992    getChoreService().scheduleChore(catalogJanitorChore);
993
994    // NAMESPACE READ!!!!
995    // Here we expect hbase:namespace to be online. See inside initClusterSchemaService.
996    // TODO: Fix this. Namespace is a pain being a sort-of system table. Fold it in to hbase:meta.
997    // isNamespace does like isMeta and waits until namespace is onlined before allowing progress.
998    if (!waitUntilNamespaceOnline()) {
999      return;
1000    }
1001    status.setStatus("Starting cluster schema service");
1002    initClusterSchemaService();
1003
1004    if (this.cpHost != null) {
1005      try {
1006        this.cpHost.preMasterInitialization();
1007      } catch (IOException e) {
1008        LOG.error("Coprocessor preMasterInitialization() hook failed", e);
1009      }
1010    }
1011
1012    status.markComplete("Initialization successful");
1013    LOG.info(String.format("Master has completed initialization %.3fsec",
1014       (System.currentTimeMillis() - masterActiveTime) / 1000.0f));
1015    this.masterFinishedInitializationTime = System.currentTimeMillis();
1016    configurationManager.registerObserver(this.balancer);
1017    configurationManager.registerObserver(this.hfileCleaner);
1018    configurationManager.registerObserver(this.logCleaner);
1019    // Set master as 'initialized'.
1020    setInitialized(true);
1021
1022    if (maintenanceMode) {
1023      LOG.info("Detected repair mode, skipping final initialization steps.");
1024      return;
1025    }
1026
1027    assignmentManager.checkIfShouldMoveSystemRegionAsync();
1028    status.setStatus("Assign meta replicas");
1029    MasterMetaBootstrap metaBootstrap = createMetaBootstrap();
1030    metaBootstrap.assignMetaReplicas();
1031    status.setStatus("Starting quota manager");
1032    initQuotaManager();
1033    if (QuotaUtil.isQuotaEnabled(conf)) {
1034      // Create the quota snapshot notifier
1035      spaceQuotaSnapshotNotifier = createQuotaSnapshotNotifier();
1036      spaceQuotaSnapshotNotifier.initialize(getClusterConnection());
1037      this.quotaObserverChore = new QuotaObserverChore(this, getMasterMetrics());
1038      // Start the chore to read the region FS space reports and act on them
1039      getChoreService().scheduleChore(quotaObserverChore);
1040
1041      this.snapshotQuotaChore = new SnapshotQuotaObserverChore(this, getMasterMetrics());
1042      // Start the chore to read snapshots and add their usage to table/NS quotas
1043      getChoreService().scheduleChore(snapshotQuotaChore);
1044    }
1045
1046    // clear the dead servers with same host name and port of online server because we are not
1047    // removing dead server with same hostname and port of rs which is trying to check in before
1048    // master initialization. See HBASE-5916.
1049    this.serverManager.clearDeadServersWithSameHostNameAndPortOfOnlineServer();
1050
1051    // Check and set the znode ACLs if needed in case we are overtaking a non-secure configuration
1052    status.setStatus("Checking ZNode ACLs");
1053    zooKeeper.checkAndSetZNodeAcls();
1054
1055    status.setStatus("Initializing MOB Cleaner");
1056    initMobCleaner();
1057
1058    status.setStatus("Calling postStartMaster coprocessors");
1059    if (this.cpHost != null) {
1060      // don't let cp initialization errors kill the master
1061      try {
1062        this.cpHost.postStartMaster();
1063      } catch (IOException ioe) {
1064        LOG.error("Coprocessor postStartMaster() hook failed", ioe);
1065      }
1066    }
1067
1068    zombieDetector.interrupt();
1069
1070    /*
1071     * After master has started up, lets do balancer post startup initialization. Since this runs
1072     * in activeMasterManager thread, it should be fine.
1073     */
1074    long start = System.currentTimeMillis();
1075    this.balancer.postMasterStartupInitialize();
1076    if (LOG.isDebugEnabled()) {
1077      LOG.debug("Balancer post startup initialization complete, took " + (
1078          (System.currentTimeMillis() - start) / 1000) + " seconds");
1079    }
1080  }
1081
1082  /**
1083   * Check hbase:meta is up and ready for reading. For use during Master startup only.
1084   * @return True if meta is UP and online and startup can progress. Otherwise, meta is not online
1085   *   and we will hold here until operator intervention.
1086   */
1087  @VisibleForTesting
1088  public boolean waitUntilMetaOnline() throws InterruptedException {
1089    return isRegionOnline(RegionInfoBuilder.FIRST_META_REGIONINFO);
1090  }
1091
1092  /**
1093   * @return True if region is online and scannable else false if an error or shutdown (Otherwise
1094   *   we just block in here holding up all forward-progess).
1095   */
1096  private boolean isRegionOnline(RegionInfo ri) throws InterruptedException {
1097    RetryCounter rc = null;
1098    while (!isStopped()) {
1099      RegionState rs = this.assignmentManager.getRegionStates().getRegionState(ri);
1100      if (rs.isOpened()) {
1101        if (this.getServerManager().isServerOnline(rs.getServerName())) {
1102          return true;
1103        }
1104      }
1105      // Region is not OPEN.
1106      Optional<Procedure<MasterProcedureEnv>> optProc = this.procedureExecutor.getProcedures().
1107          stream().filter(p -> p instanceof ServerCrashProcedure).findAny();
1108      // TODO: Add a page to refguide on how to do repair. Have this log message point to it.
1109      // Page will talk about loss of edits, how to schedule at least the meta WAL recovery, and
1110      // then how to assign including how to break region lock if one held.
1111      LOG.warn("{} is NOT online; state={}; ServerCrashProcedures={}. Master startup cannot " +
1112          "progress, in holding-pattern until region onlined; operator intervention required. " +
1113          "Schedule an assign.", ri.getRegionNameAsString(), rs, optProc.isPresent());
1114      // Check once-a-minute.
1115      if (rc == null) {
1116        rc = new RetryCounterFactory(1000).create();
1117      }
1118      Threads.sleep(rc.getBackoffTimeAndIncrementAttempts());
1119    }
1120    return false;
1121  }
1122
1123  /**
1124   * Check hbase:namespace table is assigned. If not, startup will hang looking for the ns table
1125   * (TODO: Fix this! NS should not hold-up startup).
1126   * @return True if namespace table is up/online.
1127   */
1128  @VisibleForTesting
1129  public boolean waitUntilNamespaceOnline() throws InterruptedException {
1130    List<RegionInfo> ris = this.assignmentManager.getRegionStates().
1131        getRegionsOfTable(TableName.NAMESPACE_TABLE_NAME);
1132    if (ris.isEmpty()) {
1133      // If empty, means we've not assigned the namespace table yet... Just return true so startup
1134      // continues and the namespace table gets created.
1135      return true;
1136    }
1137    // Else there are namespace regions up in meta. Ensure they are assigned before we go on.
1138    for (RegionInfo ri: ris) {
1139      isRegionOnline(ri);
1140    }
1141    return true;
1142  }
1143
1144  /**
1145   * Adds the {@code MasterQuotasObserver} to the list of configured Master observers to
1146   * automatically remove quotas for a table when that table is deleted.
1147   */
1148  @VisibleForTesting
1149  public void updateConfigurationForQuotasObserver(Configuration conf) {
1150    // We're configured to not delete quotas on table deletion, so we don't need to add the obs.
1151    if (!conf.getBoolean(
1152          MasterQuotasObserver.REMOVE_QUOTA_ON_TABLE_DELETE,
1153          MasterQuotasObserver.REMOVE_QUOTA_ON_TABLE_DELETE_DEFAULT)) {
1154      return;
1155    }
1156    String[] masterCoprocs = conf.getStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY);
1157    final int length = null == masterCoprocs ? 0 : masterCoprocs.length;
1158    String[] updatedCoprocs = new String[length + 1];
1159    if (length > 0) {
1160      System.arraycopy(masterCoprocs, 0, updatedCoprocs, 0, masterCoprocs.length);
1161    }
1162    updatedCoprocs[length] = MasterQuotasObserver.class.getName();
1163    conf.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, updatedCoprocs);
1164  }
1165
1166  private void initMobCleaner() {
1167    this.expiredMobFileCleanerChore = new ExpiredMobFileCleanerChore(this);
1168    getChoreService().scheduleChore(expiredMobFileCleanerChore);
1169
1170    int mobCompactionPeriod = conf.getInt(MobConstants.MOB_COMPACTION_CHORE_PERIOD,
1171        MobConstants.DEFAULT_MOB_COMPACTION_CHORE_PERIOD);
1172    if (mobCompactionPeriod > 0) {
1173      this.mobCompactChore = new MobCompactionChore(this, mobCompactionPeriod);
1174      getChoreService().scheduleChore(mobCompactChore);
1175    } else {
1176      LOG
1177        .info("The period is " + mobCompactionPeriod + " seconds, MobCompactionChore is disabled");
1178    }
1179    this.mobCompactThread = new MasterMobCompactionThread(this);
1180  }
1181
1182  /**
1183   * <p>
1184   * Create a {@link MasterMetaBootstrap} instance.
1185   * </p>
1186   * <p>
1187   * Will be overridden in tests.
1188   * </p>
1189   */
1190  @VisibleForTesting
1191  protected MasterMetaBootstrap createMetaBootstrap() {
1192    // We put this out here in a method so can do a Mockito.spy and stub it out
1193    // w/ a mocked up MasterMetaBootstrap.
1194    return new MasterMetaBootstrap(this);
1195  }
1196
1197  /**
1198   * <p>
1199   * Create a {@link ServerManager} instance.
1200   * </p>
1201   * <p>
1202   * Will be overridden in tests.
1203   * </p>
1204   */
1205  @VisibleForTesting
1206  protected ServerManager createServerManager(final MasterServices master) throws IOException {
1207    // We put this out here in a method so can do a Mockito.spy and stub it out
1208    // w/ a mocked up ServerManager.
1209    setupClusterConnection();
1210    return new ServerManager(master);
1211  }
1212
1213  private void waitForRegionServers(final MonitoredTask status)
1214      throws IOException, InterruptedException {
1215    this.serverManager.waitForRegionServers(status);
1216  }
1217
1218  // Will be overridden in tests
1219  @VisibleForTesting
1220  protected void initClusterSchemaService() throws IOException, InterruptedException {
1221    this.clusterSchemaService = new ClusterSchemaServiceImpl(this);
1222    this.clusterSchemaService.startAsync();
1223    try {
1224      this.clusterSchemaService.awaitRunning(getConfiguration().getInt(
1225        HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS,
1226        DEFAULT_HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS), TimeUnit.SECONDS);
1227    } catch (TimeoutException toe) {
1228      throw new IOException("Timedout starting ClusterSchemaService", toe);
1229    }
1230  }
1231
1232  private void initQuotaManager() throws IOException {
1233    MasterQuotaManager quotaManager = new MasterQuotaManager(this);
1234    quotaManager.start();
1235    this.quotaManager = quotaManager;
1236  }
1237
1238  private SpaceQuotaSnapshotNotifier createQuotaSnapshotNotifier() {
1239    SpaceQuotaSnapshotNotifier notifier =
1240        SpaceQuotaSnapshotNotifierFactory.getInstance().create(getConfiguration());
1241    return notifier;
1242  }
1243
1244  boolean isCatalogJanitorEnabled() {
1245    return catalogJanitorChore != null ?
1246      catalogJanitorChore.getEnabled() : false;
1247  }
1248
1249  boolean isCleanerChoreEnabled() {
1250    boolean hfileCleanerFlag = true, logCleanerFlag = true;
1251
1252    if (hfileCleaner != null) {
1253      hfileCleanerFlag = hfileCleaner.getEnabled();
1254    }
1255
1256    if (logCleaner != null) {
1257      logCleanerFlag = logCleaner.getEnabled();
1258    }
1259
1260    return (hfileCleanerFlag && logCleanerFlag);
1261  }
1262
1263  @Override
1264  public TableDescriptors getTableDescriptors() {
1265    return this.tableDescriptors;
1266  }
1267
1268  @Override
1269  public ServerManager getServerManager() {
1270    return this.serverManager;
1271  }
1272
1273  @Override
1274  public MasterFileSystem getMasterFileSystem() {
1275    return this.fileSystemManager;
1276  }
1277
1278  @Override
1279  public MasterWalManager getMasterWalManager() {
1280    return this.walManager;
1281  }
1282
1283  @Override
1284  public TableStateManager getTableStateManager() {
1285    return tableStateManager;
1286  }
1287
1288  /*
1289   * Start up all services. If any of these threads gets an unhandled exception
1290   * then they just die with a logged message.  This should be fine because
1291   * in general, we do not expect the master to get such unhandled exceptions
1292   *  as OOMEs; it should be lightly loaded. See what HRegionServer does if
1293   *  need to install an unexpected exception handler.
1294   */
1295  private void startServiceThreads() throws IOException{
1296   // Start the executor service pools
1297   this.executorService.startExecutorService(ExecutorType.MASTER_OPEN_REGION,
1298      conf.getInt("hbase.master.executor.openregion.threads", 5));
1299   this.executorService.startExecutorService(ExecutorType.MASTER_CLOSE_REGION,
1300      conf.getInt("hbase.master.executor.closeregion.threads", 5));
1301   this.executorService.startExecutorService(ExecutorType.MASTER_SERVER_OPERATIONS,
1302      conf.getInt("hbase.master.executor.serverops.threads", 5));
1303   this.executorService.startExecutorService(ExecutorType.MASTER_META_SERVER_OPERATIONS,
1304      conf.getInt("hbase.master.executor.meta.serverops.threads", 5));
1305   this.executorService.startExecutorService(ExecutorType.M_LOG_REPLAY_OPS,
1306      conf.getInt("hbase.master.executor.logreplayops.threads", 10));
1307
1308   // We depend on there being only one instance of this executor running
1309   // at a time.  To do concurrency, would need fencing of enable/disable of
1310   // tables.
1311   // Any time changing this maxThreads to > 1, pls see the comment at
1312   // AccessController#postCompletedCreateTableAction
1313   this.executorService.startExecutorService(ExecutorType.MASTER_TABLE_OPERATIONS, 1);
1314   startProcedureExecutor();
1315
1316   // Start log cleaner thread
1317   int cleanerInterval = conf.getInt("hbase.master.cleaner.interval", 600 * 1000);
1318   this.logCleaner =
1319      new LogCleaner(cleanerInterval,
1320         this, conf, getMasterWalManager().getFileSystem(),
1321         getMasterWalManager().getOldLogDir());
1322    getChoreService().scheduleChore(logCleaner);
1323
1324   //start the hfile archive cleaner thread
1325    Path archiveDir = HFileArchiveUtil.getArchivePath(conf);
1326    Map<String, Object> params = new HashMap<>();
1327    params.put(MASTER, this);
1328    this.hfileCleaner = new HFileCleaner(cleanerInterval, this, conf, getMasterFileSystem()
1329        .getFileSystem(), archiveDir, params);
1330    getChoreService().scheduleChore(hfileCleaner);
1331    serviceStarted = true;
1332    if (LOG.isTraceEnabled()) {
1333      LOG.trace("Started service threads");
1334    }
1335    // Start replication zk node cleaner
1336    try {
1337      replicationZKNodeCleanerChore = new ReplicationZKNodeCleanerChore(this, cleanerInterval,
1338          new ReplicationZKNodeCleaner(this.conf, this.getZooKeeper(), this));
1339      getChoreService().scheduleChore(replicationZKNodeCleanerChore);
1340    } catch (Exception e) {
1341      LOG.error("start replicationZKNodeCleanerChore failed", e);
1342    }
1343  }
1344
1345  @Override
1346  protected void stopServiceThreads() {
1347    if (masterJettyServer != null) {
1348      LOG.info("Stopping master jetty server");
1349      try {
1350        masterJettyServer.stop();
1351      } catch (Exception e) {
1352        LOG.error("Failed to stop master jetty server", e);
1353      }
1354    }
1355    super.stopServiceThreads();
1356    stopChores();
1357
1358    if (LOG.isDebugEnabled()) {
1359      LOG.debug("Stopping service threads");
1360    }
1361
1362    // Clean up and close up shop
1363    if (this.logCleaner != null) this.logCleaner.cancel(true);
1364    if (this.hfileCleaner != null) this.hfileCleaner.cancel(true);
1365    if (this.replicationZKNodeCleanerChore != null) this.replicationZKNodeCleanerChore.cancel(true);
1366    if (this.quotaManager != null) this.quotaManager.stop();
1367
1368    if (this.activeMasterManager != null) this.activeMasterManager.stop();
1369    if (this.serverManager != null) this.serverManager.stop();
1370    if (this.assignmentManager != null) this.assignmentManager.stop();
1371
1372    stopProcedureExecutor();
1373
1374    if (this.walManager != null) this.walManager.stop();
1375    if (this.fileSystemManager != null) this.fileSystemManager.stop();
1376    if (this.mpmHost != null) this.mpmHost.stop("server shutting down.");
1377  }
1378
1379  private void createProcedureExecutor() throws IOException {
1380    MasterProcedureEnv procEnv = new MasterProcedureEnv(this);
1381    procedureStore =
1382      new WALProcedureStore(conf, new MasterProcedureEnv.WALStoreLeaseRecovery(this));
1383    procedureStore.registerListener(new ProcedureStoreListener() {
1384
1385      @Override
1386      public void abortProcess() {
1387        abort("The Procedure Store lost the lease", null);
1388      }
1389    });
1390    MasterProcedureScheduler procedureScheduler = procEnv.getProcedureScheduler();
1391    procedureExecutor = new ProcedureExecutor<>(conf, procEnv, procedureStore, procedureScheduler);
1392    configurationManager.registerObserver(procEnv);
1393
1394    int cpus = Runtime.getRuntime().availableProcessors();
1395    final int numThreads = conf.getInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, Math.max(
1396      (cpus > 0 ? cpus / 4 : 0), MasterProcedureConstants.DEFAULT_MIN_MASTER_PROCEDURE_THREADS));
1397    final int urgentWorkers = conf
1398        .getInt(MasterProcedureConstants.MASTER_URGENT_PROCEDURE_THREADS,
1399            MasterProcedureConstants.DEFAULT_MASTER_URGENT_PROCEDURE_THREADS);
1400    final boolean abortOnCorruption =
1401      conf.getBoolean(MasterProcedureConstants.EXECUTOR_ABORT_ON_CORRUPTION,
1402        MasterProcedureConstants.DEFAULT_EXECUTOR_ABORT_ON_CORRUPTION);
1403    procedureStore.start(numThreads);
1404    // Just initialize it but do not start the workers, we will start the workers later by calling
1405    // startProcedureExecutor. See the javadoc for finishActiveMasterInitialization for more
1406    // details.
1407    procedureExecutor.init(numThreads, urgentWorkers, abortOnCorruption);
1408    procEnv.getRemoteDispatcher().start();
1409  }
1410
1411  private void startProcedureExecutor() throws IOException {
1412    procedureExecutor.startWorkers();
1413  }
1414
1415  private void stopProcedureExecutor() {
1416    if (procedureExecutor != null) {
1417      configurationManager.deregisterObserver(procedureExecutor.getEnvironment());
1418      procedureExecutor.getEnvironment().getRemoteDispatcher().stop();
1419      procedureExecutor.stop();
1420      procedureExecutor.join();
1421      procedureExecutor = null;
1422    }
1423
1424    if (procedureStore != null) {
1425      procedureStore.stop(isAborted());
1426      procedureStore = null;
1427    }
1428    if (this.regionServerTracker != null) {
1429      this.regionServerTracker.stop();
1430    }
1431  }
1432
1433  private void stopChores() {
1434    if (this.expiredMobFileCleanerChore != null) {
1435      this.expiredMobFileCleanerChore.cancel(true);
1436    }
1437    if (this.mobCompactChore != null) {
1438      this.mobCompactChore.cancel(true);
1439    }
1440    if (this.balancerChore != null) {
1441      this.balancerChore.cancel(true);
1442    }
1443    if (this.normalizerChore != null) {
1444      this.normalizerChore.cancel(true);
1445    }
1446    if (this.clusterStatusChore != null) {
1447      this.clusterStatusChore.cancel(true);
1448    }
1449    if (this.catalogJanitorChore != null) {
1450      this.catalogJanitorChore.cancel(true);
1451    }
1452    if (this.clusterStatusPublisherChore != null){
1453      clusterStatusPublisherChore.cancel(true);
1454    }
1455    if (this.mobCompactThread != null) {
1456      this.mobCompactThread.close();
1457    }
1458
1459    if (this.quotaObserverChore != null) {
1460      quotaObserverChore.cancel();
1461    }
1462    if (this.snapshotQuotaChore != null) {
1463      snapshotQuotaChore.cancel();
1464    }
1465  }
1466
1467  /**
1468   * @return Get remote side's InetAddress
1469   */
1470  InetAddress getRemoteInetAddress(final int port,
1471      final long serverStartCode) throws UnknownHostException {
1472    // Do it out here in its own little method so can fake an address when
1473    // mocking up in tests.
1474    InetAddress ia = RpcServer.getRemoteIp();
1475
1476    // The call could be from the local regionserver,
1477    // in which case, there is no remote address.
1478    if (ia == null && serverStartCode == startcode) {
1479      InetSocketAddress isa = rpcServices.getSocketAddress();
1480      if (isa != null && isa.getPort() == port) {
1481        ia = isa.getAddress();
1482      }
1483    }
1484    return ia;
1485  }
1486
1487  /**
1488   * @return Maximum time we should run balancer for
1489   */
1490  private int getMaxBalancingTime() {
1491    int maxBalancingTime = getConfiguration().getInt(HConstants.HBASE_BALANCER_MAX_BALANCING, -1);
1492    if (maxBalancingTime == -1) {
1493      // if max balancing time isn't set, defaulting it to period time
1494      maxBalancingTime = getConfiguration().getInt(HConstants.HBASE_BALANCER_PERIOD,
1495        HConstants.DEFAULT_HBASE_BALANCER_PERIOD);
1496    }
1497    return maxBalancingTime;
1498  }
1499
1500  /**
1501   * @return Maximum number of regions in transition
1502   */
1503  private int getMaxRegionsInTransition() {
1504    int numRegions = this.assignmentManager.getRegionStates().getRegionAssignments().size();
1505    return Math.max((int) Math.floor(numRegions * this.maxRitPercent), 1);
1506  }
1507
1508  /**
1509   * It first sleep to the next balance plan start time. Meanwhile, throttling by the max
1510   * number regions in transition to protect availability.
1511   * @param nextBalanceStartTime The next balance plan start time
1512   * @param maxRegionsInTransition max number of regions in transition
1513   * @param cutoffTime when to exit balancer
1514   */
1515  private void balanceThrottling(long nextBalanceStartTime, int maxRegionsInTransition,
1516      long cutoffTime) {
1517    boolean interrupted = false;
1518
1519    // Sleep to next balance plan start time
1520    // But if there are zero regions in transition, it can skip sleep to speed up.
1521    while (!interrupted && System.currentTimeMillis() < nextBalanceStartTime
1522        && this.assignmentManager.getRegionStates().hasRegionsInTransition()) {
1523      try {
1524        Thread.sleep(100);
1525      } catch (InterruptedException ie) {
1526        interrupted = true;
1527      }
1528    }
1529
1530    // Throttling by max number regions in transition
1531    while (!interrupted
1532        && maxRegionsInTransition > 0
1533        && this.assignmentManager.getRegionStates().getRegionsInTransitionCount()
1534        >= maxRegionsInTransition && System.currentTimeMillis() <= cutoffTime) {
1535      try {
1536        // sleep if the number of regions in transition exceeds the limit
1537        Thread.sleep(100);
1538      } catch (InterruptedException ie) {
1539        interrupted = true;
1540      }
1541    }
1542
1543    if (interrupted) Thread.currentThread().interrupt();
1544  }
1545
1546  public boolean balance() throws IOException {
1547    return balance(false);
1548  }
1549
1550  public boolean balance(boolean force) throws IOException {
1551    // if master not initialized, don't run balancer.
1552    if (!isInitialized()) {
1553      LOG.debug("Master has not been initialized, don't run balancer.");
1554      return false;
1555    }
1556
1557    if (isInMaintenanceMode()) {
1558      LOG.info("Master is in maintenanceMode mode, don't run balancer.");
1559      return false;
1560    }
1561
1562    int maxRegionsInTransition = getMaxRegionsInTransition();
1563    synchronized (this.balancer) {
1564      // If balance not true, don't run balancer.
1565      if (!this.loadBalancerTracker.isBalancerOn()) return false;
1566        // Only allow one balance run at at time.
1567      if (this.assignmentManager.hasRegionsInTransition()) {
1568        List<RegionStateNode> regionsInTransition = assignmentManager.getRegionsInTransition();
1569        // if hbase:meta region is in transition, result of assignment cannot be recorded
1570        // ignore the force flag in that case
1571        boolean metaInTransition = assignmentManager.isMetaRegionInTransition();
1572        String prefix = force && !metaInTransition ? "R" : "Not r";
1573        List<RegionStateNode> toPrint = regionsInTransition;
1574        int max = 5;
1575        boolean truncated = false;
1576        if (regionsInTransition.size() > max) {
1577          toPrint = regionsInTransition.subList(0, max);
1578          truncated = true;
1579        }
1580        LOG.info(prefix + "unning balancer because " + regionsInTransition.size() +
1581          " region(s) in transition: " + toPrint + (truncated? "(truncated list)": ""));
1582        if (!force || metaInTransition) return false;
1583      }
1584      if (this.serverManager.areDeadServersInProgress()) {
1585        LOG.info("Not running balancer because processing dead regionserver(s): " +
1586          this.serverManager.getDeadServers());
1587        return false;
1588      }
1589      Map<ServerName, ServerMetrics> onlineServers = serverManager.getOnlineServers();
1590      int regionNotOnOnlineServer = 0;
1591      for (RegionState regionState : assignmentManager.getRegionStates().getRegionStates()) {
1592        if (regionState.isOpened() && !onlineServers
1593            .containsKey(regionState.getServerName())) {
1594          LOG.warn("{} 's server is not in the online server list.", regionState);
1595          regionNotOnOnlineServer++;
1596        }
1597      }
1598      if (regionNotOnOnlineServer > 0) {
1599        LOG.info("Not running balancer because {} regions found not on an online server",
1600            regionNotOnOnlineServer);
1601        return false;
1602      }
1603
1604      if (this.cpHost != null) {
1605        try {
1606          if (this.cpHost.preBalance()) {
1607            LOG.debug("Coprocessor bypassing balancer request");
1608            return false;
1609          }
1610        } catch (IOException ioe) {
1611          LOG.error("Error invoking master coprocessor preBalance()", ioe);
1612          return false;
1613        }
1614      }
1615
1616      boolean isByTable = getConfiguration().getBoolean("hbase.master.loadbalance.bytable", false);
1617      Map<TableName, Map<ServerName, List<RegionInfo>>> assignmentsByTable =
1618        this.assignmentManager.getRegionStates().getAssignmentsByTable(!isByTable);
1619
1620      List<RegionPlan> plans = new ArrayList<>();
1621
1622      //Give the balancer the current cluster state.
1623      this.balancer.setClusterMetrics(getClusterMetricsWithoutCoprocessor());
1624      this.balancer.setClusterLoad(assignmentsByTable);
1625
1626      for (Map<ServerName, List<RegionInfo>> serverMap : assignmentsByTable.values()) {
1627        serverMap.keySet().removeAll(this.serverManager.getDrainingServersList());
1628      }
1629      for (Entry<TableName, Map<ServerName, List<RegionInfo>>> e : assignmentsByTable.entrySet()) {
1630        List<RegionPlan> partialPlans = this.balancer.balanceCluster(e.getKey(), e.getValue());
1631        if (partialPlans != null) plans.addAll(partialPlans);
1632      }
1633
1634      long balanceStartTime = System.currentTimeMillis();
1635      long cutoffTime = balanceStartTime + this.maxBlancingTime;
1636      int rpCount = 0;  // number of RegionPlans balanced so far
1637      if (plans != null && !plans.isEmpty()) {
1638        int balanceInterval = this.maxBlancingTime / plans.size();
1639        LOG.info("Balancer plans size is " + plans.size() + ", the balance interval is "
1640            + balanceInterval + " ms, and the max number regions in transition is "
1641            + maxRegionsInTransition);
1642
1643        for (RegionPlan plan: plans) {
1644          LOG.info("balance " + plan);
1645          //TODO: bulk assign
1646          try {
1647            this.assignmentManager.moveAsync(plan);
1648          } catch (HBaseIOException hioe) {
1649            //should ignore failed plans here, avoiding the whole balance plans be aborted
1650            //later calls of balance() can fetch up the failed and skipped plans
1651            LOG.warn("Failed balance plan: {}, just skip it", plan, hioe);
1652          }
1653          //rpCount records balance plans processed, does not care if a plan succeeds
1654          rpCount++;
1655
1656          balanceThrottling(balanceStartTime + rpCount * balanceInterval, maxRegionsInTransition,
1657            cutoffTime);
1658
1659          // if performing next balance exceeds cutoff time, exit the loop
1660          if (rpCount < plans.size() && System.currentTimeMillis() > cutoffTime) {
1661            // TODO: After balance, there should not be a cutoff time (keeping it as
1662            // a security net for now)
1663            LOG.debug("No more balancing till next balance run; maxBalanceTime="
1664                + this.maxBlancingTime);
1665            break;
1666          }
1667        }
1668      }
1669
1670      if (this.cpHost != null) {
1671        try {
1672          this.cpHost.postBalance(rpCount < plans.size() ? plans.subList(0, rpCount) : plans);
1673        } catch (IOException ioe) {
1674          // balancing already succeeded so don't change the result
1675          LOG.error("Error invoking master coprocessor postBalance()", ioe);
1676        }
1677      }
1678    }
1679    // If LoadBalancer did not generate any plans, it means the cluster is already balanced.
1680    // Return true indicating a success.
1681    return true;
1682  }
1683
1684  @Override
1685  @VisibleForTesting
1686  public RegionNormalizer getRegionNormalizer() {
1687    return this.normalizer;
1688  }
1689
1690  /**
1691   * Perform normalization of cluster (invoked by {@link RegionNormalizerChore}).
1692   *
1693   * @return true if normalization step was performed successfully, false otherwise
1694   *    (specifically, if HMaster hasn't been initialized properly or normalization
1695   *    is globally disabled)
1696   */
1697  public boolean normalizeRegions() throws IOException {
1698    if (!isInitialized()) {
1699      LOG.debug("Master has not been initialized, don't run region normalizer.");
1700      return false;
1701    }
1702    if (this.getServerManager().isClusterShutdown()) {
1703      LOG.info("Cluster is shutting down, don't run region normalizer.");
1704      return false;
1705    }
1706    if (isInMaintenanceMode()) {
1707      LOG.info("Master is in maintenance mode, don't run region normalizer.");
1708      return false;
1709    }
1710    if (!this.regionNormalizerTracker.isNormalizerOn()) {
1711      LOG.debug("Region normalization is disabled, don't run region normalizer.");
1712      return false;
1713    }
1714
1715    synchronized (this.normalizer) {
1716      // Don't run the normalizer concurrently
1717      List<TableName> allEnabledTables = new ArrayList<>(
1718        this.tableStateManager.getTablesInStates(TableState.State.ENABLED));
1719
1720      Collections.shuffle(allEnabledTables);
1721
1722      for (TableName table : allEnabledTables) {
1723        if (isInMaintenanceMode()) {
1724          LOG.debug("Master is in maintenance mode, stop running region normalizer.");
1725          return false;
1726        }
1727
1728        TableDescriptor tblDesc = getTableDescriptors().get(table);
1729        if (table.isSystemTable() || (tblDesc != null &&
1730            !tblDesc.isNormalizationEnabled())) {
1731          LOG.trace("Skipping normalization for {}, as it's either system"
1732              + " table or doesn't have auto normalization turned on", table);
1733          continue;
1734        }
1735        List<NormalizationPlan> plans = this.normalizer.computePlanForTable(table);
1736        if (plans != null) {
1737          for (NormalizationPlan plan : plans) {
1738            plan.execute(clusterConnection.getAdmin());
1739            if (plan.getType() == PlanType.SPLIT) {
1740              splitPlanCount++;
1741            } else if (plan.getType() == PlanType.MERGE) {
1742              mergePlanCount++;
1743            }
1744          }
1745        }
1746      }
1747    }
1748    // If Region did not generate any plans, it means the cluster is already balanced.
1749    // Return true indicating a success.
1750    return true;
1751  }
1752
1753  /**
1754   * @return Client info for use as prefix on an audit log string; who did an action
1755   */
1756  @Override
1757  public String getClientIdAuditPrefix() {
1758    return "Client=" + RpcServer.getRequestUserName().orElse(null)
1759        + "/" + RpcServer.getRemoteAddress().orElse(null);
1760  }
1761
1762  /**
1763   * Switch for the background CatalogJanitor thread.
1764   * Used for testing.  The thread will continue to run.  It will just be a noop
1765   * if disabled.
1766   * @param b If false, the catalog janitor won't do anything.
1767   */
1768  public void setCatalogJanitorEnabled(final boolean b) {
1769    this.catalogJanitorChore.setEnabled(b);
1770  }
1771
1772  @Override
1773  public long mergeRegions(
1774      final RegionInfo[] regionsToMerge,
1775      final boolean forcible,
1776      final long nonceGroup,
1777      final long nonce) throws IOException {
1778    checkInitialized();
1779
1780    assert(regionsToMerge.length == 2);
1781
1782    TableName tableName = regionsToMerge[0].getTable();
1783    if (tableName == null || regionsToMerge[1].getTable() == null) {
1784      throw new UnknownRegionException ("Can't merge regions without table associated");
1785    }
1786
1787    if (!tableName.equals(regionsToMerge[1].getTable())) {
1788      throw new IOException (
1789        "Cannot merge regions from two different tables " + regionsToMerge[0].getTable()
1790        + " and " + regionsToMerge[1].getTable());
1791    }
1792
1793    if (RegionInfo.COMPARATOR.compare(regionsToMerge[0], regionsToMerge[1]) == 0) {
1794      throw new MergeRegionException(
1795        "Cannot merge a region to itself " + regionsToMerge[0] + ", " + regionsToMerge[1]);
1796    }
1797
1798    return MasterProcedureUtil.submitProcedure(
1799        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1800      @Override
1801      protected void run() throws IOException {
1802        getMaster().getMasterCoprocessorHost().preMergeRegions(regionsToMerge);
1803
1804        LOG.info(getClientIdAuditPrefix() + " Merge regions " +
1805          regionsToMerge[0].getEncodedName() + " and " + regionsToMerge[1].getEncodedName());
1806
1807        submitProcedure(new MergeTableRegionsProcedure(procedureExecutor.getEnvironment(),
1808          regionsToMerge, forcible));
1809
1810        getMaster().getMasterCoprocessorHost().postMergeRegions(regionsToMerge);
1811      }
1812
1813      @Override
1814      protected String getDescription() {
1815        return "MergeTableProcedure";
1816      }
1817    });
1818  }
1819
1820  @Override
1821  public long splitRegion(final RegionInfo regionInfo, final byte[] splitRow,
1822      final long nonceGroup, final long nonce)
1823  throws IOException {
1824    checkInitialized();
1825    return MasterProcedureUtil.submitProcedure(
1826        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1827      @Override
1828      protected void run() throws IOException {
1829        getMaster().getMasterCoprocessorHost().preSplitRegion(regionInfo.getTable(), splitRow);
1830        LOG.info(getClientIdAuditPrefix() + " split " + regionInfo.getRegionNameAsString());
1831
1832        // Execute the operation asynchronously
1833        submitProcedure(getAssignmentManager().createSplitProcedure(regionInfo, splitRow));
1834      }
1835
1836      @Override
1837      protected String getDescription() {
1838        return "SplitTableProcedure";
1839      }
1840    });
1841  }
1842
1843  // Public so can be accessed by tests. Blocks until move is done.
1844  // Replace with an async implementation from which you can get
1845  // a success/failure result.
1846  @VisibleForTesting
1847  public void move(final byte[] encodedRegionName, byte[] destServerName) throws HBaseIOException {
1848    RegionState regionState = assignmentManager.getRegionStates().
1849      getRegionState(Bytes.toString(encodedRegionName));
1850
1851    RegionInfo hri;
1852    if (regionState != null) {
1853      hri = regionState.getRegion();
1854    } else {
1855      throw new UnknownRegionException(Bytes.toStringBinary(encodedRegionName));
1856    }
1857
1858    ServerName dest;
1859    List<ServerName> exclude = hri.getTable().isSystemTable() ? assignmentManager.getExcludedServersForSystemTable()
1860        : new ArrayList<>(1);
1861    if (destServerName != null && exclude.contains(ServerName.valueOf(Bytes.toString(destServerName)))) {
1862      LOG.info(
1863          Bytes.toString(encodedRegionName) + " can not move to " + Bytes.toString(destServerName)
1864              + " because the server is in exclude list");
1865      destServerName = null;
1866    }
1867    if (destServerName == null || destServerName.length == 0) {
1868      LOG.info("Passed destination servername is null/empty so " +
1869        "choosing a server at random");
1870      exclude.add(regionState.getServerName());
1871      final List<ServerName> destServers = this.serverManager.createDestinationServersList(exclude);
1872      dest = balancer.randomAssignment(hri, destServers);
1873      if (dest == null) {
1874        LOG.debug("Unable to determine a plan to assign " + hri);
1875        return;
1876      }
1877    } else {
1878      ServerName candidate = ServerName.valueOf(Bytes.toString(destServerName));
1879      dest = balancer.randomAssignment(hri, Lists.newArrayList(candidate));
1880      if (dest == null) {
1881        LOG.debug("Unable to determine a plan to assign " + hri);
1882        return;
1883      }
1884      // TODO: What is this? I don't get it.
1885      if (dest.equals(serverName) && balancer instanceof BaseLoadBalancer
1886          && !((BaseLoadBalancer)balancer).shouldBeOnMaster(hri)) {
1887        // To avoid unnecessary region moving later by balancer. Don't put user
1888        // regions on master.
1889        LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1890          + " to avoid unnecessary region moving later by load balancer,"
1891          + " because it should not be on master");
1892        return;
1893      }
1894    }
1895
1896    if (dest.equals(regionState.getServerName())) {
1897      LOG.debug("Skipping move of region " + hri.getRegionNameAsString()
1898        + " because region already assigned to the same server " + dest + ".");
1899      return;
1900    }
1901
1902    // Now we can do the move
1903    RegionPlan rp = new RegionPlan(hri, regionState.getServerName(), dest);
1904    assert rp.getDestination() != null: rp.toString() + " " + dest;
1905
1906    try {
1907      checkInitialized();
1908      if (this.cpHost != null) {
1909        this.cpHost.preMove(hri, rp.getSource(), rp.getDestination());
1910      }
1911      // Warmup the region on the destination before initiating the move. this call
1912      // is synchronous and takes some time. doing it before the source region gets
1913      // closed
1914      serverManager.sendRegionWarmup(rp.getDestination(), hri);
1915
1916      LOG.info(getClientIdAuditPrefix() + " move " + rp + ", running balancer");
1917      Future<byte []> future = this.assignmentManager.moveAsync(rp);
1918      try {
1919        // Is this going to work? Will we throw exception on error?
1920        // TODO: CompletableFuture rather than this stunted Future.
1921        future.get();
1922      } catch (InterruptedException | ExecutionException e) {
1923        throw new HBaseIOException(e);
1924      }
1925      if (this.cpHost != null) {
1926        this.cpHost.postMove(hri, rp.getSource(), rp.getDestination());
1927      }
1928    } catch (IOException ioe) {
1929      if (ioe instanceof HBaseIOException) {
1930        throw (HBaseIOException)ioe;
1931      }
1932      throw new HBaseIOException(ioe);
1933    }
1934  }
1935
1936  @Override
1937  public long createTable(
1938      final TableDescriptor tableDescriptor,
1939      final byte [][] splitKeys,
1940      final long nonceGroup,
1941      final long nonce) throws IOException {
1942    checkInitialized();
1943
1944    String namespace = tableDescriptor.getTableName().getNamespaceAsString();
1945    this.clusterSchemaService.getNamespace(namespace);
1946
1947    RegionInfo[] newRegions = ModifyRegionUtils.createRegionInfos(tableDescriptor, splitKeys);
1948    sanityCheckTableDescriptor(tableDescriptor);
1949
1950    return MasterProcedureUtil.submitProcedure(
1951        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
1952      @Override
1953      protected void run() throws IOException {
1954        getMaster().getMasterCoprocessorHost().preCreateTable(tableDescriptor, newRegions);
1955
1956        LOG.info(getClientIdAuditPrefix() + " create " + tableDescriptor);
1957
1958        // TODO: We can handle/merge duplicate requests, and differentiate the case of
1959        //       TableExistsException by saying if the schema is the same or not.
1960        //
1961        // We need to wait for the procedure to potentially fail due to "prepare" sanity
1962        // checks. This will block only the beginning of the procedure. See HBASE-19953.
1963        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
1964        submitProcedure(new CreateTableProcedure(
1965            procedureExecutor.getEnvironment(), tableDescriptor, newRegions, latch));
1966        latch.await();
1967
1968        getMaster().getMasterCoprocessorHost().postCreateTable(tableDescriptor, newRegions);
1969      }
1970
1971      @Override
1972      protected String getDescription() {
1973        return "CreateTableProcedure";
1974      }
1975    });
1976  }
1977
1978  @Override
1979  public long createSystemTable(final TableDescriptor tableDescriptor) throws IOException {
1980    if (isStopped()) {
1981      throw new MasterNotRunningException();
1982    }
1983
1984    TableName tableName = tableDescriptor.getTableName();
1985    if (!(tableName.isSystemTable())) {
1986      throw new IllegalArgumentException(
1987        "Only system table creation can use this createSystemTable API");
1988    }
1989
1990    RegionInfo[] newRegions = ModifyRegionUtils.createRegionInfos(tableDescriptor, null);
1991
1992    LOG.info(getClientIdAuditPrefix() + " create " + tableDescriptor);
1993
1994    // This special create table is called locally to master.  Therefore, no RPC means no need
1995    // to use nonce to detect duplicated RPC call.
1996    long procId = this.procedureExecutor.submitProcedure(
1997      new CreateTableProcedure(procedureExecutor.getEnvironment(), tableDescriptor, newRegions));
1998
1999    return procId;
2000  }
2001
2002  /**
2003   * Checks whether the table conforms to some sane limits, and configured
2004   * values (compression, etc) work. Throws an exception if something is wrong.
2005   * @throws IOException
2006   */
2007  private void sanityCheckTableDescriptor(final TableDescriptor htd) throws IOException {
2008    final String CONF_KEY = "hbase.table.sanity.checks";
2009    boolean logWarn = false;
2010    if (!conf.getBoolean(CONF_KEY, true)) {
2011      logWarn = true;
2012    }
2013    String tableVal = htd.getValue(CONF_KEY);
2014    if (tableVal != null && !Boolean.valueOf(tableVal)) {
2015      logWarn = true;
2016    }
2017
2018    // check max file size
2019    long maxFileSizeLowerLimit = 2 * 1024 * 1024L; // 2M is the default lower limit
2020    long maxFileSize = htd.getMaxFileSize();
2021    if (maxFileSize < 0) {
2022      maxFileSize = conf.getLong(HConstants.HREGION_MAX_FILESIZE, maxFileSizeLowerLimit);
2023    }
2024    if (maxFileSize < conf.getLong("hbase.hregion.max.filesize.limit", maxFileSizeLowerLimit)) {
2025      String message = "MAX_FILESIZE for table descriptor or "
2026          + "\"hbase.hregion.max.filesize\" (" + maxFileSize
2027          + ") is too small, which might cause over splitting into unmanageable "
2028          + "number of regions.";
2029      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2030    }
2031
2032    // check flush size
2033    long flushSizeLowerLimit = 1024 * 1024L; // 1M is the default lower limit
2034    long flushSize = htd.getMemStoreFlushSize();
2035    if (flushSize < 0) {
2036      flushSize = conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, flushSizeLowerLimit);
2037    }
2038    if (flushSize < conf.getLong("hbase.hregion.memstore.flush.size.limit", flushSizeLowerLimit)) {
2039      String message = "MEMSTORE_FLUSHSIZE for table descriptor or "
2040          + "\"hbase.hregion.memstore.flush.size\" ("+flushSize+") is too small, which might cause"
2041          + " very frequent flushing.";
2042      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2043    }
2044
2045    // check that coprocessors and other specified plugin classes can be loaded
2046    try {
2047      checkClassLoading(conf, htd);
2048    } catch (Exception ex) {
2049      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, ex.getMessage(), null);
2050    }
2051
2052    // check compression can be loaded
2053    try {
2054      checkCompression(htd);
2055    } catch (IOException e) {
2056      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
2057    }
2058
2059    // check encryption can be loaded
2060    try {
2061      checkEncryption(conf, htd);
2062    } catch (IOException e) {
2063      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, e.getMessage(), e);
2064    }
2065    // Verify compaction policy
2066    try{
2067      checkCompactionPolicy(conf, htd);
2068    } catch(IOException e){
2069      warnOrThrowExceptionForFailure(false, CONF_KEY, e.getMessage(), e);
2070    }
2071    // check that we have at least 1 CF
2072    if (htd.getColumnFamilyCount() == 0) {
2073      String message = "Table should have at least one column family.";
2074      warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2075    }
2076
2077    for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
2078      if (hcd.getTimeToLive() <= 0) {
2079        String message = "TTL for column family " + hcd.getNameAsString() + " must be positive.";
2080        warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2081      }
2082
2083      // check blockSize
2084      if (hcd.getBlocksize() < 1024 || hcd.getBlocksize() > 16 * 1024 * 1024) {
2085        String message = "Block size for column family " + hcd.getNameAsString()
2086            + "  must be between 1K and 16MB.";
2087        warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2088      }
2089
2090      // check versions
2091      if (hcd.getMinVersions() < 0) {
2092        String message = "Min versions for column family " + hcd.getNameAsString()
2093          + "  must be positive.";
2094        warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2095      }
2096      // max versions already being checked
2097
2098      // HBASE-13776 Setting illegal versions for ColumnFamilyDescriptor
2099      //  does not throw IllegalArgumentException
2100      // check minVersions <= maxVerions
2101      if (hcd.getMinVersions() > hcd.getMaxVersions()) {
2102        String message = "Min versions for column family " + hcd.getNameAsString()
2103            + " must be less than the Max versions.";
2104        warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2105      }
2106
2107      // check replication scope
2108      checkReplicationScope(hcd);
2109
2110      // check data replication factor, it can be 0(default value) when user has not explicitly
2111      // set the value, in this case we use default replication factor set in the file system.
2112      if (hcd.getDFSReplication() < 0) {
2113        String message = "HFile Replication for column family " + hcd.getNameAsString()
2114            + "  must be greater than zero.";
2115        warnOrThrowExceptionForFailure(logWarn, CONF_KEY, message, null);
2116      }
2117
2118      // TODO: should we check coprocessors and encryption ?
2119    }
2120  }
2121
2122  private void checkReplicationScope(ColumnFamilyDescriptor hcd) throws IOException{
2123    // check replication scope
2124    WALProtos.ScopeType scop = WALProtos.ScopeType.valueOf(hcd.getScope());
2125    if (scop == null) {
2126      String message = "Replication scope for column family "
2127          + hcd.getNameAsString() + " is " + hcd.getScope() + " which is invalid.";
2128
2129      LOG.error(message);
2130      throw new DoNotRetryIOException(message);
2131    }
2132  }
2133
2134  private void checkCompactionPolicy(Configuration conf, TableDescriptor htd)
2135      throws IOException {
2136    // FIFO compaction has some requirements
2137    // Actually FCP ignores periodic major compactions
2138    String className = htd.getValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
2139    if (className == null) {
2140      className =
2141          conf.get(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY,
2142            ExploringCompactionPolicy.class.getName());
2143    }
2144
2145    int blockingFileCount = HStore.DEFAULT_BLOCKING_STOREFILE_COUNT;
2146    String sv = htd.getValue(HStore.BLOCKING_STOREFILES_KEY);
2147    if (sv != null) {
2148      blockingFileCount = Integer.parseInt(sv);
2149    } else {
2150      blockingFileCount = conf.getInt(HStore.BLOCKING_STOREFILES_KEY, blockingFileCount);
2151    }
2152
2153    for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
2154      String compactionPolicy =
2155          hcd.getConfigurationValue(DefaultStoreEngine.DEFAULT_COMPACTION_POLICY_CLASS_KEY);
2156      if (compactionPolicy == null) {
2157        compactionPolicy = className;
2158      }
2159      if (!compactionPolicy.equals(FIFOCompactionPolicy.class.getName())) {
2160        continue;
2161      }
2162      // FIFOCompaction
2163      String message = null;
2164
2165      // 1. Check TTL
2166      if (hcd.getTimeToLive() == ColumnFamilyDescriptorBuilder.DEFAULT_TTL) {
2167        message = "Default TTL is not supported for FIFO compaction";
2168        throw new IOException(message);
2169      }
2170
2171      // 2. Check min versions
2172      if (hcd.getMinVersions() > 0) {
2173        message = "MIN_VERSION > 0 is not supported for FIFO compaction";
2174        throw new IOException(message);
2175      }
2176
2177      // 3. blocking file count
2178      sv = hcd.getConfigurationValue(HStore.BLOCKING_STOREFILES_KEY);
2179      if (sv != null) {
2180        blockingFileCount = Integer.parseInt(sv);
2181      }
2182      if (blockingFileCount < 1000) {
2183        message =
2184            "Blocking file count '" + HStore.BLOCKING_STOREFILES_KEY + "' " + blockingFileCount
2185                + " is below recommended minimum of 1000 for column family "+ hcd.getNameAsString();
2186        throw new IOException(message);
2187      }
2188    }
2189  }
2190
2191  // HBASE-13350 - Helper method to log warning on sanity check failures if checks disabled.
2192  private static void warnOrThrowExceptionForFailure(boolean logWarn, String confKey,
2193      String message, Exception cause) throws IOException {
2194    if (!logWarn) {
2195      throw new DoNotRetryIOException(message + " Set " + confKey +
2196          " to false at conf or table descriptor if you want to bypass sanity checks", cause);
2197    }
2198    LOG.warn(message);
2199  }
2200
2201  private void startActiveMasterManager(int infoPort) throws KeeperException {
2202    String backupZNode = ZNodePaths.joinZNode(
2203      zooKeeper.znodePaths.backupMasterAddressesZNode, serverName.toString());
2204    /*
2205    * Add a ZNode for ourselves in the backup master directory since we
2206    * may not become the active master. If so, we want the actual active
2207    * master to know we are backup masters, so that it won't assign
2208    * regions to us if so configured.
2209    *
2210    * If we become the active master later, ActiveMasterManager will delete
2211    * this node explicitly.  If we crash before then, ZooKeeper will delete
2212    * this node for us since it is ephemeral.
2213    */
2214    LOG.info("Adding backup master ZNode " + backupZNode);
2215    if (!MasterAddressTracker.setMasterAddress(zooKeeper, backupZNode, serverName, infoPort)) {
2216      LOG.warn("Failed create of " + backupZNode + " by " + serverName);
2217    }
2218    this.activeMasterManager.setInfoPort(infoPort);
2219    int timeout = conf.getInt(HConstants.ZK_SESSION_TIMEOUT, HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
2220    // If we're a backup master, stall until a primary to write this address
2221    if (conf.getBoolean(HConstants.MASTER_TYPE_BACKUP, HConstants.DEFAULT_MASTER_TYPE_BACKUP)) {
2222      LOG.debug("HMaster started in backup mode. Stalling until master znode is written.");
2223      // This will only be a minute or so while the cluster starts up,
2224      // so don't worry about setting watches on the parent znode
2225      while (!activeMasterManager.hasActiveMaster()) {
2226        LOG.debug("Waiting for master address and cluster state znode to be written.");
2227        Threads.sleep(timeout);
2228      }
2229    }
2230    MonitoredTask status = TaskMonitor.get().createStatus("Master startup");
2231    status.setDescription("Master startup");
2232    try {
2233      if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) {
2234        finishActiveMasterInitialization(status);
2235      }
2236    } catch (Throwable t) {
2237      status.setStatus("Failed to become active: " + t.getMessage());
2238      LOG.error(HBaseMarkers.FATAL, "Failed to become active master", t);
2239      // HBASE-5680: Likely hadoop23 vs hadoop 20.x/1.x incompatibility
2240      if (t instanceof NoClassDefFoundError && t.getMessage().
2241          contains("org/apache/hadoop/hdfs/protocol/HdfsConstants$SafeModeAction")) {
2242        // improved error message for this special case
2243        abort("HBase is having a problem with its Hadoop jars.  You may need to recompile " +
2244          "HBase against Hadoop version " + org.apache.hadoop.util.VersionInfo.getVersion() +
2245          " or change your hadoop jars to start properly", t);
2246      } else {
2247        abort("Unhandled exception. Starting shutdown.", t);
2248      }
2249    } finally {
2250      status.cleanup();
2251    }
2252  }
2253
2254  private void checkCompression(final TableDescriptor htd)
2255  throws IOException {
2256    if (!this.masterCheckCompression) return;
2257    for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
2258      checkCompression(hcd);
2259    }
2260  }
2261
2262  private void checkCompression(final ColumnFamilyDescriptor hcd)
2263  throws IOException {
2264    if (!this.masterCheckCompression) return;
2265    CompressionTest.testCompression(hcd.getCompressionType());
2266    CompressionTest.testCompression(hcd.getCompactionCompressionType());
2267  }
2268
2269  private void checkEncryption(final Configuration conf, final TableDescriptor htd)
2270  throws IOException {
2271    if (!this.masterCheckEncryption) return;
2272    for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
2273      checkEncryption(conf, hcd);
2274    }
2275  }
2276
2277  private void checkEncryption(final Configuration conf, final ColumnFamilyDescriptor hcd)
2278  throws IOException {
2279    if (!this.masterCheckEncryption) return;
2280    EncryptionTest.testEncryption(conf, hcd.getEncryptionType(), hcd.getEncryptionKey());
2281  }
2282
2283  private void checkClassLoading(final Configuration conf, final TableDescriptor htd)
2284  throws IOException {
2285    RegionSplitPolicy.getSplitPolicyClass(htd, conf);
2286    RegionCoprocessorHost.testTableCoprocessorAttrs(conf, htd);
2287  }
2288
2289  private static boolean isCatalogTable(final TableName tableName) {
2290    return tableName.equals(TableName.META_TABLE_NAME);
2291  }
2292
2293  @Override
2294  public long deleteTable(
2295      final TableName tableName,
2296      final long nonceGroup,
2297      final long nonce) throws IOException {
2298    checkInitialized();
2299
2300    return MasterProcedureUtil.submitProcedure(
2301        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2302      @Override
2303      protected void run() throws IOException {
2304        getMaster().getMasterCoprocessorHost().preDeleteTable(tableName);
2305
2306        LOG.info(getClientIdAuditPrefix() + " delete " + tableName);
2307
2308        // TODO: We can handle/merge duplicate request
2309        //
2310        // We need to wait for the procedure to potentially fail due to "prepare" sanity
2311        // checks. This will block only the beginning of the procedure. See HBASE-19953.
2312        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
2313        submitProcedure(new DeleteTableProcedure(procedureExecutor.getEnvironment(),
2314            tableName, latch));
2315        latch.await();
2316
2317        getMaster().getMasterCoprocessorHost().postDeleteTable(tableName);
2318      }
2319
2320      @Override
2321      protected String getDescription() {
2322        return "DeleteTableProcedure";
2323      }
2324    });
2325  }
2326
2327  @Override
2328  public long truncateTable(
2329      final TableName tableName,
2330      final boolean preserveSplits,
2331      final long nonceGroup,
2332      final long nonce) throws IOException {
2333    checkInitialized();
2334
2335    return MasterProcedureUtil.submitProcedure(
2336        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2337      @Override
2338      protected void run() throws IOException {
2339        getMaster().getMasterCoprocessorHost().preTruncateTable(tableName);
2340
2341        LOG.info(getClientIdAuditPrefix() + " truncate " + tableName);
2342        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch(2, 0);
2343        submitProcedure(new TruncateTableProcedure(procedureExecutor.getEnvironment(),
2344            tableName, preserveSplits, latch));
2345        latch.await();
2346
2347        getMaster().getMasterCoprocessorHost().postTruncateTable(tableName);
2348      }
2349
2350      @Override
2351      protected String getDescription() {
2352        return "TruncateTableProcedure";
2353      }
2354    });
2355  }
2356
2357  @Override
2358  public long addColumn(final TableName tableName, final ColumnFamilyDescriptor column,
2359      final long nonceGroup, final long nonce) throws IOException {
2360    checkInitialized();
2361    checkTableExists(tableName);
2362
2363    return modifyTable(tableName, new TableDescriptorGetter() {
2364
2365      @Override
2366      public TableDescriptor get() throws IOException {
2367        TableDescriptor old = getTableDescriptors().get(tableName);
2368        if (old.hasColumnFamily(column.getName())) {
2369          throw new InvalidFamilyOperationException("Column family '" + column.getNameAsString()
2370              + "' in table '" + tableName + "' already exists so cannot be added");
2371        }
2372
2373        return TableDescriptorBuilder.newBuilder(old).setColumnFamily(column).build();
2374      }
2375    }, nonceGroup, nonce);
2376  }
2377
2378  /**
2379   * Implement to return TableDescriptor after pre-checks
2380   */
2381  protected interface TableDescriptorGetter {
2382    TableDescriptor get() throws IOException;
2383  }
2384
2385  @Override
2386  public long modifyColumn(final TableName tableName, final ColumnFamilyDescriptor descriptor,
2387      final long nonceGroup, final long nonce) throws IOException {
2388    checkInitialized();
2389    checkTableExists(tableName);
2390    return modifyTable(tableName, new TableDescriptorGetter() {
2391
2392      @Override
2393      public TableDescriptor get() throws IOException {
2394        TableDescriptor old = getTableDescriptors().get(tableName);
2395        if (!old.hasColumnFamily(descriptor.getName())) {
2396          throw new InvalidFamilyOperationException("Family '" + descriptor.getNameAsString()
2397              + "' does not exist, so it cannot be modified");
2398        }
2399
2400        return TableDescriptorBuilder.newBuilder(old).modifyColumnFamily(descriptor).build();
2401      }
2402    }, nonceGroup, nonce);
2403  }
2404
2405  @Override
2406  public long deleteColumn(final TableName tableName, final byte[] columnName,
2407      final long nonceGroup, final long nonce) throws IOException {
2408    checkInitialized();
2409    checkTableExists(tableName);
2410
2411    return modifyTable(tableName, new TableDescriptorGetter() {
2412
2413      @Override
2414      public TableDescriptor get() throws IOException {
2415        TableDescriptor old = getTableDescriptors().get(tableName);
2416
2417        if (!old.hasColumnFamily(columnName)) {
2418          throw new InvalidFamilyOperationException("Family '" + Bytes.toString(columnName)
2419              + "' does not exist, so it cannot be deleted");
2420        }
2421        if (old.getColumnFamilyCount() == 1) {
2422          throw new InvalidFamilyOperationException("Family '" + Bytes.toString(columnName)
2423              + "' is the only column family in the table, so it cannot be deleted");
2424        }
2425        return TableDescriptorBuilder.newBuilder(old).removeColumnFamily(columnName).build();
2426      }
2427    }, nonceGroup, nonce);
2428  }
2429
2430  @Override
2431  public long enableTable(final TableName tableName, final long nonceGroup, final long nonce)
2432      throws IOException {
2433    checkInitialized();
2434
2435    return MasterProcedureUtil.submitProcedure(
2436        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2437      @Override
2438      protected void run() throws IOException {
2439        getMaster().getMasterCoprocessorHost().preEnableTable(tableName);
2440
2441        // Normally, it would make sense for this authorization check to exist inside
2442        // AccessController, but because the authorization check is done based on internal state
2443        // (rather than explicit permissions) we'll do the check here instead of in the
2444        // coprocessor.
2445        MasterQuotaManager quotaManager = getMasterQuotaManager();
2446        if (quotaManager != null) {
2447          if (quotaManager.isQuotaInitialized()) {
2448            Quotas quotaForTable = QuotaUtil.getTableQuota(getConnection(), tableName);
2449            if (quotaForTable != null && quotaForTable.hasSpace()) {
2450              SpaceViolationPolicy policy = quotaForTable.getSpace().getViolationPolicy();
2451              if (SpaceViolationPolicy.DISABLE == policy) {
2452                throw new AccessDeniedException("Enabling the table '" + tableName
2453                    + "' is disallowed due to a violated space quota.");
2454              }
2455            }
2456          } else if (LOG.isTraceEnabled()) {
2457            LOG.trace("Unable to check for space quotas as the MasterQuotaManager is not enabled");
2458          }
2459        }
2460
2461        LOG.info(getClientIdAuditPrefix() + " enable " + tableName);
2462
2463        // Execute the operation asynchronously - client will check the progress of the operation
2464        // In case the request is from a <1.1 client before returning,
2465        // we want to make sure that the table is prepared to be
2466        // enabled (the table is locked and the table state is set).
2467        // Note: if the procedure throws exception, we will catch it and rethrow.
2468        final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createLatch();
2469        submitProcedure(new EnableTableProcedure(procedureExecutor.getEnvironment(),
2470            tableName, false, prepareLatch));
2471        prepareLatch.await();
2472
2473        getMaster().getMasterCoprocessorHost().postEnableTable(tableName);
2474      }
2475
2476      @Override
2477      protected String getDescription() {
2478        return "EnableTableProcedure";
2479      }
2480    });
2481  }
2482
2483  @Override
2484  public long disableTable(final TableName tableName, final long nonceGroup, final long nonce)
2485      throws IOException {
2486    checkInitialized();
2487
2488    return MasterProcedureUtil.submitProcedure(
2489        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2490      @Override
2491      protected void run() throws IOException {
2492        getMaster().getMasterCoprocessorHost().preDisableTable(tableName);
2493
2494        LOG.info(getClientIdAuditPrefix() + " disable " + tableName);
2495
2496        // Execute the operation asynchronously - client will check the progress of the operation
2497        // In case the request is from a <1.1 client before returning,
2498        // we want to make sure that the table is prepared to be
2499        // enabled (the table is locked and the table state is set).
2500        // Note: if the procedure throws exception, we will catch it and rethrow.
2501        //
2502        // We need to wait for the procedure to potentially fail due to "prepare" sanity
2503        // checks. This will block only the beginning of the procedure. See HBASE-19953.
2504        final ProcedurePrepareLatch prepareLatch = ProcedurePrepareLatch.createBlockingLatch();
2505        submitProcedure(new DisableTableProcedure(procedureExecutor.getEnvironment(),
2506            tableName, false, prepareLatch));
2507        prepareLatch.await();
2508
2509        getMaster().getMasterCoprocessorHost().postDisableTable(tableName);
2510      }
2511
2512      @Override
2513      protected String getDescription() {
2514        return "DisableTableProcedure";
2515      }
2516    });
2517  }
2518
2519  private long modifyTable(final TableName tableName,
2520      final TableDescriptorGetter newDescriptorGetter, final long nonceGroup, final long nonce)
2521      throws IOException {
2522    return MasterProcedureUtil
2523        .submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2524          @Override
2525          protected void run() throws IOException {
2526            TableDescriptor newDescriptor = newDescriptorGetter.get();
2527            sanityCheckTableDescriptor(newDescriptor);
2528            TableDescriptor oldDescriptor = getMaster().getTableDescriptors().get(tableName);
2529            getMaster().getMasterCoprocessorHost().preModifyTable(tableName, oldDescriptor,
2530              newDescriptor);
2531
2532            LOG.info(getClientIdAuditPrefix() + " modify " + tableName);
2533
2534            // Execute the operation synchronously - wait for the operation completes before
2535            // continuing.
2536            //
2537            // We need to wait for the procedure to potentially fail due to "prepare" sanity
2538            // checks. This will block only the beginning of the procedure. See HBASE-19953.
2539            ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
2540            submitProcedure(
2541              new ModifyTableProcedure(procedureExecutor.getEnvironment(), newDescriptor, latch));
2542            latch.await();
2543
2544            getMaster().getMasterCoprocessorHost().postModifyTable(tableName, oldDescriptor,
2545              newDescriptor);
2546          }
2547
2548          @Override
2549          protected String getDescription() {
2550            return "ModifyTableProcedure";
2551          }
2552        });
2553
2554  }
2555
2556  @Override
2557  public long modifyTable(final TableName tableName, final TableDescriptor newDescriptor,
2558      final long nonceGroup, final long nonce) throws IOException {
2559    checkInitialized();
2560    return modifyTable(tableName, new TableDescriptorGetter() {
2561      @Override
2562      public TableDescriptor get() throws IOException {
2563        return newDescriptor;
2564      }
2565    }, nonceGroup, nonce);
2566
2567  }
2568
2569  public long restoreSnapshot(final SnapshotDescription snapshotDesc,
2570      final long nonceGroup, final long nonce, final boolean restoreAcl) throws IOException {
2571    checkInitialized();
2572    getSnapshotManager().checkSnapshotSupport();
2573
2574    // Ensure namespace exists. Will throw exception if non-known NS.
2575    final TableName dstTable = TableName.valueOf(snapshotDesc.getTable());
2576    getClusterSchema().getNamespace(dstTable.getNamespaceAsString());
2577
2578    return MasterProcedureUtil.submitProcedure(
2579        new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
2580      @Override
2581      protected void run() throws IOException {
2582          setProcId(
2583            getSnapshotManager().restoreOrCloneSnapshot(snapshotDesc, getNonceKey(), restoreAcl));
2584      }
2585
2586      @Override
2587      protected String getDescription() {
2588        return "RestoreSnapshotProcedure";
2589      }
2590    });
2591  }
2592
2593  private void checkTableExists(final TableName tableName)
2594      throws IOException, TableNotFoundException {
2595    if (!MetaTableAccessor.tableExists(getConnection(), tableName)) {
2596      throw new TableNotFoundException(tableName);
2597    }
2598  }
2599
2600  @Override
2601  public void checkTableModifiable(final TableName tableName)
2602      throws IOException, TableNotFoundException, TableNotDisabledException {
2603    if (isCatalogTable(tableName)) {
2604      throw new IOException("Can't modify catalog tables");
2605    }
2606    checkTableExists(tableName);
2607    TableState ts = getTableStateManager().getTableState(tableName);
2608    if (!ts.isDisabled()) {
2609      throw new TableNotDisabledException("Not DISABLED; " + ts);
2610    }
2611  }
2612
2613  public ClusterMetrics getClusterMetricsWithoutCoprocessor() throws InterruptedIOException {
2614    return getClusterMetricsWithoutCoprocessor(EnumSet.allOf(Option.class));
2615  }
2616
2617  public ClusterMetrics getClusterMetricsWithoutCoprocessor(EnumSet<Option> options)
2618      throws InterruptedIOException {
2619    ClusterMetricsBuilder builder = ClusterMetricsBuilder.newBuilder();
2620    // given that hbase1 can't submit the request with Option,
2621    // we return all information to client if the list of Option is empty.
2622    if (options.isEmpty()) {
2623      options = EnumSet.allOf(Option.class);
2624    }
2625
2626    for (Option opt : options) {
2627      switch (opt) {
2628        case HBASE_VERSION: builder.setHBaseVersion(VersionInfo.getVersion()); break;
2629        case CLUSTER_ID: builder.setClusterId(getClusterId()); break;
2630        case MASTER: builder.setMasterName(getServerName()); break;
2631        case BACKUP_MASTERS: builder.setBackerMasterNames(getBackupMasters()); break;
2632        case LIVE_SERVERS: {
2633          if (serverManager != null) {
2634            builder.setLiveServerMetrics(serverManager.getOnlineServers().entrySet().stream()
2635              .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
2636          }
2637          break;
2638        }
2639        case DEAD_SERVERS: {
2640          if (serverManager != null) {
2641            builder.setDeadServerNames(new ArrayList<>(
2642              serverManager.getDeadServers().copyServerNames()));
2643          }
2644          break;
2645        }
2646        case MASTER_COPROCESSORS: {
2647          if (cpHost != null) {
2648            builder.setMasterCoprocessorNames(Arrays.asList(getMasterCoprocessors()));
2649          }
2650          break;
2651        }
2652        case REGIONS_IN_TRANSITION: {
2653          if (assignmentManager != null) {
2654            builder.setRegionsInTransition(assignmentManager.getRegionStates()
2655                .getRegionsStateInTransition());
2656          }
2657          break;
2658        }
2659        case BALANCER_ON: {
2660          if (loadBalancerTracker != null) {
2661            builder.setBalancerOn(loadBalancerTracker.isBalancerOn());
2662          }
2663          break;
2664        }
2665        case MASTER_INFO_PORT: {
2666          if (infoServer != null) {
2667            builder.setMasterInfoPort(infoServer.getPort());
2668          }
2669          break;
2670        }
2671      }
2672    }
2673    return builder.build();
2674  }
2675
2676  /**
2677   * @return cluster status
2678   */
2679  public ClusterMetrics getClusterMetrics() throws IOException {
2680    return getClusterMetrics(EnumSet.allOf(Option.class));
2681  }
2682
2683  public ClusterMetrics getClusterMetrics(EnumSet<Option> options) throws IOException {
2684    if (cpHost != null) {
2685      cpHost.preGetClusterMetrics();
2686    }
2687    ClusterMetrics status = getClusterMetricsWithoutCoprocessor(options);
2688    if (cpHost != null) {
2689      cpHost.postGetClusterMetrics(status);
2690    }
2691    return status;
2692  }
2693
2694  private List<ServerName> getBackupMasters() throws InterruptedIOException {
2695    // Build Set of backup masters from ZK nodes
2696    List<String> backupMasterStrings;
2697    try {
2698      backupMasterStrings = ZKUtil.listChildrenNoWatch(this.zooKeeper,
2699        this.zooKeeper.znodePaths.backupMasterAddressesZNode);
2700    } catch (KeeperException e) {
2701      LOG.warn(this.zooKeeper.prefix("Unable to list backup servers"), e);
2702      backupMasterStrings = null;
2703    }
2704
2705    List<ServerName> backupMasters = Collections.emptyList();
2706    if (backupMasterStrings != null && !backupMasterStrings.isEmpty()) {
2707      backupMasters = new ArrayList<>(backupMasterStrings.size());
2708      for (String s: backupMasterStrings) {
2709        try {
2710          byte [] bytes;
2711          try {
2712            bytes = ZKUtil.getData(this.zooKeeper, ZNodePaths.joinZNode(
2713                this.zooKeeper.znodePaths.backupMasterAddressesZNode, s));
2714          } catch (InterruptedException e) {
2715            throw new InterruptedIOException();
2716          }
2717          if (bytes != null) {
2718            ServerName sn;
2719            try {
2720              sn = ProtobufUtil.parseServerNameFrom(bytes);
2721            } catch (DeserializationException e) {
2722              LOG.warn("Failed parse, skipping registering backup server", e);
2723              continue;
2724            }
2725            backupMasters.add(sn);
2726          }
2727        } catch (KeeperException e) {
2728          LOG.warn(this.zooKeeper.prefix("Unable to get information about " +
2729                   "backup servers"), e);
2730        }
2731      }
2732      Collections.sort(backupMasters, new Comparator<ServerName>() {
2733        @Override
2734        public int compare(ServerName s1, ServerName s2) {
2735          return s1.getServerName().compareTo(s2.getServerName());
2736        }});
2737    }
2738    return backupMasters;
2739  }
2740
2741  /**
2742   * The set of loaded coprocessors is stored in a static set. Since it's
2743   * statically allocated, it does not require that HMaster's cpHost be
2744   * initialized prior to accessing it.
2745   * @return a String representation of the set of names of the loaded coprocessors.
2746   */
2747  public static String getLoadedCoprocessors() {
2748    return CoprocessorHost.getLoadedCoprocessors().toString();
2749  }
2750
2751  /**
2752   * @return timestamp in millis when HMaster was started.
2753   */
2754  public long getMasterStartTime() {
2755    return startcode;
2756  }
2757
2758  /**
2759   * @return timestamp in millis when HMaster became the active master.
2760   */
2761  public long getMasterActiveTime() {
2762    return masterActiveTime;
2763  }
2764
2765  /**
2766   * @return timestamp in millis when HMaster finished becoming the active master
2767   */
2768  public long getMasterFinishedInitializationTime() {
2769    return masterFinishedInitializationTime;
2770  }
2771
2772  public int getNumWALFiles() {
2773    return procedureStore != null ? procedureStore.getActiveLogs().size() : 0;
2774  }
2775
2776  public WALProcedureStore getWalProcedureStore() {
2777    return procedureStore;
2778  }
2779
2780  public int getRegionServerInfoPort(final ServerName sn) {
2781    int port = this.serverManager.getInfoPort(sn);
2782    return port == 0 ? conf.getInt(HConstants.REGIONSERVER_INFO_PORT,
2783      HConstants.DEFAULT_REGIONSERVER_INFOPORT) : port;
2784  }
2785
2786  @Override
2787  public String getRegionServerVersion(final ServerName sn) {
2788    // Will return 0 if the server is not online to prevent move system region to unknown version
2789    // RS.
2790    int versionNumber = this.serverManager.getServerVersion(sn);
2791    return VersionInfoUtil.versionNumberToString(versionNumber);
2792  }
2793
2794  @Override
2795  public void checkIfShouldMoveSystemRegionAsync() {
2796    assignmentManager.checkIfShouldMoveSystemRegionAsync();
2797  }
2798
2799  /**
2800   * @return array of coprocessor SimpleNames.
2801   */
2802  public String[] getMasterCoprocessors() {
2803    Set<String> masterCoprocessors = getMasterCoprocessorHost().getCoprocessors();
2804    return masterCoprocessors.toArray(new String[masterCoprocessors.size()]);
2805  }
2806
2807  @Override
2808  public void abort(String reason, Throwable cause) {
2809    if (isAborted() || isStopped()) {
2810      return;
2811    }
2812    if (cpHost != null) {
2813      // HBASE-4014: dump a list of loaded coprocessors.
2814      LOG.error(HBaseMarkers.FATAL, "Master server abort: loaded coprocessors are: " +
2815          getLoadedCoprocessors());
2816    }
2817    String msg = "***** ABORTING master " + this + ": " + reason + " *****";
2818    if (cause != null) {
2819      LOG.error(HBaseMarkers.FATAL, msg, cause);
2820    } else {
2821      LOG.error(HBaseMarkers.FATAL, msg);
2822    }
2823
2824    try {
2825      stopMaster();
2826    } catch (IOException e) {
2827      LOG.error("Exception occurred while stopping master", e);
2828    }
2829  }
2830
2831  @Override
2832  public ZKWatcher getZooKeeper() {
2833    return zooKeeper;
2834  }
2835
2836  @Override
2837  public MasterCoprocessorHost getMasterCoprocessorHost() {
2838    return cpHost;
2839  }
2840
2841  @Override
2842  public MasterQuotaManager getMasterQuotaManager() {
2843    return quotaManager;
2844  }
2845
2846  @Override
2847  public ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
2848    return procedureExecutor;
2849  }
2850
2851  @Override
2852  public ServerName getServerName() {
2853    return this.serverName;
2854  }
2855
2856  @Override
2857  public AssignmentManager getAssignmentManager() {
2858    return this.assignmentManager;
2859  }
2860
2861  @Override
2862  public CatalogJanitor getCatalogJanitor() {
2863    return this.catalogJanitorChore;
2864  }
2865
2866  public MemoryBoundedLogMessageBuffer getRegionServerFatalLogBuffer() {
2867    return rsFatals;
2868  }
2869
2870  /**
2871   * Shutdown the cluster.
2872   * Master runs a coordinated stop of all RegionServers and then itself.
2873   */
2874  public void shutdown() throws IOException {
2875    if (cpHost != null) {
2876      cpHost.preShutdown();
2877    }
2878    // Tell the servermanager cluster shutdown has been called. This makes it so when Master is
2879    // last running server, it'll stop itself. Next, we broadcast the cluster shutdown by setting
2880    // the cluster status as down. RegionServers will notice this change in state and will start
2881    // shutting themselves down. When last has exited, Master can go down.
2882    if (this.serverManager != null) {
2883      this.serverManager.shutdownCluster();
2884    }
2885    if (this.clusterStatusTracker != null) {
2886      try {
2887        this.clusterStatusTracker.setClusterDown();
2888      } catch (KeeperException e) {
2889        LOG.error("ZooKeeper exception trying to set cluster as down in ZK", e);
2890      }
2891    }
2892    // Stop the procedure executor. Will stop any ongoing assign, unassign, server crash etc.,
2893    // processing so we can go down.
2894    if (this.procedureExecutor != null) {
2895      this.procedureExecutor.stop();
2896    }
2897    // Shutdown our cluster connection. This will kill any hosted RPCs that might be going on;
2898    // this is what we want especially if the Master is in startup phase doing call outs to
2899    // hbase:meta, etc. when cluster is down. Without ths connection close, we'd have to wait on
2900    // the rpc to timeout.
2901    if (this.clusterConnection != null) {
2902      this.clusterConnection.close();
2903    }
2904  }
2905
2906  public void stopMaster() throws IOException {
2907    if (cpHost != null) {
2908      cpHost.preStopMaster();
2909    }
2910    stop("Stopped by " + Thread.currentThread().getName());
2911  }
2912
2913  @Override
2914  public void stop(String msg) {
2915    if (!isStopped()) {
2916      super.stop(msg);
2917      if (this.activeMasterManager != null) {
2918        this.activeMasterManager.stop();
2919      }
2920    }
2921  }
2922
2923  void checkServiceStarted() throws ServerNotRunningYetException {
2924    if (!serviceStarted) {
2925      throw new ServerNotRunningYetException("Server is not running yet");
2926    }
2927  }
2928
2929  public static class MasterStoppedException extends DoNotRetryIOException {
2930    MasterStoppedException() {
2931      super();
2932    }
2933  }
2934
2935  void checkInitialized() throws PleaseHoldException, ServerNotRunningYetException,
2936      MasterNotRunningException, MasterStoppedException {
2937    checkServiceStarted();
2938    if (!isInitialized()) {
2939      throw new PleaseHoldException("Master is initializing");
2940    }
2941    if (isStopped()) {
2942      throw new MasterStoppedException();
2943    }
2944  }
2945
2946  /**
2947   * Report whether this master is currently the active master or not.
2948   * If not active master, we are parked on ZK waiting to become active.
2949   *
2950   * This method is used for testing.
2951   *
2952   * @return true if active master, false if not.
2953   */
2954  @Override
2955  public boolean isActiveMaster() {
2956    return activeMaster;
2957  }
2958
2959  /**
2960   * Report whether this master has completed with its initialization and is
2961   * ready.  If ready, the master is also the active master.  A standby master
2962   * is never ready.
2963   *
2964   * This method is used for testing.
2965   *
2966   * @return true if master is ready to go, false if not.
2967   */
2968  @Override
2969  public boolean isInitialized() {
2970    return initialized.isReady();
2971  }
2972
2973  /**
2974   * Report whether this master is in maintenance mode.
2975   *
2976   * @return true if master is in maintenanceMode
2977   */
2978  @Override
2979  public boolean isInMaintenanceMode() {
2980    return maintenanceMode;
2981  }
2982
2983  @VisibleForTesting
2984  public void setInitialized(boolean isInitialized) {
2985    procedureExecutor.getEnvironment().setEventReady(initialized, isInitialized);
2986  }
2987
2988  @Override
2989  public ProcedureEvent getInitializedEvent() {
2990    return initialized;
2991  }
2992
2993  /**
2994   * Compute the average load across all region servers.
2995   * Currently, this uses a very naive computation - just uses the number of
2996   * regions being served, ignoring stats about number of requests.
2997   * @return the average load
2998   */
2999  public double getAverageLoad() {
3000    if (this.assignmentManager == null) {
3001      return 0;
3002    }
3003
3004    RegionStates regionStates = this.assignmentManager.getRegionStates();
3005    if (regionStates == null) {
3006      return 0;
3007    }
3008    return regionStates.getAverageLoad();
3009  }
3010
3011  /*
3012   * @return the count of region split plans executed
3013   */
3014  public long getSplitPlanCount() {
3015    return splitPlanCount;
3016  }
3017
3018  /*
3019   * @return the count of region merge plans executed
3020   */
3021  public long getMergePlanCount() {
3022    return mergePlanCount;
3023  }
3024
3025  @Override
3026  public boolean registerService(Service instance) {
3027    /*
3028     * No stacking of instances is allowed for a single service name
3029     */
3030    Descriptors.ServiceDescriptor serviceDesc = instance.getDescriptorForType();
3031    String serviceName = CoprocessorRpcUtils.getServiceName(serviceDesc);
3032    if (coprocessorServiceHandlers.containsKey(serviceName)) {
3033      LOG.error("Coprocessor service "+serviceName+
3034          " already registered, rejecting request from "+instance
3035      );
3036      return false;
3037    }
3038
3039    coprocessorServiceHandlers.put(serviceName, instance);
3040    if (LOG.isDebugEnabled()) {
3041      LOG.debug("Registered master coprocessor service: service="+serviceName);
3042    }
3043    return true;
3044  }
3045
3046  /**
3047   * Utility for constructing an instance of the passed HMaster class.
3048   * @param masterClass
3049   * @return HMaster instance.
3050   */
3051  public static HMaster constructMaster(Class<? extends HMaster> masterClass,
3052      final Configuration conf)  {
3053    try {
3054      Constructor<? extends HMaster> c = masterClass.getConstructor(Configuration.class);
3055      return c.newInstance(conf);
3056    } catch(Exception e) {
3057      Throwable error = e;
3058      if (e instanceof InvocationTargetException &&
3059          ((InvocationTargetException)e).getTargetException() != null) {
3060        error = ((InvocationTargetException)e).getTargetException();
3061      }
3062      throw new RuntimeException("Failed construction of Master: " + masterClass.toString() + ". "
3063        , error);
3064    }
3065  }
3066
3067  /**
3068   * @see org.apache.hadoop.hbase.master.HMasterCommandLine
3069   */
3070  public static void main(String [] args) {
3071    LOG.info("STARTING service " + HMaster.class.getSimpleName());
3072    VersionInfo.logVersion();
3073    new HMasterCommandLine(HMaster.class).doMain(args);
3074  }
3075
3076  public HFileCleaner getHFileCleaner() {
3077    return this.hfileCleaner;
3078  }
3079
3080  public LogCleaner getLogCleaner() {
3081    return this.logCleaner;
3082  }
3083
3084  /**
3085   * @return the underlying snapshot manager
3086   */
3087  @Override
3088  public SnapshotManager getSnapshotManager() {
3089    return this.snapshotManager;
3090  }
3091
3092  /**
3093   * @return the underlying MasterProcedureManagerHost
3094   */
3095  @Override
3096  public MasterProcedureManagerHost getMasterProcedureManagerHost() {
3097    return mpmHost;
3098  }
3099
3100  @Override
3101  public ClusterSchema getClusterSchema() {
3102    return this.clusterSchemaService;
3103  }
3104
3105  /**
3106   * Create a new Namespace.
3107   * @param namespaceDescriptor descriptor for new Namespace
3108   * @param nonceGroup Identifier for the source of the request, a client or process.
3109   * @param nonce A unique identifier for this operation from the client or process identified by
3110   * <code>nonceGroup</code> (the source must ensure each operation gets a unique id).
3111   * @return procedure id
3112   */
3113  long createNamespace(final NamespaceDescriptor namespaceDescriptor, final long nonceGroup,
3114      final long nonce) throws IOException {
3115    checkInitialized();
3116
3117    TableName.isLegalNamespaceName(Bytes.toBytes(namespaceDescriptor.getName()));
3118
3119    return MasterProcedureUtil.submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(this,
3120          nonceGroup, nonce) {
3121      @Override
3122      protected void run() throws IOException {
3123        getMaster().getMasterCoprocessorHost().preCreateNamespace(namespaceDescriptor);
3124        // We need to wait for the procedure to potentially fail due to "prepare" sanity
3125        // checks. This will block only the beginning of the procedure. See HBASE-19953.
3126        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
3127        LOG.info(getClientIdAuditPrefix() + " creating " + namespaceDescriptor);
3128        // Execute the operation synchronously - wait for the operation to complete before
3129        // continuing.
3130        setProcId(getClusterSchema().createNamespace(namespaceDescriptor, getNonceKey(), latch));
3131        latch.await();
3132        getMaster().getMasterCoprocessorHost().postCreateNamespace(namespaceDescriptor);
3133      }
3134
3135      @Override
3136      protected String getDescription() {
3137        return "CreateNamespaceProcedure";
3138      }
3139    });
3140  }
3141
3142  /**
3143   * Modify an existing Namespace.
3144   * @param nonceGroup Identifier for the source of the request, a client or process.
3145   * @param nonce A unique identifier for this operation from the client or process identified by
3146   * <code>nonceGroup</code> (the source must ensure each operation gets a unique id).
3147   * @return procedure id
3148   */
3149  long modifyNamespace(final NamespaceDescriptor newNsDescriptor, final long nonceGroup,
3150      final long nonce) throws IOException {
3151    checkInitialized();
3152
3153    TableName.isLegalNamespaceName(Bytes.toBytes(newNsDescriptor.getName()));
3154
3155    return MasterProcedureUtil.submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(this,
3156          nonceGroup, nonce) {
3157      @Override
3158      protected void run() throws IOException {
3159        NamespaceDescriptor oldNsDescriptor = getNamespace(newNsDescriptor.getName());
3160        getMaster().getMasterCoprocessorHost().preModifyNamespace(oldNsDescriptor, newNsDescriptor);
3161        // We need to wait for the procedure to potentially fail due to "prepare" sanity
3162        // checks. This will block only the beginning of the procedure. See HBASE-19953.
3163        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
3164        LOG.info(getClientIdAuditPrefix() + " modify " + newNsDescriptor);
3165        // Execute the operation synchronously - wait for the operation to complete before
3166        // continuing.
3167        setProcId(getClusterSchema().modifyNamespace(newNsDescriptor, getNonceKey(), latch));
3168        latch.await();
3169        getMaster().getMasterCoprocessorHost().postModifyNamespace(oldNsDescriptor,
3170          newNsDescriptor);
3171      }
3172
3173      @Override
3174      protected String getDescription() {
3175        return "ModifyNamespaceProcedure";
3176      }
3177    });
3178  }
3179
3180  /**
3181   * Delete an existing Namespace. Only empty Namespaces (no tables) can be removed.
3182   * @param nonceGroup Identifier for the source of the request, a client or process.
3183   * @param nonce A unique identifier for this operation from the client or process identified by
3184   * <code>nonceGroup</code> (the source must ensure each operation gets a unique id).
3185   * @return procedure id
3186   */
3187  long deleteNamespace(final String name, final long nonceGroup, final long nonce)
3188      throws IOException {
3189    checkInitialized();
3190
3191    return MasterProcedureUtil.submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(this,
3192          nonceGroup, nonce) {
3193      @Override
3194      protected void run() throws IOException {
3195        getMaster().getMasterCoprocessorHost().preDeleteNamespace(name);
3196        LOG.info(getClientIdAuditPrefix() + " delete " + name);
3197        // Execute the operation synchronously - wait for the operation to complete before
3198        // continuing.
3199        //
3200        // We need to wait for the procedure to potentially fail due to "prepare" sanity
3201        // checks. This will block only the beginning of the procedure. See HBASE-19953.
3202        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createBlockingLatch();
3203        setProcId(submitProcedure(
3204              new DeleteNamespaceProcedure(procedureExecutor.getEnvironment(), name, latch)));
3205        latch.await();
3206        // Will not be invoked in the face of Exception thrown by the Procedure's execution
3207        getMaster().getMasterCoprocessorHost().postDeleteNamespace(name);
3208      }
3209
3210      @Override
3211      protected String getDescription() {
3212        return "DeleteNamespaceProcedure";
3213      }
3214    });
3215  }
3216
3217  /**
3218   * Get a Namespace
3219   * @param name Name of the Namespace
3220   * @return Namespace descriptor for <code>name</code>
3221   */
3222  NamespaceDescriptor getNamespace(String name) throws IOException {
3223    checkInitialized();
3224    if (this.cpHost != null) this.cpHost.preGetNamespaceDescriptor(name);
3225    NamespaceDescriptor nsd = this.clusterSchemaService.getNamespace(name);
3226    if (this.cpHost != null) this.cpHost.postGetNamespaceDescriptor(nsd);
3227    return nsd;
3228  }
3229
3230  /**
3231   * Get all Namespaces
3232   * @return All Namespace descriptors
3233   */
3234  List<NamespaceDescriptor> getNamespaces() throws IOException {
3235    checkInitialized();
3236    final List<NamespaceDescriptor> nsds = new ArrayList<>();
3237    if (cpHost != null) {
3238      cpHost.preListNamespaceDescriptors(nsds);
3239    }
3240    nsds.addAll(this.clusterSchemaService.getNamespaces());
3241    if (this.cpHost != null) {
3242      this.cpHost.postListNamespaceDescriptors(nsds);
3243    }
3244    return nsds;
3245  }
3246
3247  @Override
3248  public List<TableName> listTableNamesByNamespace(String name) throws IOException {
3249    checkInitialized();
3250    return listTableNames(name, null, true);
3251  }
3252
3253  @Override
3254  public List<TableDescriptor> listTableDescriptorsByNamespace(String name) throws IOException {
3255    checkInitialized();
3256    return listTableDescriptors(name, null, null, true);
3257  }
3258
3259  @Override
3260  public boolean abortProcedure(final long procId, final boolean mayInterruptIfRunning)
3261      throws IOException {
3262    if (cpHost != null) {
3263      cpHost.preAbortProcedure(this.procedureExecutor, procId);
3264    }
3265
3266    final boolean result = this.procedureExecutor.abort(procId, mayInterruptIfRunning);
3267
3268    if (cpHost != null) {
3269      cpHost.postAbortProcedure();
3270    }
3271
3272    return result;
3273  }
3274
3275  @Override
3276  public List<Procedure<?>> getProcedures() throws IOException {
3277    if (cpHost != null) {
3278      cpHost.preGetProcedures();
3279    }
3280
3281    @SuppressWarnings({ "unchecked", "rawtypes" })
3282    List<Procedure<?>> procList = (List) this.procedureExecutor.getProcedures();
3283
3284    if (cpHost != null) {
3285      cpHost.postGetProcedures(procList);
3286    }
3287
3288    return procList;
3289  }
3290
3291  @Override
3292  public List<LockedResource> getLocks() throws IOException {
3293    if (cpHost != null) {
3294      cpHost.preGetLocks();
3295    }
3296
3297    MasterProcedureScheduler procedureScheduler =
3298      procedureExecutor.getEnvironment().getProcedureScheduler();
3299
3300    final List<LockedResource> lockedResources = procedureScheduler.getLocks();
3301
3302    if (cpHost != null) {
3303      cpHost.postGetLocks(lockedResources);
3304    }
3305
3306    return lockedResources;
3307  }
3308
3309  /**
3310   * Returns the list of table descriptors that match the specified request
3311   * @param namespace the namespace to query, or null if querying for all
3312   * @param regex The regular expression to match against, or null if querying for all
3313   * @param tableNameList the list of table names, or null if querying for all
3314   * @param includeSysTables False to match only against userspace tables
3315   * @return the list of table descriptors
3316   */
3317  public List<TableDescriptor> listTableDescriptors(final String namespace, final String regex,
3318      final List<TableName> tableNameList, final boolean includeSysTables)
3319  throws IOException {
3320    List<TableDescriptor> htds = new ArrayList<>();
3321    if (cpHost != null) {
3322      cpHost.preGetTableDescriptors(tableNameList, htds, regex);
3323    }
3324    htds = getTableDescriptors(htds, namespace, regex, tableNameList, includeSysTables);
3325    if (cpHost != null) {
3326      cpHost.postGetTableDescriptors(tableNameList, htds, regex);
3327    }
3328    return htds;
3329  }
3330
3331  /**
3332   * Returns the list of table names that match the specified request
3333   * @param regex The regular expression to match against, or null if querying for all
3334   * @param namespace the namespace to query, or null if querying for all
3335   * @param includeSysTables False to match only against userspace tables
3336   * @return the list of table names
3337   */
3338  public List<TableName> listTableNames(final String namespace, final String regex,
3339      final boolean includeSysTables) throws IOException {
3340    List<TableDescriptor> htds = new ArrayList<>();
3341    if (cpHost != null) {
3342      cpHost.preGetTableNames(htds, regex);
3343    }
3344    htds = getTableDescriptors(htds, namespace, regex, null, includeSysTables);
3345    if (cpHost != null) {
3346      cpHost.postGetTableNames(htds, regex);
3347    }
3348    List<TableName> result = new ArrayList<>(htds.size());
3349    for (TableDescriptor htd: htds) result.add(htd.getTableName());
3350    return result;
3351  }
3352
3353  /**
3354   * @return list of table table descriptors after filtering by regex and whether to include system
3355   *    tables, etc.
3356   * @throws IOException
3357   */
3358  private List<TableDescriptor> getTableDescriptors(final List<TableDescriptor> htds,
3359      final String namespace, final String regex, final List<TableName> tableNameList,
3360      final boolean includeSysTables)
3361  throws IOException {
3362    if (tableNameList == null || tableNameList.isEmpty()) {
3363      // request for all TableDescriptors
3364      Collection<TableDescriptor> allHtds;
3365      if (namespace != null && namespace.length() > 0) {
3366        // Do a check on the namespace existence. Will fail if does not exist.
3367        this.clusterSchemaService.getNamespace(namespace);
3368        allHtds = tableDescriptors.getByNamespace(namespace).values();
3369      } else {
3370        allHtds = tableDescriptors.getAll().values();
3371      }
3372      for (TableDescriptor desc: allHtds) {
3373        if (tableStateManager.isTablePresent(desc.getTableName())
3374            && (includeSysTables || !desc.getTableName().isSystemTable())) {
3375          htds.add(desc);
3376        }
3377      }
3378    } else {
3379      for (TableName s: tableNameList) {
3380        if (tableStateManager.isTablePresent(s)) {
3381          TableDescriptor desc = tableDescriptors.get(s);
3382          if (desc != null) {
3383            htds.add(desc);
3384          }
3385        }
3386      }
3387    }
3388
3389    // Retains only those matched by regular expression.
3390    if (regex != null) filterTablesByRegex(htds, Pattern.compile(regex));
3391    return htds;
3392  }
3393
3394  /**
3395   * Removes the table descriptors that don't match the pattern.
3396   * @param descriptors list of table descriptors to filter
3397   * @param pattern the regex to use
3398   */
3399  private static void filterTablesByRegex(final Collection<TableDescriptor> descriptors,
3400      final Pattern pattern) {
3401    final String defaultNS = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
3402    Iterator<TableDescriptor> itr = descriptors.iterator();
3403    while (itr.hasNext()) {
3404      TableDescriptor htd = itr.next();
3405      String tableName = htd.getTableName().getNameAsString();
3406      boolean matched = pattern.matcher(tableName).matches();
3407      if (!matched && htd.getTableName().getNamespaceAsString().equals(defaultNS)) {
3408        matched = pattern.matcher(defaultNS + TableName.NAMESPACE_DELIM + tableName).matches();
3409      }
3410      if (!matched) {
3411        itr.remove();
3412      }
3413    }
3414  }
3415
3416  @Override
3417  public long getLastMajorCompactionTimestamp(TableName table) throws IOException {
3418    return getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
3419        .getLastMajorCompactionTimestamp(table);
3420  }
3421
3422  @Override
3423  public long getLastMajorCompactionTimestampForRegion(byte[] regionName) throws IOException {
3424    return getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
3425        .getLastMajorCompactionTimestamp(regionName);
3426  }
3427
3428  /**
3429   * Gets the mob file compaction state for a specific table.
3430   * Whether all the mob files are selected is known during the compaction execution, but
3431   * the statistic is done just before compaction starts, it is hard to know the compaction
3432   * type at that time, so the rough statistics are chosen for the mob file compaction. Only two
3433   * compaction states are available, CompactionState.MAJOR_AND_MINOR and CompactionState.NONE.
3434   * @param tableName The current table name.
3435   * @return If a given table is in mob file compaction now.
3436   */
3437  public CompactionState getMobCompactionState(TableName tableName) {
3438    AtomicInteger compactionsCount = mobCompactionStates.get(tableName);
3439    if (compactionsCount != null && compactionsCount.get() != 0) {
3440      return CompactionState.MAJOR_AND_MINOR;
3441    }
3442    return CompactionState.NONE;
3443  }
3444
3445  public void reportMobCompactionStart(TableName tableName) throws IOException {
3446    IdLock.Entry lockEntry = null;
3447    try {
3448      lockEntry = mobCompactionLock.getLockEntry(tableName.hashCode());
3449      AtomicInteger compactionsCount = mobCompactionStates.get(tableName);
3450      if (compactionsCount == null) {
3451        compactionsCount = new AtomicInteger(0);
3452        mobCompactionStates.put(tableName, compactionsCount);
3453      }
3454      compactionsCount.incrementAndGet();
3455    } finally {
3456      if (lockEntry != null) {
3457        mobCompactionLock.releaseLockEntry(lockEntry);
3458      }
3459    }
3460  }
3461
3462  public void reportMobCompactionEnd(TableName tableName) throws IOException {
3463    IdLock.Entry lockEntry = null;
3464    try {
3465      lockEntry = mobCompactionLock.getLockEntry(tableName.hashCode());
3466      AtomicInteger compactionsCount = mobCompactionStates.get(tableName);
3467      if (compactionsCount != null) {
3468        int count = compactionsCount.decrementAndGet();
3469        // remove the entry if the count is 0.
3470        if (count == 0) {
3471          mobCompactionStates.remove(tableName);
3472        }
3473      }
3474    } finally {
3475      if (lockEntry != null) {
3476        mobCompactionLock.releaseLockEntry(lockEntry);
3477      }
3478    }
3479  }
3480
3481  /**
3482   * Requests mob compaction.
3483   * @param tableName The table the compact.
3484   * @param columns The compacted columns.
3485   * @param allFiles Whether add all mob files into the compaction.
3486   */
3487  public void requestMobCompaction(TableName tableName,
3488                                   List<ColumnFamilyDescriptor> columns, boolean allFiles) throws IOException {
3489    mobCompactThread.requestMobCompaction(conf, fs, tableName, columns, allFiles);
3490  }
3491
3492  /**
3493   * Queries the state of the {@link LoadBalancerTracker}. If the balancer is not initialized,
3494   * false is returned.
3495   *
3496   * @return The state of the load balancer, or false if the load balancer isn't defined.
3497   */
3498  public boolean isBalancerOn() {
3499    return !isInMaintenanceMode()
3500        && loadBalancerTracker != null
3501        && loadBalancerTracker.isBalancerOn();
3502  }
3503
3504  /**
3505   * Queries the state of the {@link RegionNormalizerTracker}. If it's not initialized,
3506   * false is returned.
3507   */
3508  public boolean isNormalizerOn() {
3509    return !isInMaintenanceMode()
3510        && regionNormalizerTracker != null
3511        && regionNormalizerTracker.isNormalizerOn();
3512  }
3513
3514  /**
3515   * Queries the state of the {@link SplitOrMergeTracker}. If it is not initialized,
3516   * false is returned. If switchType is illegal, false will return.
3517   * @param switchType see {@link org.apache.hadoop.hbase.client.MasterSwitchType}
3518   * @return The state of the switch
3519   */
3520  @Override
3521  public boolean isSplitOrMergeEnabled(MasterSwitchType switchType) {
3522    return !isInMaintenanceMode()
3523        && splitOrMergeTracker != null
3524        && splitOrMergeTracker.isSplitOrMergeEnabled(switchType);
3525  }
3526
3527  /**
3528   * Fetch the configured {@link LoadBalancer} class name. If none is set, a default is returned.
3529   *
3530   * @return The name of the {@link LoadBalancer} in use.
3531   */
3532  public String getLoadBalancerClassName() {
3533    return conf.get(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, LoadBalancerFactory
3534        .getDefaultLoadBalancerClass().getName());
3535  }
3536
3537  /**
3538   * @return RegionNormalizerTracker instance
3539   */
3540  public RegionNormalizerTracker getRegionNormalizerTracker() {
3541    return regionNormalizerTracker;
3542  }
3543
3544  public SplitOrMergeTracker getSplitOrMergeTracker() {
3545    return splitOrMergeTracker;
3546  }
3547
3548  @Override
3549  public LoadBalancer getLoadBalancer() {
3550    return balancer;
3551  }
3552
3553  @Override
3554  public FavoredNodesManager getFavoredNodesManager() {
3555    return favoredNodesManager;
3556  }
3557
3558  @Override
3559  public void addReplicationPeer(String peerId, ReplicationPeerConfig peerConfig, boolean enabled)
3560      throws ReplicationException, IOException {
3561    if (cpHost != null) {
3562      cpHost.preAddReplicationPeer(peerId, peerConfig);
3563    }
3564    LOG.info(getClientIdAuditPrefix() + " creating replication peer, id=" + peerId + ", config="
3565        + peerConfig + ", state=" + (enabled ? "ENABLED" : "DISABLED"));
3566    this.replicationManager.addReplicationPeer(peerId, peerConfig, enabled);
3567    if (cpHost != null) {
3568      cpHost.postAddReplicationPeer(peerId, peerConfig);
3569    }
3570  }
3571
3572  @Override
3573  public void removeReplicationPeer(String peerId) throws ReplicationException, IOException {
3574    if (cpHost != null) {
3575      cpHost.preRemoveReplicationPeer(peerId);
3576    }
3577    LOG.info(getClientIdAuditPrefix() + " removing replication peer, id=" + peerId);
3578    this.replicationManager.removeReplicationPeer(peerId);
3579    if (cpHost != null) {
3580      cpHost.postRemoveReplicationPeer(peerId);
3581    }
3582  }
3583
3584  @Override
3585  public void enableReplicationPeer(String peerId) throws ReplicationException, IOException {
3586    if (cpHost != null) {
3587      cpHost.preEnableReplicationPeer(peerId);
3588    }
3589    LOG.info(getClientIdAuditPrefix() + " enable replication peer, id=" + peerId);
3590    this.replicationManager.enableReplicationPeer(peerId);
3591    if (cpHost != null) {
3592      cpHost.postEnableReplicationPeer(peerId);
3593    }
3594  }
3595
3596  @Override
3597  public void disableReplicationPeer(String peerId) throws ReplicationException, IOException {
3598    if (cpHost != null) {
3599      cpHost.preDisableReplicationPeer(peerId);
3600    }
3601    LOG.info(getClientIdAuditPrefix() + " disable replication peer, id=" + peerId);
3602    this.replicationManager.disableReplicationPeer(peerId);
3603    if (cpHost != null) {
3604      cpHost.postDisableReplicationPeer(peerId);
3605    }
3606  }
3607
3608  @Override
3609  public ReplicationPeerConfig getReplicationPeerConfig(String peerId) throws ReplicationException,
3610      IOException {
3611    if (cpHost != null) {
3612      cpHost.preGetReplicationPeerConfig(peerId);
3613    }
3614    final ReplicationPeerConfig peerConfig = this.replicationManager.getPeerConfig(peerId);
3615    LOG.info(getClientIdAuditPrefix() + " get replication peer config, id=" + peerId + ", config="
3616        + peerConfig);
3617    if (cpHost != null) {
3618      cpHost.postGetReplicationPeerConfig(peerId);
3619    }
3620    return peerConfig;
3621  }
3622
3623  @Override
3624  public void updateReplicationPeerConfig(String peerId, ReplicationPeerConfig peerConfig)
3625      throws ReplicationException, IOException {
3626    if (cpHost != null) {
3627      cpHost.preUpdateReplicationPeerConfig(peerId, peerConfig);
3628    }
3629    LOG.info(getClientIdAuditPrefix() + " update replication peer config, id=" + peerId
3630        + ", config=" + peerConfig);
3631    this.replicationManager.updatePeerConfig(peerId, peerConfig);
3632    if (cpHost != null) {
3633      cpHost.postUpdateReplicationPeerConfig(peerId, peerConfig);
3634    }
3635  }
3636
3637  @Override
3638  public List<ReplicationPeerDescription> listReplicationPeers(String regex)
3639      throws ReplicationException, IOException {
3640    if (cpHost != null) {
3641      cpHost.preListReplicationPeers(regex);
3642    }
3643    LOG.info(getClientIdAuditPrefix() + " list replication peers, regex=" + regex);
3644    Pattern pattern = regex == null ? null : Pattern.compile(regex);
3645    List<ReplicationPeerDescription> peers = this.replicationManager.listReplicationPeers(pattern);
3646    if (cpHost != null) {
3647      cpHost.postListReplicationPeers(regex);
3648    }
3649    return peers;
3650  }
3651
3652  /**
3653   * Mark region server(s) as decommissioned (previously called 'draining') to prevent additional
3654   * regions from getting assigned to them. Also unload the regions on the servers asynchronously.0
3655   * @param servers Region servers to decommission.
3656   * @throws HBaseIOException
3657   */
3658  public void decommissionRegionServers(final List<ServerName> servers, final boolean offload)
3659      throws HBaseIOException {
3660    List<ServerName> serversAdded = new ArrayList<>(servers.size());
3661    // Place the decommission marker first.
3662    String parentZnode = getZooKeeper().znodePaths.drainingZNode;
3663    for (ServerName server : servers) {
3664      try {
3665        String node = ZNodePaths.joinZNode(parentZnode, server.getServerName());
3666        ZKUtil.createAndFailSilent(getZooKeeper(), node);
3667      } catch (KeeperException ke) {
3668        throw new HBaseIOException(
3669            this.zooKeeper.prefix("Unable to decommission '" + server.getServerName() + "'."), ke);
3670      }
3671      if (this.serverManager.addServerToDrainList(server)) {
3672        serversAdded.add(server);
3673      };
3674    }
3675    // Move the regions off the decommissioned servers.
3676    if (offload) {
3677      final List<ServerName> destServers = this.serverManager.createDestinationServersList();
3678      for (ServerName server : serversAdded) {
3679        final List<RegionInfo> regionsOnServer =
3680            this.assignmentManager.getRegionStates().getServerRegionInfoSet(server);
3681        for (RegionInfo hri : regionsOnServer) {
3682          ServerName dest = balancer.randomAssignment(hri, destServers);
3683          if (dest == null) {
3684            throw new HBaseIOException("Unable to determine a plan to move " + hri);
3685          }
3686          RegionPlan rp = new RegionPlan(hri, server, dest);
3687          this.assignmentManager.moveAsync(rp);
3688        }
3689      }
3690    }
3691  }
3692
3693  /**
3694   * List region servers marked as decommissioned (previously called 'draining') to not get regions
3695   * assigned to them.
3696   * @return List of decommissioned servers.
3697   */
3698  public List<ServerName> listDecommissionedRegionServers() {
3699    return this.serverManager.getDrainingServersList();
3700  }
3701
3702  /**
3703   * Remove decommission marker (previously called 'draining') from a region server to allow regions
3704   * assignments. Load regions onto the server asynchronously if a list of regions is given
3705   * @param server Region server to remove decommission marker from.
3706   * @throws HBaseIOException
3707   */
3708  public void recommissionRegionServer(final ServerName server,
3709      final List<byte[]> encodedRegionNames) throws HBaseIOException {
3710    // Remove the server from decommissioned (draining) server list.
3711    String parentZnode = getZooKeeper().znodePaths.drainingZNode;
3712    String node = ZNodePaths.joinZNode(parentZnode, server.getServerName());
3713    try {
3714      ZKUtil.deleteNodeFailSilent(getZooKeeper(), node);
3715    } catch (KeeperException ke) {
3716      throw new HBaseIOException(
3717          this.zooKeeper.prefix("Unable to recommission '" + server.getServerName() + "'."), ke);
3718    }
3719    this.serverManager.removeServerFromDrainList(server);
3720
3721    // Load the regions onto the server if we are given a list of regions.
3722    if (encodedRegionNames == null || encodedRegionNames.isEmpty()) {
3723      return;
3724    }
3725    if (!this.serverManager.isServerOnline(server)) {
3726      return;
3727    }
3728    for (byte[] encodedRegionName : encodedRegionNames) {
3729      RegionState regionState =
3730          assignmentManager.getRegionStates().getRegionState(Bytes.toString(encodedRegionName));
3731      if (regionState == null) {
3732        LOG.warn("Unknown region " + Bytes.toStringBinary(encodedRegionName));
3733        continue;
3734      }
3735      RegionInfo hri = regionState.getRegion();
3736      if (server.equals(regionState.getServerName())) {
3737        LOG.info("Skipping move of region " + hri.getRegionNameAsString()
3738          + " because region already assigned to the same server " + server + ".");
3739        continue;
3740      }
3741      RegionPlan rp = new RegionPlan(hri, regionState.getServerName(), server);
3742      this.assignmentManager.moveAsync(rp);
3743    }
3744  }
3745
3746  @Override
3747  public LockManager getLockManager() {
3748    return lockManager;
3749  }
3750
3751  public QuotaObserverChore getQuotaObserverChore() {
3752    return this.quotaObserverChore;
3753  }
3754
3755  public SpaceQuotaSnapshotNotifier getSpaceQuotaSnapshotNotifier() {
3756    return this.spaceQuotaSnapshotNotifier;
3757  }
3758
3759  /**
3760   * This method modifies the master's configuration in order to inject replication-related features
3761   */
3762  @VisibleForTesting
3763  public static void decorateMasterConfiguration(Configuration conf) {
3764    String plugins = conf.get(HBASE_MASTER_LOGCLEANER_PLUGINS);
3765    String cleanerClass = ReplicationLogCleaner.class.getCanonicalName();
3766    if (!plugins.contains(cleanerClass)) {
3767      conf.set(HBASE_MASTER_LOGCLEANER_PLUGINS, plugins + "," + cleanerClass);
3768    }
3769    if (ReplicationUtils.isReplicationForBulkLoadDataEnabled(conf)) {
3770      plugins = conf.get(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS);
3771      cleanerClass = ReplicationHFileCleaner.class.getCanonicalName();
3772      if (!plugins.contains(cleanerClass)) {
3773        conf.set(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, plugins + "," + cleanerClass);
3774      }
3775    }
3776  }
3777}