diff --git a/hbase-client/pom.xml b/hbase-client/pom.xml index d76049fdc0e..928ba039b8b 100644 --- a/hbase-client/pom.xml +++ b/hbase-client/pom.xml @@ -110,6 +110,14 @@ org.apache.hbase hbase-common + + org.apache.hbase + hbase-hadoop-compat + + + org.apache.hbase + hbase-hadoop2-compat + org.apache.hbase hbase-common diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeper.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeper.java new file mode 100644 index 00000000000..6b5e1883170 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeper.java @@ -0,0 +1,110 @@ +/** + * + * 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.zookeeper; + +import org.apache.hadoop.hbase.CompatibilitySingletonFactory; +import org.apache.hadoop.hbase.classification.InterfaceAudience; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.hadoop.hbase.regionserver.wal.MetricsWALSource; +import org.apache.hadoop.hbase.regionserver.wal.MetricsWALSourceImpl; + +/** + * Class used to push numbers about ZooKeeper into the metrics subsystem. This will take a + * single function call and turn it into multiple manipulations of the hadoop metrics system. + */ +@InterfaceAudience.Private +public class MetricsZooKeeper implements ZooKeeperMetricsListener { + private final MetricsZooKeeperSource source; + + public MetricsZooKeeper() { + this(CompatibilitySingletonFactory.getInstance(MetricsZooKeeperSource.class)); + } + + @VisibleForTesting + public MetricsZooKeeper(MetricsZooKeeperSource s) { + this.source = s; + } + + @Override + public void registerAuthFailedException() { + source.incrementAuthFailedCount(); + } + + @Override + public void registerConnectionLossException() { + source.incrementConnectionLossCount(); + } + + @Override + public void registerDataInconsistencyException() { + source.incrementDataInconsistencyCount(); + } + + @Override + public void registerInvalidACLException() { + source.incrementInvalidACLCount(); + } + + @Override + public void registerNoAuthException() { + source.incrementNoAuthCount(); + } + + @Override + public void registerOperationTimeoutException() { + source.incrementOperationTimeoutCount(); + } + + @Override + public void registerRuntimeInconsistencyException() { + source.incrementRuntimeInconsistencyCount(); + } + + @Override + public void registerSessionExpiredException() { + source.incrementSessionExpiredCount(); + } + + @Override + public void registerSystemErrorException() { + source.incrementSystemErrorCount(); + } + + @Override + public void registerFailedZKCall() { + source.incrementTotalFailedZKCalls(); + } + + @Override + public void registerReadOperationLatency(long latency) { + source.recordReadOperationLatency(latency); + } + + @Override + public void registerWriteOperationLatency(long latency) { + source.recordWriteOperationLatency(latency); + } + + @Override + public void registerSyncOperationLatency(long latency) { + source.recordSyncOperationLatency(latency); + } +} \ No newline at end of file diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/RecoverableZooKeeper.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/RecoverableZooKeeper.java index 4f07d5b65a8..e0a950d6f5d 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/RecoverableZooKeeper.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/RecoverableZooKeeper.java @@ -31,6 +31,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.RetryCounter; import org.apache.hadoop.hbase.util.RetryCounterFactory; +import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; @@ -83,6 +84,7 @@ public class RecoverableZooKeeper { private int sessionTimeout; private String quorumServers; private final Random salter; + private final ZooKeeperMetricsListener metrics; // The metadata attached to each piece of data has the // format: @@ -125,6 +127,7 @@ public class RecoverableZooKeeper { this.watcher = watcher; this.sessionTimeout = sessionTimeout; this.quorumServers = quorumServers; + this.metrics = new MetricsZooKeeper(); try {checkZk();} catch (Exception x) {/* ignore */} salter = new Random(); } @@ -175,9 +178,12 @@ public class RecoverableZooKeeper { boolean isRetry = false; // False for first attempt, true for all retries. while (true) { try { + long startTime = EnvironmentEdgeManager.currentTime(); checkZk().delete(path, version); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); return; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case NONODE: if (isRetry) { @@ -189,7 +195,11 @@ public class RecoverableZooKeeper { throw e; case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "delete"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "delete"); break; @@ -217,11 +227,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().exists(path, watcher); + long startTime = EnvironmentEdgeManager.currentTime(); + Stat nodeStat = checkZk().exists(path, watcher); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodeStat; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "exists"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "exists"); break; @@ -248,11 +266,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().exists(path, watch); + long startTime = EnvironmentEdgeManager.currentTime(); + Stat nodeStat = checkZk().exists(path, watch); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodeStat; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "exists"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "exists"); break; @@ -289,11 +315,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().getChildren(path, watcher); + long startTime = EnvironmentEdgeManager.currentTime(); + List children = checkZk().getChildren(path, watcher); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return children; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "getChildren"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "getChildren"); break; @@ -320,11 +354,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().getChildren(path, watch); + long startTime = EnvironmentEdgeManager.currentTime(); + List children = checkZk().getChildren(path, watch); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return children; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "getChildren"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "getChildren"); break; @@ -351,12 +393,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { + long startTime = EnvironmentEdgeManager.currentTime(); byte[] revData = checkZk().getData(path, watcher, stat); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); return removeMetaData(revData); } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "getData"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "getData"); break; @@ -372,7 +421,7 @@ public class RecoverableZooKeeper { } /** - * getData is an idemnpotent operation. Retry before throwing exception + * getData is an idempotent operation. Retry before throwing exception * @return Data */ public byte[] getData(String path, boolean watch, Stat stat) @@ -383,12 +432,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { + long startTime = EnvironmentEdgeManager.currentTime(); byte[] revData = checkZk().getData(path, watch, stat); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); return removeMetaData(revData); } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "getData"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "getData"); break; @@ -417,13 +473,22 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); byte[] newData = appendMetaData(data); boolean isRetry = false; + long startTime; while (true) { try { - return checkZk().setData(path, newData, version); + startTime = EnvironmentEdgeManager.currentTime(); + Stat nodeStat = checkZk().setData(path, newData, version); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodeStat; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "setData"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "setData"); break; case BADVERSION: @@ -431,12 +496,15 @@ public class RecoverableZooKeeper { // try to verify whether the previous setData success or not try{ Stat stat = new Stat(); + startTime = EnvironmentEdgeManager.currentTime(); byte[] revData = checkZk().getData(path, false, stat); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); if(Bytes.compareTo(revData, newData) == 0) { // the bad version is caused by previous successful setData return stat; } } catch(KeeperException keeperException){ + this.metrics.registerFailedZKCall(); // the ZK is not reliable at this moment. just throwing exception throw keeperException; } @@ -466,11 +534,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().getACL(path, stat); + long startTime = EnvironmentEdgeManager.currentTime(); + List nodeACL = checkZk().getACL(path, stat); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodeACL; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "getAcl"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "getAcl"); break; @@ -497,11 +573,19 @@ public class RecoverableZooKeeper { RetryCounter retryCounter = retryCounterFactory.create(); while (true) { try { - return checkZk().setACL(path, acls, version); + long startTime = EnvironmentEdgeManager.currentTime(); + Stat nodeStat = checkZk().setACL(path, acls, version); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodeStat; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "setAcl"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "setAcl"); break; @@ -560,17 +644,24 @@ public class RecoverableZooKeeper { CreateMode createMode) throws KeeperException, InterruptedException { RetryCounter retryCounter = retryCounterFactory.create(); boolean isRetry = false; // False for first attempt, true for all retries. + long startTime; while (true) { try { - return checkZk().create(path, data, acl, createMode); + startTime = EnvironmentEdgeManager.currentTime(); + String nodePath = checkZk().create(path, data, acl, createMode); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodePath; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case NODEEXISTS: if (isRetry) { // If the connection was lost, there is still a possibility that // we have successfully created the node at our previous attempt, // so we read the node and compare. + startTime = EnvironmentEdgeManager.currentTime(); byte[] currentData = checkZk().getData(path, false, null); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); if (currentData != null && Bytes.compareTo(currentData, data) == 0) { // We successfully created a non-sequential node @@ -585,7 +676,11 @@ public class RecoverableZooKeeper { throw e; case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "create"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "create"); break; @@ -614,11 +709,19 @@ public class RecoverableZooKeeper { } } first = false; - return checkZk().create(newPath, data, acl, createMode); + long startTime = EnvironmentEdgeManager.currentTime(); + String nodePath = checkZk().create(newPath, data, acl, createMode); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return nodePath; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "create"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "create"); break; @@ -669,11 +772,19 @@ public class RecoverableZooKeeper { Iterable multiOps = prepareZKMulti(ops); while (true) { try { - return checkZk().multi(multiOps); + long startTime = EnvironmentEdgeManager.currentTime(); + List opResults = checkZk().multi(multiOps); + this.metrics.registerWriteOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); + return opResults; } catch (KeeperException e) { + this.metrics.registerFailedZKCall(); switch (e.code()) { case CONNECTIONLOSS: + this.metrics.registerConnectionLossException(); + retryOrThrow(retryCounter, e, "multi"); + break; case OPERATIONTIMEOUT: + this.metrics.registerOperationTimeoutException(); retryOrThrow(retryCounter, e, "multi"); break; @@ -682,7 +793,7 @@ public class RecoverableZooKeeper { } } retryCounter.sleepUntilNextRetry(); - } + } } finally { if (traceScope != null) traceScope.close(); } @@ -694,12 +805,15 @@ public class RecoverableZooKeeper { assert(lastSlashIdx != -1); String parent = path.substring(0, lastSlashIdx); String nodePrefix = path.substring(lastSlashIdx+1); - + long startTime = EnvironmentEdgeManager.currentTime(); List nodes = checkZk().getChildren(parent, false); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); List matching = filterByPrefix(nodes, nodePrefix); for (String node : matching) { String nodePath = parent + "/" + node; + startTime = EnvironmentEdgeManager.currentTime(); Stat stat = checkZk().exists(nodePath, false); + this.metrics.registerReadOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); if (stat != null) { return nodePath; } @@ -763,7 +877,9 @@ public class RecoverableZooKeeper { } public void sync(String path, AsyncCallback.VoidCallback cb, Object ctx) throws KeeperException { + long startTime = EnvironmentEdgeManager.currentTime(); checkZk().sync(path, cb, null); + this.metrics.registerSyncOperationLatency(Math.min(EnvironmentEdgeManager.currentTime() - startTime, 1)); } /** @@ -792,4 +908,4 @@ public class RecoverableZooKeeper { public String getIdentifier() { return identifier; } -} +} \ No newline at end of file diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperMetricsListener.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperMetricsListener.java new file mode 100644 index 00000000000..222affc8c71 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperMetricsListener.java @@ -0,0 +1,91 @@ +/* + * + * 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.zookeeper; + +import org.apache.hadoop.hbase.classification.InterfaceAudience; + +@InterfaceAudience.Private +public interface ZooKeeperMetricsListener { + + /** + * An AUTHFAILED Exception was seen. + */ + void registerAuthFailedException(); + + /** + * A CONNECTIONLOSS Exception was seen. + */ + void registerConnectionLossException(); + + /** + * A DATAINCONSISTENCY Exception was seen. + */ + void registerDataInconsistencyException(); + + /** + * An INVALIDACL Exception was seen. + */ + void registerInvalidACLException(); + + /** + * A NOAUTH Exception was seen. + */ + void registerNoAuthException(); + + /** + * A OPERATIONTIMEOUT Exception was seen. + */ + void registerOperationTimeoutException(); + + /** + * A RUNTIMEINCONSISTENCY Exception was seen. + */ + void registerRuntimeInconsistencyException(); + + /** + * A SESSIONEXPIRED Exception was seen. + */ + void registerSessionExpiredException(); + + /** + * A SYSTEMERROR Exception was seen. + */ + void registerSystemErrorException(); + + /** + * A ZooKeeper API Call failed. + */ + void registerFailedZKCall(); + + /** + * Register the latency incurred for read operations. + */ + void registerReadOperationLatency(long latency); + + /** + * Register the latency incurred for write operations. + */ + void registerWriteOperationLatency(long latency); + + /** + * Register the latency incurred for sync operations. + */ + void registerSyncOperationLatency(long latency); +} diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeper.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeper.java new file mode 100644 index 00000000000..9d7a4950edc --- /dev/null +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeper.java @@ -0,0 +1,77 @@ +/** + * + * 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.zookeeper; + +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import static org.mockito.Mockito.*; + +@Category(SmallTests.class) +public class TestMetricsZooKeeper { + + @Test + public void testRegisterExceptions() { + MetricsZooKeeperSource zkSource = mock(MetricsZooKeeperSourceImpl.class); + MetricsZooKeeper metricsZK = new MetricsZooKeeper(zkSource); + metricsZK.registerAuthFailedException(); + metricsZK.registerConnectionLossException(); + metricsZK.registerConnectionLossException(); + metricsZK.registerDataInconsistencyException(); + metricsZK.registerInvalidACLException(); + metricsZK.registerNoAuthException(); + metricsZK.registerOperationTimeoutException(); + metricsZK.registerOperationTimeoutException(); + metricsZK.registerRuntimeInconsistencyException(); + metricsZK.registerSessionExpiredException(); + metricsZK.registerSystemErrorException(); + metricsZK.registerSystemErrorException(); + metricsZK.registerFailedZKCall(); + + verify(zkSource, times(1)).incrementAuthFailedCount(); + // ConnectionLoss Exception was registered twice. + verify(zkSource, times(2)).incrementConnectionLossCount(); + verify(zkSource, times(1)).incrementDataInconsistencyCount(); + verify(zkSource, times(1)).incrementInvalidACLCount(); + verify(zkSource, times(1)).incrementNoAuthCount(); + // OperationTimeout Exception was registered twice. + verify(zkSource, times(2)).incrementOperationTimeoutCount(); + verify(zkSource, times(1)).incrementRuntimeInconsistencyCount(); + verify(zkSource, times(1)).incrementSessionExpiredCount(); + // SystemError Exception was registered twice. + verify(zkSource, times(2)).incrementSystemErrorCount(); + verify(zkSource, times(1)).incrementTotalFailedZKCalls(); + } + + @Test + public void testLatencyHistogramUpdates() { + MetricsZooKeeperSource zkSource = mock(MetricsZooKeeperSourceImpl.class); + MetricsZooKeeper metricsZK = new MetricsZooKeeper(zkSource); + long latency = 100; + + metricsZK.registerReadOperationLatency(latency); + metricsZK.registerReadOperationLatency(latency); + metricsZK.registerWriteOperationLatency(latency); + metricsZK.registerSyncOperationLatency(latency); + // Read Operation Latency update was registered twice. + verify(zkSource, times(2)).recordReadOperationLatency(latency); + verify(zkSource, times(1)).recordWriteOperationLatency(latency); + verify(zkSource, times(1)).recordSyncOperationLatency(latency); + } +} \ No newline at end of file diff --git a/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSource.java b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSource.java new file mode 100644 index 00000000000..0b54b7b4c98 --- /dev/null +++ b/hbase-hadoop-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSource.java @@ -0,0 +1,139 @@ +/** + * 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.zookeeper; + +import org.apache.hadoop.hbase.metrics.BaseSource; + +/** + * Interface of the source that will export metrics about the ZooKeeper. + */ +public interface MetricsZooKeeperSource extends BaseSource { + + /** + * The name of the metrics + */ + String METRICS_NAME = "ZOOKEEPER"; + + /** + * The name of the metrics context that metrics will be under. + */ + String METRICS_CONTEXT = "zookeeper"; + + /** + * Description + */ + String METRICS_DESCRIPTION = "Metrics about ZooKeeper"; + + /** + * The name of the metrics context that metrics will be under in jmx. + */ + String METRICS_JMX_CONTEXT = "ZooKeeper,sub=" + METRICS_NAME; + + String EXCEPTION_AUTHFAILED = "AUTHFAILED Exception"; + String EXCEPTION_AUTHFAILED_DESC = "Number of failed ops due to an AUTHFAILED exception,"; + String EXCEPTION_CONNECTIONLOSS = "CONNECTIONLOSS Exception"; + String EXCEPTION_CONNECTIONLOSS_DESC = "Number of failed ops due to a CONNECTIONLOSS exception."; + String EXCEPTION_DATAINCONSISTENCY = "DATAINCONSISTENCY Exception"; + String EXCEPTION_DATAINCONSISTENCY_DESC = "Number of failed ops due to a DATAINCONSISTENCY exception."; + String EXCEPTION_INVALIDACL = "INVALIDACL Exception"; + String EXCEPTION_INVALIDACL_DESC = "Number of failed ops due to an INVALIDACL exception"; + String EXCEPTION_NOAUTH = "NOAUTH Exception"; + String EXCEPTION_NOAUTH_DESC = "Number of failed ops due to a NOAUTH exception."; + String EXCEPTION_OPERATIONTIMEOUT = "OPERATIONTIMEOUT Exception"; + String EXCEPTION_OPERATIONTIMEOUT_DESC = "Number of failed ops due to an OPERATIONTIMEOUT exception."; + String EXCEPTION_RUNTIMEINCONSISTENCY = "RUNTIMEINCONSISTENCY Exception"; + String EXCEPTION_RUNTIMEINCONSISTENCY_DESC = "Number of failed ops due to a RUNTIMEINCONSISTENCY exception."; + String EXCEPTION_SESSIONEXPIRED = "SESSIONEXPIRED Exception"; + String EXCEPTION_SESSIONEXPIRED_DESC = "Number of failed ops due to a SESSIONEXPIRED exception."; + String EXCEPTION_SYSTEMERROR = "SYSTEMERROR Exception"; + String EXCEPTION_SYSTEMERROR_DESC = "Number of failed ops due to a SYSTEMERROR exception."; + String TOTAL_FAILED_ZK_CALLS = "TotalFailedZKCalls"; + String TOTAL_FAILED_ZK_CALLS_DESC = "Total number of failed ZooKeeper API Calls"; + + String READ_OPERATION_LATENCY_NAME = "ReadOperationLatency"; + String READ_OPERATION_LATENCY_DESC = "Latency histogram for read operations."; + String WRITE_OPERATION_LATENCY_NAME = "WriteOperationLatency"; + String WRITE_OPERATION_LATENCY_DESC = "Latency histogram for write operations."; + String SYNC_OPERATION_LATENCY_NAME = "SyncOperationLatency"; + String SYNC_OPERATION_LATENCY_DESC = "Latency histogram for sync operations."; + + /** + * Increment the count of failed ops due to AUTHFAILED Exception. + */ + void incrementAuthFailedCount(); + + /** + * Increment the count of failed ops due to a CONNECTIONLOSS Exception. + */ + void incrementConnectionLossCount(); + + /** + * Increment the count of failed ops due to a DATAINCONSISTENCY Exception. + */ + void incrementDataInconsistencyCount(); + + /** + * Increment the count of failed ops due to INVALIDACL Exception. + */ + void incrementInvalidACLCount(); + + /** + * Increment the count of failed ops due to NOAUTH Exception. + */ + void incrementNoAuthCount(); + + /** + * Increment the count of failed ops due to an OPERATIONTIMEOUT Exception. + */ + void incrementOperationTimeoutCount(); + + /** + * Increment the count of failed ops due to RUNTIMEINCONSISTENCY Exception. + */ + void incrementRuntimeInconsistencyCount(); + + /** + * Increment the count of failed ops due to a SESSIONEXPIRED Exception. + */ + void incrementSessionExpiredCount(); + + /** + * Increment the count of failed ops due to a SYSTEMERROR Exception. + */ + void incrementSystemErrorCount(); + + /** + * Record the latency incurred for read operations. + */ + void recordReadOperationLatency(long latency); + + /** + * Record the latency incurred for write operations. + */ + void recordWriteOperationLatency(long latency); + + /** + * Record the latency incurred for sync operations. + */ + void recordSyncOperationLatency(long latency); + + /** + * Record the total number of failed ZooKeeper API calls. + */ + void incrementTotalFailedZKCalls(); +} \ No newline at end of file diff --git a/hbase-hadoop-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSource.java b/hbase-hadoop-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSource.java new file mode 100644 index 00000000000..ba45f0a6a09 --- /dev/null +++ b/hbase-hadoop-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSource.java @@ -0,0 +1,34 @@ +/** + * 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.zookeeper; + +import org.apache.hadoop.hbase.CompatibilitySingletonFactory; +import org.apache.hadoop.hbase.testclassification.MetricsTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category({MetricsTests.class, SmallTests.class}) +public class TestMetricsZooKeeperSource { + + @Test(expected=RuntimeException.class) + public void testGetInstanceNoHadoopCompat() throws Exception { + //This should throw an exception because there is no compat lib on the class path. + CompatibilitySingletonFactory.getInstance(MetricsZooKeeperSource.class); + } +} \ No newline at end of file diff --git a/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSourceImpl.java b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSourceImpl.java new file mode 100644 index 00000000000..13ab1305489 --- /dev/null +++ b/hbase-hadoop2-compat/src/main/java/org/apache/hadoop/hbase/zookeeper/MetricsZooKeeperSourceImpl.java @@ -0,0 +1,164 @@ +/** + * 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.zookeeper; + +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.metrics.BaseSourceImpl; +import org.apache.hadoop.metrics2.MetricsCollector; +import org.apache.hadoop.metrics2.lib.MutableGaugeLong; +import org.apache.hadoop.metrics2.lib.MutableHistogram; + +/** + * Class that transitions metrics from MetricsZooKeeper into the metrics subsystem. + * + * Implements BaseSource through BaseSourceImpl, following the pattern. + */ +@InterfaceAudience.Private +public class MetricsZooKeeperSourceImpl extends BaseSourceImpl implements MetricsZooKeeperSource { + + private final MutableGaugeLong authFailedFailedOpCount; + private final MutableGaugeLong connectionLossFailedOpCount; + private final MutableGaugeLong dataInconsistencyFailedOpCount; + private final MutableGaugeLong invalidACLFailedOpCount; + private final MutableGaugeLong noAuthFailedOpCount; + private final MutableGaugeLong operationTimeOutFailedOpCount; + private final MutableGaugeLong runtimeInconsistencyFailedOpCount; + private final MutableGaugeLong sessionExpiredFailedOpCount; + private final MutableGaugeLong systemErrorFailedOpCount; + private final MutableGaugeLong totalFailedZKCalls; + + private MutableHistogram readOpLatency; + private MutableHistogram writeOpLatency; + private MutableHistogram syncOpLatency; + + public MetricsZooKeeperSourceImpl() { + this(METRICS_NAME, METRICS_DESCRIPTION, METRICS_CONTEXT, METRICS_JMX_CONTEXT); + } + + public MetricsZooKeeperSourceImpl(String metricsName, String metricsDescription, String metricsContext, + String metricsJmxContext) { + super(metricsName, metricsDescription, metricsContext, metricsJmxContext); + + //Create and store the metrics that will be used. + authFailedFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_AUTHFAILED, EXCEPTION_AUTHFAILED_DESC, 0L); + connectionLossFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_CONNECTIONLOSS, EXCEPTION_CONNECTIONLOSS_DESC, 0L); + dataInconsistencyFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_DATAINCONSISTENCY, EXCEPTION_DATAINCONSISTENCY_DESC, 0L); + invalidACLFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_INVALIDACL, EXCEPTION_INVALIDACL_DESC, 0L); + noAuthFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_NOAUTH, EXCEPTION_NOAUTH_DESC, 0L); + operationTimeOutFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_OPERATIONTIMEOUT, EXCEPTION_OPERATIONTIMEOUT_DESC, 0L); + runtimeInconsistencyFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_RUNTIMEINCONSISTENCY, EXCEPTION_RUNTIMEINCONSISTENCY_DESC, 0L); + sessionExpiredFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_SESSIONEXPIRED, EXCEPTION_SESSIONEXPIRED_DESC, 0L); + systemErrorFailedOpCount = this.getMetricsRegistry().newGauge(EXCEPTION_SYSTEMERROR, EXCEPTION_SYSTEMERROR_DESC, 0L); + totalFailedZKCalls = this.getMetricsRegistry().newGauge(TOTAL_FAILED_ZK_CALLS, TOTAL_FAILED_ZK_CALLS_DESC, 0L); + + readOpLatency = this.getMetricsRegistry().newHistogram(READ_OPERATION_LATENCY_NAME, READ_OPERATION_LATENCY_DESC); + writeOpLatency = this.getMetricsRegistry().newHistogram(WRITE_OPERATION_LATENCY_NAME, WRITE_OPERATION_LATENCY_DESC); + syncOpLatency = this.getMetricsRegistry().newHistogram(SYNC_OPERATION_LATENCY_NAME, SYNC_OPERATION_LATENCY_DESC); + } + + public void getMetrics(MetricsCollector metricsCollector, boolean all) { + super.getMetrics(metricsCollector, all); + clearZKExceptionMetrics(); + } + + private void clearZKExceptionMetrics() { + //Reset the exception metrics. + clearMetricIfNotNull(authFailedFailedOpCount); + clearMetricIfNotNull(connectionLossFailedOpCount); + clearMetricIfNotNull(dataInconsistencyFailedOpCount); + clearMetricIfNotNull(invalidACLFailedOpCount); + clearMetricIfNotNull(noAuthFailedOpCount); + clearMetricIfNotNull(operationTimeOutFailedOpCount); + clearMetricIfNotNull(runtimeInconsistencyFailedOpCount); + clearMetricIfNotNull(sessionExpiredFailedOpCount); + clearMetricIfNotNull(systemErrorFailedOpCount); + clearMetricIfNotNull(totalFailedZKCalls); + } + + private static void clearMetricIfNotNull(MutableGaugeLong metric) { + if (metric != null) { + metric.set(0L); + } + } + + @Override + public void incrementAuthFailedCount() { + authFailedFailedOpCount.incr(); + } + + @Override + public void incrementConnectionLossCount() { + connectionLossFailedOpCount.incr(); + } + + @Override + public void incrementDataInconsistencyCount() { + dataInconsistencyFailedOpCount.incr(); + } + + @Override + public void incrementInvalidACLCount() { + invalidACLFailedOpCount.incr(); + } + + @Override + public void incrementNoAuthCount() { + noAuthFailedOpCount.incr(); + } + + @Override + public void incrementOperationTimeoutCount() { + operationTimeOutFailedOpCount.incr(); + } + + @Override + public void incrementRuntimeInconsistencyCount() { + runtimeInconsistencyFailedOpCount.incr(); + } + + @Override + public void incrementSessionExpiredCount() { + sessionExpiredFailedOpCount.incr(); + } + + @Override + public void incrementSystemErrorCount() { + systemErrorFailedOpCount.incr(); + } + + @Override + public void incrementTotalFailedZKCalls() { + totalFailedZKCalls.incr(); + } + + @Override + public void recordReadOperationLatency(long latency) { + readOpLatency.add(latency); + } + + @Override + public void recordWriteOperationLatency(long latency) { + writeOpLatency.add(latency); + } + + @Override + public void recordSyncOperationLatency(long latency) { + syncOpLatency.add(latency); + } +} \ No newline at end of file diff --git a/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.zookeeper.MetricsZooKeeperSource b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.zookeeper.MetricsZooKeeperSource new file mode 100644 index 00000000000..43f9a227f73 --- /dev/null +++ b/hbase-hadoop2-compat/src/main/resources/META-INF/services/org.apache.hadoop.hbase.zookeeper.MetricsZooKeeperSource @@ -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.zookeeper.MetricsZooKeeperSourceImpl diff --git a/hbase-hadoop2-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSourceImpl.java b/hbase-hadoop2-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSourceImpl.java new file mode 100644 index 00000000000..ce1a62ef174 --- /dev/null +++ b/hbase-hadoop2-compat/src/test/java/org/apache/hadoop/hbase/zookeeper/TestMetricsZooKeeperSourceImpl.java @@ -0,0 +1,38 @@ +/** + * 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.zookeeper; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import org.apache.hadoop.hbase.CompatibilitySingletonFactory; +import org.apache.hadoop.hbase.testclassification.MetricsTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category({MetricsTests.class, SmallTests.class}) +public class TestMetricsZooKeeperSourceImpl { + + @Test + public void testGetInstance() throws Exception { + MetricsZooKeeperSource zkSource = CompatibilitySingletonFactory.getInstance(MetricsZooKeeperSource.class); + assertTrue(zkSource instanceof MetricsZooKeeperSourceImpl); + assertSame(zkSource, CompatibilitySingletonFactory.getInstance(MetricsZooKeeperSource.class)); + } +} \ No newline at end of file diff --git a/hbase-metrics-api/src/main/java/org/apache/hadoop/hbase/metrics/PackageMarker.java b/hbase-metrics-api/src/main/java/org/apache/hadoop/hbase/metrics/PackageMarker.java index 8278d0cac54..f0be2ce52ab 100644 --- a/hbase-metrics-api/src/main/java/org/apache/hadoop/hbase/metrics/PackageMarker.java +++ b/hbase-metrics-api/src/main/java/org/apache/hadoop/hbase/metrics/PackageMarker.java @@ -20,6 +20,8 @@ package org.apache.hadoop.hbase.metrics; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.apache.hadoop.hbase.classification.InterfaceAudience; + /** * This is a dummy annotation that forces javac to produce output for * otherwise empty package-info.java. @@ -32,5 +34,6 @@ import java.lang.annotation.RetentionPolicy; * maven-compiler-plugin: incremental compilation broken */ @Retention(RetentionPolicy.SOURCE) +@InterfaceAudience.Private public @interface PackageMarker { }