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}