From 0301168f528f006c288f6b31c1c1ecd38425e038 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 15 Feb 2017 14:24:57 -0500 Subject: [PATCH] HBASE-17002 JMX metrics and some UI additions for space quotas --- .../hbase/master/MetricsMasterQuotaSource.java | 74 ++++++++++++ .../master/MetricsMasterQuotaSourceFactory.java | 26 +++++ .../hadoop/hbase/master/MetricsMasterWrapper.java | 13 +++ .../MetricsRegionServerQuotaSource.java | 54 +++++++++ .../MetricsMasterQuotaSourceFactoryImpl.java | 33 ++++++ .../hbase/master/MetricsMasterQuotaSourceImpl.java | 124 +++++++++++++++++++++ ...he.hadoop.hbase.master.MetricsMasterQuotaSource | 18 +++ ...op.hbase.master.MetricsMasterQuotaSourceFactory | 18 +++ .../org/apache/hadoop/hbase/master/HMaster.java | 2 +- .../apache/hadoop/hbase/master/MetricsMaster.java | 42 +++++++ .../hbase/master/MetricsMasterWrapperImpl.java | 42 ++++++- .../hadoop/hbase/quotas/QuotaObserverChore.java | 68 ++++++++++- .../main/resources/hbase-webapps/master/table.jsp | 34 ++++++ .../hbase/master/TestMasterMetricsWrapper.java | 14 +++ 14 files changed, 554 insertions(+), 8 deletions(-) create mode 100644 hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSource.java create mode 100644 hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactory.java create mode 100644 hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServerQuotaSource.java create mode 100644 hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactoryImpl.java create mode 100644 hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceImpl.java create mode 100644 hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSource create mode 100644 hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceFactory diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSource.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSource.java new file mode 100644 index 0000000..eaa3423 --- /dev/null +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSource.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master; + +import org.apache.hadoop.hbase.metrics.BaseSource; + +/** + * A collection of exposed metrics for HBase quotas from the HBase Master. + */ +public interface MetricsMasterQuotaSource extends BaseSource { + + String METRICS_NAME = "Quotas"; + String METRICS_CONTEXT = "master"; + String METRICS_JMX_CONTEXT = "Master,sub=" + METRICS_NAME; + String METRICS_DESCRIPTION = "Metrics about HBase Quotas by the Master"; + + String NUM_SPACE_QUOTAS_NAME = "numSpaceQuotas"; + String NUM_SPACE_QUOTAS_DESC = "Number of space quotas defined"; + String NUM_VIOLATING_TABLE_QUOTAS_NAME = "numTablesViolatingQuotas"; + String NUM_VIOLATING_TABLE_QUOTAS_DESC = "Number of tables violating space quotas"; + String NUM_VIOLATING_NS_QUOTAS_NAME = "numNamespacesViolatingQuotas"; + String NUM_VIOLATING_NS_QUOTAS_DESC = "Number of namespaces violating space quotas"; + String NUM_REGION_SIZE_REPORTS_NAME = "numRegionSizeReports"; + String NUM_REGION_SIZE_REPORTS_DESC = "The number of Region sizes reported"; + String QUOTA_OBSERVER_CHORE_TIME_NAME = "quotaObserverChoreTime"; + String QUOTA_OBSERVER_CHORE_TIME_DESC = "Histogram for the time in millis for the QuotaObserverChore"; + String TABLE_QUOTA_USAGE_NAME = "tableSpaceQuotaOverview"; + String TABLE_QUOTA_USAGE_DESC = "A JSON summary of the usage of all tables with space quotas"; + String NS_QUOTA_USAGE_NAME = "namespaceSpaceQuotaOverview"; + String NS_QUOTA_USAGE_DESC = "A JSON summary of the usage of all namespaces with space quotas"; + + /** + * Updates the metric tracking the number of space quotas defined in the system. + */ + void updateNumSpaceQuotas(long numSpaceQuotas); + + /** + * Updates the metric tracking the number of tables the master has computed to be in + * violation of their space quota. + */ + void updateNumTablesInSpaceQuotaViolation(long numTablesInViolation); + + /** + * Updates the metric tracking the number of namespaces the master has computed to be in + * violation of their space quota. + */ + void updateNumNamespacesInSpaceQuotaViolation(long numNamespacesInViolation); + + /** + * Updates the metric tracking the number of region size reports the master is currently + * retaining in memory. + */ + void updateNumCurrentSpaceQuotaRegionSizeReports(long numCurrentRegionSizeReports); + + /** + * Updates the metric tracking the amount of time taken by the {@code QuotaObserverChore} + * which runs periodically. + */ + void incrementSpaceQuotaObserverChoreTime(long time); +} diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactory.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactory.java new file mode 100644 index 0000000..6e10746 --- /dev/null +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactory.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master; + +/** + * Interface of a factory to create MetricsMasterQuotaSource when given a MetricsMasterWrapper. + */ +public interface MetricsMasterQuotaSourceFactory { + + MetricsMasterQuotaSource create(MetricsMasterWrapper masterWrapper); + +} diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapper.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapper.java index 64e0a8a..4789283 100644 --- a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapper.java +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapper.java @@ -18,6 +18,9 @@ package org.apache.hadoop.hbase.master; +import java.util.Map; +import java.util.Map.Entry; + /** * This is the interface that will expose information to hadoop1/hadoop2 implementations of the * MetricsMasterSource. @@ -121,4 +124,14 @@ public interface MetricsMasterWrapper { * Get the number of region merge plans executed. */ long getMergePlanCount(); + + /** + * Gets the space usage and limit for each table. + */ + Map> getTableSpaceUtilization(); + + /** + * Gets the space usage and limit for each namespace. + */ + Map> getNamespaceSpaceUtilization(); } diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServerQuotaSource.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServerQuotaSource.java new file mode 100644 index 0000000..2bdf791 --- /dev/null +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServerQuotaSource.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.regionserver; + +import org.apache.hadoop.hbase.metrics.BaseSource; + +/** + * A collection of exposed metrics for HBase quotas from an HBase RegionServer. + */ +public interface MetricsRegionServerQuotaSource extends BaseSource { + + String METRICS_NAME = "Quotas"; + String METRICS_CONTEXT = "regionserver"; + String METRICS_DESCRIPTION = "Metrics about HBase RegionServer Quotas"; + String METRICS_JMX_CONTEXT = "RegionServer,sub=" + METRICS_NAME; + + /** + * Updates the metric tracking how many tables this RegionServer has marked as in violation + * of their space quota. + */ + void updateNumTablesInSpaceQuotaViolation(long tablesInViolation); + + /** + * Updates the metric tracking how many tables this RegionServer has received + * {@code SpaceQuotaSnapshot}s for. + */ + void updateNumTableSpaceQuotaSnapshots(long numSnapshots); + + /** + * Updates the metric tracking how much time was spent scanning the filesystem to compute + * the size of each region hosted by this RegionServer. + */ + void incrementSpaceQuotaFileSystemScannerChoreTime(long time); + + /** + * Updates the metric tracking how much time was spent updating the RegionServer with the + * lastest information on space quotas from the {@code hbase:quota} table. + */ + void incrementSpaceQuotaRefresherChoreTime(long time); +} diff --git a/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactoryImpl.java b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactoryImpl.java new file mode 100644 index 0000000..9adf6fe --- /dev/null +++ b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceFactoryImpl.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master; + +/** + * Factory to create MetricsMasterQuotaSource instances when given a MetricsMasterWrapper. + */ +public class MetricsMasterQuotaSourceFactoryImpl implements MetricsMasterQuotaSourceFactory { + + private MetricsMasterQuotaSource quotaSource; + + @Override + public synchronized MetricsMasterQuotaSource create(MetricsMasterWrapper masterWrapper) { + if (null == quotaSource) { + quotaSource = new MetricsMasterQuotaSourceImpl(masterWrapper); + } + return quotaSource; + } +} diff --git a/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceImpl.java b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceImpl.java new file mode 100644 index 0000000..2e2d6c0 --- /dev/null +++ b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterQuotaSourceImpl.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master; + +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.metrics.BaseSourceImpl; +import org.apache.hadoop.metrics2.MetricHistogram; +import org.apache.hadoop.metrics2.MetricsCollector; +import org.apache.hadoop.metrics2.MetricsRecordBuilder; +import org.apache.hadoop.metrics2.lib.Interns; +import org.apache.hadoop.metrics2.lib.MutableGaugeLong; + +@InterfaceAudience.Private +public class MetricsMasterQuotaSourceImpl extends BaseSourceImpl implements MetricsMasterQuotaSource { + private final MetricsMasterWrapper wrapper; + private final MutableGaugeLong spaceQuotasGauge; + private final MutableGaugeLong tablesViolatingQuotasGauge; + private final MutableGaugeLong namespacesViolatingQuotasGauge; + private final MutableGaugeLong regionSpaceReportsGauge; + private final MetricHistogram quotaObserverTimeHisto; + + public MetricsMasterQuotaSourceImpl(MetricsMasterWrapper wrapper) { + this(METRICS_NAME, METRICS_DESCRIPTION, METRICS_CONTEXT, METRICS_JMX_CONTEXT, wrapper); + } + + public MetricsMasterQuotaSourceImpl( + String metricsName, String metricsDescription, String metricsContext, + String metricsJmxContext, MetricsMasterWrapper wrapper) { + super(metricsName, metricsDescription, metricsContext, metricsJmxContext); + this.wrapper = wrapper; + + spaceQuotasGauge = getMetricsRegistry().newGauge(NUM_SPACE_QUOTAS_NAME, NUM_SPACE_QUOTAS_DESC, 0L); + tablesViolatingQuotasGauge = getMetricsRegistry().newGauge(NUM_VIOLATING_TABLE_QUOTAS_NAME, NUM_VIOLATING_TABLE_QUOTAS_DESC, 0L); + namespacesViolatingQuotasGauge = getMetricsRegistry().newGauge(NUM_VIOLATING_NS_QUOTAS_NAME, NUM_VIOLATING_NS_QUOTAS_DESC, 0L); + regionSpaceReportsGauge = getMetricsRegistry().newGauge(NUM_REGION_SIZE_REPORTS_NAME, NUM_REGION_SIZE_REPORTS_DESC, 0L); + + quotaObserverTimeHisto = getMetricsRegistry().newTimeHistogram(QUOTA_OBSERVER_CHORE_TIME_NAME, QUOTA_OBSERVER_CHORE_TIME_DESC); + } + + @Override + public void updateNumSpaceQuotas(long numSpaceQuotas) { + spaceQuotasGauge.set(numSpaceQuotas); + } + + @Override + public void updateNumTablesInSpaceQuotaViolation(long numTablesInViolation) { + tablesViolatingQuotasGauge.set(numTablesInViolation); + } + + @Override + public void updateNumNamespacesInSpaceQuotaViolation(long numNamespacesInViolation) { + namespacesViolatingQuotasGauge.set(numNamespacesInViolation); + } + + @Override + public void updateNumCurrentSpaceQuotaRegionSizeReports(long numCurrentRegionSizeReports) { + regionSpaceReportsGauge.set(numCurrentRegionSizeReports); + } + + @Override + public void incrementSpaceQuotaObserverChoreTime(long time) { + quotaObserverTimeHisto.add(time); + } + + @Override + public void getMetrics(MetricsCollector metricsCollector, boolean all) { + MetricsRecordBuilder record = metricsCollector.addRecord(metricsRegistry.info()); + if (null != wrapper) { + // Summarize the tables + Map> tableUsages = wrapper.getTableSpaceUtilization(); + String tableSummary = "[]"; + if (null != tableUsages && !tableUsages.isEmpty()) { + tableSummary = generateJsonQuotaSummary(tableUsages.entrySet(), "table"); + } + record.tag(Interns.info(TABLE_QUOTA_USAGE_NAME, TABLE_QUOTA_USAGE_DESC), tableSummary); + + // Summarize the namespaces + String nsSummary = "[]"; + Map> namespaceUsages = wrapper.getNamespaceSpaceUtilization(); + if (null != namespaceUsages && !namespaceUsages.isEmpty()) { + nsSummary = generateJsonQuotaSummary(namespaceUsages.entrySet(), "namespace"); + } + record.tag(Interns.info(NS_QUOTA_USAGE_NAME, NS_QUOTA_USAGE_DESC), nsSummary); + } + metricsRegistry.snapshot(record, all); + } + + /** + * Summarizes the usage and limit for many targets (table or namespace) into JSON. + */ + private String generateJsonQuotaSummary( + Iterable>> data, String target) { + StringBuilder sb = new StringBuilder(); + for (Entry> tableUsage : data) { + String tableName = tableUsage.getKey(); + long usage = tableUsage.getValue().getKey(); + long limit = tableUsage.getValue().getValue(); + if (sb.length() > 0) { + sb.append(", "); + } + sb.append("{").append(target).append("=").append(tableName).append(", usage=").append(usage) + .append(", limit=").append(limit).append("}"); + } + sb.insert(0, "[").append("]"); + return sb.toString(); + } +} diff --git a/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSource b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSource new file mode 100644 index 0000000..d3891d2 --- /dev/null +++ b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSource @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceImpl diff --git a/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceFactory b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceFactory new file mode 100644 index 0000000..5bbcc8a --- /dev/null +++ b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceFactory @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +org.apache.hadoop.hbase.master.MetricsMasterQuotaSourceFactoryImpl diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index ccddd71..7a7623c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -869,7 +869,7 @@ public class HMaster extends HRegionServer implements MasterServices { // Create the quota snapshot notifier spaceQuotaSnapshotNotifier = createQuotaSnapshotNotifier(); spaceQuotaSnapshotNotifier.initialize(getClusterConnection()); - this.quotaObserverChore = new QuotaObserverChore(this); + this.quotaObserverChore = new QuotaObserverChore(this, getMasterMetrics()); // Start the chore to read the region FS space reports and act on them getChoreService().scheduleChore(quotaObserverChore); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMaster.java index d055853..b5bc3d7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMaster.java @@ -37,11 +37,14 @@ public class MetricsMaster { private static final Log LOG = LogFactory.getLog(MetricsMaster.class); private MetricsMasterSource masterSource; private MetricsMasterProcSource masterProcSource; + private MetricsMasterQuotaSource masterQuotaSource; public MetricsMaster(MetricsMasterWrapper masterWrapper) { masterSource = CompatibilitySingletonFactory.getInstance(MetricsMasterSourceFactory.class).create(masterWrapper); masterProcSource = CompatibilitySingletonFactory.getInstance(MetricsMasterProcSourceFactory.class).create(masterWrapper); + masterQuotaSource = + CompatibilitySingletonFactory.getInstance(MetricsMasterQuotaSourceFactory.class).create(masterWrapper); } // for unit-test usage @@ -53,10 +56,49 @@ public class MetricsMaster { return masterProcSource; } + public MetricsMasterQuotaSource getMetricsQuotaSource() { + return masterQuotaSource; + } + /** * @param inc How much to add to requests. */ public void incrementRequests(final long inc) { masterSource.incRequests(inc); } + + /** + * Sets the number of space quotas defined. + */ + public void setNumSpaceQuotas(final long numSpaceQuotas) { + masterQuotaSource.updateNumSpaceQuotas(numSpaceQuotas); + } + + /** + * Sets the number of table in violation of a space quota. + */ + public void setNumTableInSpaceQuotaViolation(final long numTablesInViolation) { + masterQuotaSource.updateNumTablesInSpaceQuotaViolation(numTablesInViolation); + } + + /** + * Sets the number of namespaces in violation of a space quota. + */ + public void setNumNamespacesInSpaceQuotaViolation(final long numNamespacesInViolation) { + masterQuotaSource.updateNumNamespacesInSpaceQuotaViolation(numNamespacesInViolation); + } + + /** + * Sets the number of region size reports the master has seen. + */ + public void setNumRegionSizeReports(final long numRegionReports) { + masterQuotaSource.updateNumCurrentSpaceQuotaRegionSizeReports(numRegionReports); + } + + /** + * Sets the execution time of a period of the QuotaObserverChore. + */ + public void incrementQuotaObserverTime(final long executionTime) { + masterQuotaSource.incrementSpaceQuotaObserverChoreTime(executionTime); + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapperImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapperImpl.java index 4cff28b..cbf7ba5 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapperImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MetricsMasterWrapperImpl.java @@ -17,9 +17,18 @@ */ package org.apache.hadoop.hbase.master; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.quotas.QuotaObserverChore; +import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; /** @@ -134,4 +143,35 @@ public class MetricsMasterWrapperImpl implements MetricsMasterWrapper { return master.getNumWALFiles(); } + @Override + public Map> getTableSpaceUtilization() { + QuotaObserverChore quotaChore = master.getQuotaObserverChore(); + if (null == quotaChore) { + return Collections.emptyMap(); + } + Map tableSnapshots = quotaChore.getTableQuotaSnapshots(); + Map> convertedData = new HashMap<>(); + for (Entry entry : tableSnapshots.entrySet()) { + convertedData.put(entry.getKey().toString(), convertSnapshot(entry.getValue())); + } + return convertedData; + } + + @Override + public Map> getNamespaceSpaceUtilization() { + QuotaObserverChore quotaChore = master.getQuotaObserverChore(); + if (null == quotaChore) { + return Collections.emptyMap(); + } + Map namespaceSnapshots = quotaChore.getNamespaceQuotaSnapshots(); + Map> convertedData = new HashMap<>(); + for (Entry entry : namespaceSnapshots.entrySet()) { + convertedData.put(entry.getKey(), convertSnapshot(entry.getValue())); + } + return convertedData; + } + + Entry convertSnapshot(SpaceQuotaSnapshot snapshot) { + return new SimpleImmutableEntry(snapshot.getUsage(), snapshot.getLimit()); + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaObserverChore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaObserverChore.java index 973ac8c..5c387b6 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaObserverChore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaObserverChore.java @@ -18,12 +18,12 @@ package org.apache.hadoop.hbase.quotas; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; @@ -37,6 +37,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.MetricsMaster; import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot; import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot.SpaceQuotaStatus; import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota; @@ -72,6 +73,7 @@ public class QuotaObserverChore extends ScheduledChore { private final Connection conn; private final Configuration conf; private final MasterQuotaManager quotaManager; + private final MetricsMaster metrics; /* * Callback that changes in quota snapshots are passed to. */ @@ -81,7 +83,9 @@ public class QuotaObserverChore extends ScheduledChore { * Preserves the state of quota snapshots for tables and namespaces */ private final Map tableQuotaSnapshots; + private final Map readOnlyTableQuotaSnapshots; private final Map namespaceQuotaSnapshots; + private final Map readOnlyNamespaceSnapshots; /* * Encapsulates logic for tracking the state of a table/namespace WRT space quotas @@ -89,25 +93,28 @@ public class QuotaObserverChore extends ScheduledChore { private QuotaSnapshotStore tableSnapshotStore; private QuotaSnapshotStore namespaceSnapshotStore; - public QuotaObserverChore(HMaster master) { + public QuotaObserverChore(HMaster master, MetricsMaster metrics) { this( master.getConnection(), master.getConfiguration(), master.getSpaceQuotaSnapshotNotifier(), master.getMasterQuotaManager(), - master); + master, metrics); } QuotaObserverChore( Connection conn, Configuration conf, SpaceQuotaSnapshotNotifier snapshotNotifier, - MasterQuotaManager quotaManager, Stoppable stopper) { + MasterQuotaManager quotaManager, Stoppable stopper, MetricsMaster metrics) { super( QuotaObserverChore.class.getSimpleName(), stopper, getPeriod(conf), getInitialDelay(conf), getTimeUnit(conf)); this.conn = conn; this.conf = conf; + this.metrics = metrics; this.quotaManager = quotaManager; this.snapshotNotifier = Objects.requireNonNull(snapshotNotifier); - this.tableQuotaSnapshots = new HashMap<>(); - this.namespaceQuotaSnapshots = new HashMap<>(); + this.tableQuotaSnapshots = new ConcurrentHashMap<>(); + this.readOnlyTableQuotaSnapshots = Collections.unmodifiableMap(tableQuotaSnapshots); + this.namespaceQuotaSnapshots = new ConcurrentHashMap<>(); + this.readOnlyNamespaceSnapshots = Collections.unmodifiableMap(namespaceQuotaSnapshots); } @Override @@ -116,7 +123,11 @@ public class QuotaObserverChore extends ScheduledChore { if (LOG.isTraceEnabled()) { LOG.trace("Refreshing space quotas in RegionServer"); } + long start = System.nanoTime(); _chore(); + if (null != metrics) { + metrics.incrementQuotaObserverTime((System.nanoTime() - start) / 1_000_000); + } } catch (IOException e) { LOG.warn("Failed to process quota reports and update quota state. Will retry.", e); } @@ -130,6 +141,12 @@ public class QuotaObserverChore extends ScheduledChore { LOG.trace("Found following tables with quotas: " + tablesWithQuotas); } + if (null != metrics) { + // Set the number of namespaces and tables with quotas defined + metrics.setNumSpaceQuotas(tablesWithQuotas.getTableQuotaTables().size() + + tablesWithQuotas.getNamespacesWithQuotas().size()); + } + // The current "view" of region space use. Used henceforth. final Map reportedRegionSpaceUse = quotaManager.snapshotRegionSizes(); if (LOG.isTraceEnabled()) { @@ -138,6 +155,10 @@ public class QuotaObserverChore extends ScheduledChore { // Create the stores to track table and namespace snapshots initializeSnapshotStores(reportedRegionSpaceUse); + // Report the number of (non-expired) region size reports + if (null != metrics) { + metrics.setNumRegionSizeReports(reportedRegionSpaceUse.size()); + } // Filter out tables for which we don't have adequate regionspace reports yet. // Important that we do this after we instantiate the stores above @@ -182,6 +203,7 @@ public class QuotaObserverChore extends ScheduledChore { * @param tablesWithTableQuotas The HBase tables which have quotas defined */ void processTablesWithQuotas(final Set tablesWithTableQuotas) throws IOException { + long numTablesInViolation = 0L; for (TableName table : tablesWithTableQuotas) { final SpaceQuota spaceQuota = tableSnapshotStore.getSpaceQuota(table); if (null == spaceQuota) { @@ -197,6 +219,14 @@ public class QuotaObserverChore extends ScheduledChore { LOG.trace("Processing " + table + " with current=" + currentSnapshot + ", target=" + targetSnapshot); } updateTableQuota(table, currentSnapshot, targetSnapshot); + + if (targetSnapshot.getQuotaStatus().isInViolation()) { + numTablesInViolation++; + } + } + // Report the number of tables in violation + if (null != metrics) { + metrics.setNumTableInSpaceQuotaViolation(numTablesInViolation); } } @@ -213,6 +243,7 @@ public class QuotaObserverChore extends ScheduledChore { void processNamespacesWithQuotas( final Set namespacesWithQuotas, final Multimap tablesByNamespace) throws IOException { + long numNamespacesInViolation = 0L; for (String namespace : namespacesWithQuotas) { // Get the quota definition for the namespace final SpaceQuota spaceQuota = namespaceSnapshotStore.getSpaceQuota(namespace); @@ -226,6 +257,15 @@ public class QuotaObserverChore extends ScheduledChore { final SpaceQuotaSnapshot currentSnapshot = namespaceSnapshotStore.getCurrentState(namespace); final SpaceQuotaSnapshot targetSnapshot = namespaceSnapshotStore.getTargetState(namespace, spaceQuota); updateNamespaceQuota(namespace, currentSnapshot, targetSnapshot, tablesByNamespace); + + if (targetSnapshot.getQuotaStatus().isInViolation()) { + numNamespacesInViolation++; + } + } + + // Report the number of namespaces in violation + if (null != metrics) { + metrics.setNumNamespacesInSpaceQuotaViolation(numNamespacesInViolation); } } @@ -399,6 +439,22 @@ public class QuotaObserverChore extends ScheduledChore { } /** + * Returns an unmodifiable view over the current {@link SpaceQuotaSnapshot} objects + * for each HBase table with a quota. + */ + public Map getTableQuotaSnapshots() { + return readOnlyTableQuotaSnapshots; + } + + /** + * Returns an unmodifiable view over the current {@link SpaceQuotaSnapshot} objects + * for each HBase namespace with a quota. + */ + public Map getNamespaceQuotaSnapshots() { + return readOnlyNamespaceSnapshots; + } + + /** * Fetches the {@link SpaceQuotaSnapshot} for the given table. */ SpaceQuotaSnapshot getTableQuotaSnapshot(TableName table) { diff --git a/hbase-server/src/main/resources/hbase-webapps/master/table.jsp b/hbase-server/src/main/resources/hbase-webapps/master/table.jsp index 6e23f00..3316518 100644 --- a/hbase-server/src/main/resources/hbase-webapps/master/table.jsp +++ b/hbase-server/src/main/resources/hbase-webapps/master/table.jsp @@ -39,10 +39,13 @@ import="org.apache.hadoop.hbase.HConstants" import="org.apache.hadoop.hbase.master.HMaster" import="org.apache.hadoop.hbase.zookeeper.MetaTableLocator" + import="org.apache.hadoop.hbase.quotas.QuotaTableUtil" import="org.apache.hadoop.hbase.util.Bytes" import="org.apache.hadoop.hbase.util.FSUtils" import="org.apache.hadoop.hbase.shaded.protobuf.generated.ClusterStatusProtos" import="org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos" + import="org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas" + import="org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.SpaceQuota" import="org.apache.hadoop.hbase.TableName" import="org.apache.hadoop.hbase.HColumnDescriptor" import="org.apache.hadoop.hbase.HBaseConfiguration" @@ -71,6 +74,7 @@ if (showFragmentation) { frags = FSUtils.getTableFragmentation(master); } + boolean quotasEnabled = conf.getBoolean("hbase.quota.enabled", false); String action = request.getParameter("action"); String key = request.getParameter("key"); long totalStoreFileSizeMB = 0; @@ -295,6 +299,36 @@ if ( fqtn != null ) { How fragmented is the table. After a major compaction it is 0%. <% } %> +<% + if (quotasEnabled) { + TableName tn = TableName.valueOf(fqtn); + Quotas quota = QuotaTableUtil.getTableQuota(master.getConnection(), tn); + if (null == quota || !quota.hasSpace()) { + quota = QuotaTableUtil.getNamespaceQuota(master.getConnection(), tn.getNamespaceAsString()); + } + if (null != quota && quota.hasSpace()) { + SpaceQuota spaceQuota = quota.getSpace(); +%> + + Space Quota + + + + + + + + + + +
LimitPolicy
<%= StringUtils.byteDesc(spaceQuota.getSoftLimit()) %><%= spaceQuota.getViolationPolicy() %>
+ + Information about a Space Quota on this table, if set. + +<% + } + } +%>

Table Schema

diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterMetricsWrapper.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterMetricsWrapper.java index 02f3721..25a7d47 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterMetricsWrapper.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterMetricsWrapper.java @@ -19,9 +19,14 @@ package org.apache.hadoop.hbase.master; import static org.junit.Assert.*; +import java.util.AbstractMap.SimpleImmutableEntry; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot; +import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot.SpaceQuotaStatus; +import org.apache.hadoop.hbase.quotas.SpaceViolationPolicy; import org.apache.hadoop.hbase.testclassification.MasterTests; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.util.Threads; @@ -77,4 +82,13 @@ public class TestMasterMetricsWrapper { assertEquals(1, info.getNumDeadRegionServers()); assertEquals(1, info.getNumWALFiles()); } + + @Test + public void testQuotaSnapshotConversion() { + MetricsMasterWrapperImpl info = new MetricsMasterWrapperImpl(TEST_UTIL.getHBaseCluster().getMaster()); + assertEquals(new SimpleImmutableEntry(1024L, 2048L), + info.convertSnapshot(new SpaceQuotaSnapshot(SpaceQuotaStatus.notInViolation(), 1024L, 2048L))); + assertEquals(new SimpleImmutableEntry(4096L, 2048L), + info.convertSnapshot(new SpaceQuotaSnapshot(new SpaceQuotaStatus(SpaceViolationPolicy.NO_INSERTS), 4096L, 2048L))); + } } -- 2.10.2