This is an automated email from the ASF dual-hosted git repository.
li4wang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zookeeper.git
The following commit(s) were added to refs/heads/master by this push:
new dc2767f1a ZOOKEEPER-4983: Add client-triggered operation count metrics
(#2328)
dc2767f1a is described below
commit dc2767f1a91b78805c03b18abd333e4c1a1d46c3
Author: li4wang <[email protected]>
AuthorDate: Wed Jan 28 09:23:14 2026 -0800
ZOOKEEPER-4983: Add client-triggered operation count metrics (#2328)
* ZOOKEEPER-4983: Add client-triggered operation count metrics
Author: Li Wang <[email protected]>
* ZOOKEEPER-4983: Add client-triggered operation count metrics
Author: Li Wang <[email protected]>
---------
Co-authored-by: liwang <[email protected]>
---
.../zookeeper/server/FinalRequestProcessor.java | 36 +++-
.../org/apache/zookeeper/server/ServerMetrics.java | 53 +++++
.../zookeeper/server/ServerMetricsOpCountTest.java | 218 +++++++++++++++++++++
3 files changed, 305 insertions(+), 2 deletions(-)
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProcessor.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProcessor.java
index 911583f9f..9861f5ffe 100644
---
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProcessor.java
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProcessor.java
@@ -49,6 +49,7 @@
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.metrics.Counter;
import org.apache.zookeeper.proto.AddWatchRequest;
import org.apache.zookeeper.proto.CheckWatchesRequest;
import org.apache.zookeeper.proto.Create2Response;
@@ -213,6 +214,7 @@ public void processRequest(Request request) {
switch (request.type) {
case OpCode.ping: {
lastOp = "PING";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_PING);
updateStats(request, lastOp, lastZxid);
responseSize = cnxn.sendResponse(new
ReplyHeader(ClientCnxn.PING_XID, lastZxid, 0), null, "response");
@@ -220,6 +222,7 @@ public void processRequest(Request request) {
}
case OpCode.createSession: {
lastOp = "SESS";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CREATE_SESSION);
updateStats(request, lastOp, lastZxid);
zks.finishSessionInit(request.cnxn, true);
@@ -227,6 +230,7 @@ public void processRequest(Request request) {
}
case OpCode.multi: {
lastOp = "MULT";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_MULTI);
rsp = new MultiResponse();
for (ProcessTxnResult subTxnResult : rc.multiResult) {
@@ -269,6 +273,7 @@ public void processRequest(Request request) {
}
case OpCode.multiRead: {
lastOp = "MLTR";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_MULTI_READ);
MultiOperationRecord multiReadRecord =
request.readRequestRecord(MultiOperationRecord::new);
rsp = new MultiResponse();
OpResult subResult;
@@ -297,6 +302,7 @@ public void processRequest(Request request) {
}
case OpCode.create: {
lastOp = "CREA";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CREATE);
rsp = new CreateResponse(rc.path);
err = Code.get(rc.err);
requestPathMetricsCollector.registerRequest(request.type,
rc.path);
@@ -306,6 +312,7 @@ public void processRequest(Request request) {
case OpCode.createTTL:
case OpCode.createContainer: {
lastOp = "CREA";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CREATE);
rsp = new Create2Response(rc.path, rc.stat);
err = Code.get(rc.err);
requestPathMetricsCollector.registerRequest(request.type,
rc.path);
@@ -314,12 +321,14 @@ public void processRequest(Request request) {
case OpCode.delete:
case OpCode.deleteContainer: {
lastOp = "DELE";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_DELETE);
err = Code.get(rc.err);
requestPathMetricsCollector.registerRequest(request.type,
rc.path);
break;
}
case OpCode.setData: {
lastOp = "SETD";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_SET_DATA);
rsp = new SetDataResponse(rc.stat);
err = Code.get(rc.err);
requestPathMetricsCollector.registerRequest(request.type,
rc.path);
@@ -327,14 +336,16 @@ public void processRequest(Request request) {
}
case OpCode.reconfig: {
lastOp = "RECO";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_RECONFIG);
rsp = new GetDataResponse(
- ((QuorumZooKeeperServer)
zks).self.getQuorumVerifier().toString().getBytes(UTF_8),
- rc.stat);
+ ((QuorumZooKeeperServer)
zks).self.getQuorumVerifier().toString().getBytes(UTF_8),
+ rc.stat);
err = Code.get(rc.err);
break;
}
case OpCode.setACL: {
lastOp = "SETA";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_SET_ACL);
rsp = new SetACLResponse(rc.stat);
err = Code.get(rc.err);
requestPathMetricsCollector.registerRequest(request.type,
rc.path);
@@ -342,11 +353,13 @@ public void processRequest(Request request) {
}
case OpCode.closeSession: {
lastOp = "CLOS";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CLOSE_SESSION);
err = Code.get(rc.err);
break;
}
case OpCode.sync: {
lastOp = "SYNC";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_SYNC);
SyncRequest syncRequest =
request.readRequestRecord(SyncRequest::new);
rsp = new SyncResponse(syncRequest.getPath());
requestPathMetricsCollector.registerRequest(request.type,
syncRequest.getPath());
@@ -354,12 +367,14 @@ public void processRequest(Request request) {
}
case OpCode.check: {
lastOp = "CHEC";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CHECK);
rsp = new SetDataResponse(rc.stat);
err = Code.get(rc.err);
break;
}
case OpCode.exists: {
lastOp = "EXIS";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_EXISTS);
ExistsRequest existsRequest =
request.readRequestRecord(ExistsRequest::new);
path = existsRequest.getPath();
if (path.indexOf('\0') != -1) {
@@ -382,6 +397,7 @@ public void processRequest(Request request) {
}
case OpCode.getData: {
lastOp = "GETD";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_DATA);
GetDataRequest getDataRequest =
request.readRequestRecord(GetDataRequest::new);
path = getDataRequest.getPath();
rsp = handleGetDataRequest(getDataRequest, cnxn,
request.authInfo);
@@ -390,6 +406,7 @@ public void processRequest(Request request) {
}
case OpCode.setWatches: {
lastOp = "SETW";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_SET_WATCHES);
SetWatches setWatches =
request.readRequestRecord(SetWatches::new);
long relativeZxid = setWatches.getRelativeZxid();
zks.getZKDatabase()
@@ -405,6 +422,7 @@ public void processRequest(Request request) {
}
case OpCode.setWatches2: {
lastOp = "STW2";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_SET_WATCHES);
SetWatches2 setWatches =
request.readRequestRecord(SetWatches2::new);
long relativeZxid = setWatches.getRelativeZxid();
zks.getZKDatabase().setWatches(relativeZxid,
@@ -418,6 +436,7 @@ public void processRequest(Request request) {
}
case OpCode.addWatch: {
lastOp = "ADDW";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_ADD_WATCH);
AddWatchRequest addWatcherRequest =
request.readRequestRecord(AddWatchRequest::new);
zks.getZKDatabase().addWatch(addWatcherRequest.getPath(),
cnxn, addWatcherRequest.getMode());
rsp = new ErrorResponse(0);
@@ -425,6 +444,7 @@ public void processRequest(Request request) {
}
case OpCode.getACL: {
lastOp = "GETA";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_ACL);
GetACLRequest getACLRequest =
request.readRequestRecord(GetACLRequest::new);
path = getACLRequest.getPath();
DataNode n = zks.getZKDatabase().getNode(path);
@@ -467,6 +487,7 @@ public void processRequest(Request request) {
}
case OpCode.getChildren: {
lastOp = "GETC";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_CHILDREN);
GetChildrenRequest getChildrenRequest =
request.readRequestRecord(GetChildrenRequest::new);
path = getChildrenRequest.getPath();
rsp = handleGetChildrenRequest(getChildrenRequest, cnxn,
request.authInfo);
@@ -475,6 +496,7 @@ public void processRequest(Request request) {
}
case OpCode.getAllChildrenNumber: {
lastOp = "GETACN";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_ALL_CHILDREN_NUMBER);
GetAllChildrenNumberRequest getAllChildrenNumberRequest =
request.readRequestRecord(GetAllChildrenNumberRequest::new);
path = getAllChildrenNumberRequest.getPath();
DataNode n = zks.getZKDatabase().getNode(path);
@@ -494,6 +516,7 @@ public void processRequest(Request request) {
}
case OpCode.getChildren2: {
lastOp = "GETC";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_CHILDREN);
GetChildren2Request getChildren2Request =
request.readRequestRecord(GetChildren2Request::new);
Stat stat = new Stat();
path = getChildren2Request.getPath();
@@ -515,6 +538,7 @@ public void processRequest(Request request) {
}
case OpCode.checkWatches: {
lastOp = "CHKW";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_CHECK_WATCHES);
CheckWatchesRequest checkWatches =
request.readRequestRecord(CheckWatchesRequest::new);
WatcherType type = WatcherType.fromInt(checkWatches.getType());
path = checkWatches.getPath();
@@ -528,6 +552,7 @@ public void processRequest(Request request) {
}
case OpCode.removeWatches: {
lastOp = "REMW";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_REMOVE_WATCHES);
RemoveWatchesRequest removeWatches =
request.readRequestRecord(RemoveWatchesRequest::new);
WatcherType type =
WatcherType.fromInt(removeWatches.getType());
path = removeWatches.getPath();
@@ -541,11 +566,13 @@ public void processRequest(Request request) {
}
case OpCode.whoAmI: {
lastOp = "HOMI";
+ incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_WHO_AM_I);
rsp = new
WhoAmIResponse(AuthUtil.getClientInfos(request.authInfo));
break;
}
case OpCode.getEphemerals: {
lastOp = "GETE";
+
incrementOpCount(ServerMetrics.getMetrics().OP_COUNT_GET_EPHEMERALS);
GetEphemeralsRequest getEphemerals =
request.readRequestRecord(GetEphemeralsRequest::new);
String prefixPath = getEphemerals.getPrefixPath();
Set<String> allEphems =
zks.getZKDatabase().getDataTree().getEphemerals(request.sessionId);
@@ -677,4 +704,9 @@ private void updateStats(Request request, String lastOp,
long lastZxid) {
request.cnxn.updateStatsForResponse(request.cxid, lastZxid, lastOp,
request.createTime, currentTime);
}
+ private void incrementOpCount(final Counter specificCounter) {
+ final ServerMetrics metrics = ServerMetrics.getMetrics();
+ specificCounter.add(1);
+ metrics.OP_COUNT_TOTAL.add(1);
+ }
}
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java
index 07d2c1b90..dab844647 100644
---
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java
@@ -272,6 +272,32 @@ private ServerMetrics(MetricsProvider metricsProvider) {
TTL_NODE_DELETED_COUNT =
metricsContext.getCounter("ttl_node_deleted_count");
TTL_NODE_CREATED_COUNT =
metricsContext.getCounter("ttl_node_created_count");
+
+ // Operation count metrics
+ OP_COUNT_TOTAL = metricsContext.getCounter("op_count_total");
+ OP_COUNT_ADD_WATCH = metricsContext.getCounter("op_count_add_watch");
+ OP_COUNT_CHECK = metricsContext.getCounter("op_count_check");
+ OP_COUNT_CHECK_WATCHES =
metricsContext.getCounter("op_count_check_watches");
+ OP_COUNT_CLOSE_SESSION =
metricsContext.getCounter("op_count_close_session");
+ OP_COUNT_CREATE = metricsContext.getCounter("op_count_create");
+ OP_COUNT_CREATE_SESSION =
metricsContext.getCounter("op_count_create_session");
+ OP_COUNT_DELETE = metricsContext.getCounter("op_count_delete");
+ OP_COUNT_EXISTS = metricsContext.getCounter("op_count_exists");
+ OP_COUNT_GET_ACL = metricsContext.getCounter("op_count_get_acl");
+ OP_COUNT_GET_ALL_CHILDREN_NUMBER =
metricsContext.getCounter("op_count_get_all_children_number");
+ OP_COUNT_GET_CHILDREN =
metricsContext.getCounter("op_count_get_children");
+ OP_COUNT_GET_DATA = metricsContext.getCounter("op_count_get_data");
+ OP_COUNT_GET_EPHEMERALS =
metricsContext.getCounter("op_count_get_ephemerals");
+ OP_COUNT_MULTI = metricsContext.getCounter("op_count_multi");
+ OP_COUNT_MULTI_READ = metricsContext.getCounter("op_count_multi_read");
+ OP_COUNT_PING = metricsContext.getCounter("op_count_ping");
+ OP_COUNT_RECONFIG = metricsContext.getCounter("op_count_reconfig");
+ OP_COUNT_REMOVE_WATCHES =
metricsContext.getCounter("op_count_remove_watches");
+ OP_COUNT_SET_ACL = metricsContext.getCounter("op_count_set_acl");
+ OP_COUNT_SET_DATA = metricsContext.getCounter("op_count_set_data");
+ OP_COUNT_SET_WATCHES =
metricsContext.getCounter("op_count_set_watches");
+ OP_COUNT_SYNC = metricsContext.getCounter("op_count_sync");
+ OP_COUNT_WHO_AM_I = metricsContext.getCounter("op_count_who_am_i");
}
/**
@@ -556,6 +582,33 @@ private ServerMetrics(MetricsProvider metricsProvider) {
public final Counter TTL_NODE_DELETED_COUNT;
public final Counter TTL_NODE_CREATED_COUNT;
+ // Operation count metrics
+ public final Counter OP_COUNT_TOTAL;
+ public final Counter OP_COUNT_ADD_WATCH;
+ public final Counter OP_COUNT_CHECK;
+ public final Counter OP_COUNT_CHECK_WATCHES;
+ public final Counter OP_COUNT_CLOSE_SESSION;
+ public final Counter OP_COUNT_CREATE;
+ public final Counter OP_COUNT_CREATE_SESSION;
+ public final Counter OP_COUNT_DELETE;
+ public final Counter OP_COUNT_EXISTS;
+ public final Counter OP_COUNT_GET_ACL;
+ public final Counter OP_COUNT_GET_ALL_CHILDREN_NUMBER;
+ public final Counter OP_COUNT_GET_CHILDREN;
+ public final Counter OP_COUNT_GET_DATA;
+ public final Counter OP_COUNT_GET_EPHEMERALS;
+ public final Counter OP_COUNT_MULTI;
+ public final Counter OP_COUNT_MULTI_READ;
+ public final Counter OP_COUNT_PING;
+ public final Counter OP_COUNT_RECONFIG;
+ public final Counter OP_COUNT_REMOVE_WATCHES;
+ public final Counter OP_COUNT_SET_ACL;
+ public final Counter OP_COUNT_SET_DATA;
+ public final Counter OP_COUNT_SET_WATCHES;
+ public final Counter OP_COUNT_SYNC;
+ public final Counter OP_COUNT_WHO_AM_I;
+
+
private final MetricsProvider metricsProvider;
public void resetAll() {
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerMetricsOpCountTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerMetricsOpCountTest.java
new file mode 100644
index 000000000..e12976ab9
--- /dev/null
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerMetricsOpCountTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.zookeeper.server;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.metrics.MetricsUtils;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.jupiter.api.Test;
+
+public class ServerMetricsOpCountTest extends ClientBase {
+
+ // Metric name constants
+ private static final String OP_COUNT_TOTAL_METRIC_NAME = "op_count_total";
+ private static final String OP_COUNT_CREATE_METRIC_NAME =
"op_count_create";
+ private static final String OP_COUNT_EXISTS_METRIC_NAME =
"op_count_exists";
+ private static final String OP_COUNT_GET_DATA_METRIC_NAME =
"op_count_get_data";
+ private static final String OP_COUNT_SET_DATA_METRIC_NAME =
"op_count_set_data";
+ private static final String OP_COUNT_GET_ACL_METRIC_NAME =
"op_count_get_acl";
+ private static final String OP_COUNT_SET_ACL_METRIC_NAME =
"op_count_set_acl";
+ private static final String OP_COUNT_GET_CHILDREN_METRIC_NAME =
"op_count_get_children";
+ private static final String OP_COUNT_GET_ALL_CHILDREN_NUMBER_METRIC_NAME =
"op_count_get_all_children_number";
+ private static final String OP_COUNT_ADD_WATCH_METRIC_NAME =
"op_count_add_watch";
+ private static final String OP_COUNT_DELETE_METRIC_NAME =
"op_count_delete";
+ private static final String OP_COUNT_MULTI_METRIC_NAME = "op_count_multi";
+ private static final String OP_COUNT_MULTI_READ_METRIC_NAME =
"op_count_multi_read";
+ private static final String OP_COUNT_GET_EPHEMERALS_METRIC_NAME =
"op_count_get_ephemerals";
+ private static final String OP_COUNT_WHO_AM_I_METRIC_NAME =
"op_count_who_am_i";
+ private static final String OP_COUNT_CREATE_SESSION_METRIC_NAME =
"op_count_create_session";
+ private static final String OP_COUNT_CLOSE_SESSION_METRIC_NAME =
"op_count_close_session";
+
+ @Test
+ public void testBasicOpCounts() throws Exception {
+ final Map<String, Object> initialMetrics =
MetricsUtils.currentServerMetrics();
+ final long initialTotalCount = (Long)
initialMetrics.getOrDefault(OP_COUNT_TOTAL_METRIC_NAME, 0L);
+
+ final Map<String, Long> initialCounts = new HashMap<>();
+ initialCounts.put(OP_COUNT_CREATE_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_CREATE_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_EXISTS_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_EXISTS_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_GET_DATA_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_GET_DATA_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_SET_DATA_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_SET_DATA_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_GET_ACL_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_GET_ACL_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_SET_ACL_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_SET_ACL_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_GET_CHILDREN_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_GET_CHILDREN_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_GET_ALL_CHILDREN_NUMBER_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_GET_ALL_CHILDREN_NUMBER_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_ADD_WATCH_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_ADD_WATCH_METRIC_NAME, 0L));
+ initialCounts.put(OP_COUNT_DELETE_METRIC_NAME, (Long)
initialMetrics.getOrDefault(OP_COUNT_DELETE_METRIC_NAME, 0L));
+
+ try (final ZooKeeper zk = createClient()) {
+ final String path = generateUniquePath("testBasicOps");
+
+ // Create node
+ zk.create(path, "test".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
+
+ // Perform various operations
+ zk.exists(path, false);
+ zk.getData(path, false, null);
+ zk.setData(path, "updated".getBytes(), -1);
+ zk.getACL(path, null);
+
+ zk.setACL(path, Ids.READ_ACL_UNSAFE, -1);
+ zk.getChildren(path, false);
+
+ zk.getAllChildrenNumber(path);
+
+ zk.addWatch(path, event -> {},
org.apache.zookeeper.AddWatchMode.PERSISTENT);
+
+ // Delete node
+ zk.delete(path, -1);
+
+ // Verify all metrics increased
+ final Map<String, Object> finalMetrics =
MetricsUtils.currentServerMetrics();
+ final long finalTotalCount = (Long)
finalMetrics.getOrDefault(OP_COUNT_TOTAL_METRIC_NAME, 0L);
+
+ // Total count should have increased by at least 10 operations
+ assertTrue(finalTotalCount >= initialTotalCount + 10,
+ "Total count should increase by at least 10 operations,
initial: " + initialTotalCount
+ + ", final: " + finalTotalCount);
+
+ // Verify each specific metric increased
+ for (final Map.Entry<String, Long> entry :
initialCounts.entrySet()) {
+ final String metricName = entry.getKey();
+ final long initialCount = entry.getValue();
+ final long finalCount = (Long)
finalMetrics.getOrDefault(metricName, 0L);
+
+ assertTrue(finalCount > initialCount,
+ metricName + " should increase, initial: " +
initialCount
+ + ", final: " + finalCount);
+ }
+ }
+ }
+
+ @Test
+ public void testMultiOpCount() throws Exception {
+ testSingleOpCount(OP_COUNT_MULTI_METRIC_NAME, (zk, basePath) -> {
+ final String path1 = basePath + "_1";
+ final String path2 = basePath + "_2";
+ final List<org.apache.zookeeper.Op> ops = Arrays.asList(
+ org.apache.zookeeper.Op.create(path1, "test".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT),
+ org.apache.zookeeper.Op.create(path2, "test".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)
+ );
+ zk.multi(ops);
+ zk.delete(path1, -1);
+ zk.delete(path2, -1);
+ });
+ }
+
+ @Test
+ public void testMultiReadOpCount() throws Exception {
+ testSingleOpCount(OP_COUNT_MULTI_READ_METRIC_NAME, (zk, basePath) -> {
+ final String path1 = basePath + "_1";
+ final String path2 = basePath + "_2";
+
+ // Create nodes first
+ zk.create(path1, "test1".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
+ zk.create(path2, "test2".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
+
+ // Perform multi-read operation
+ final List<org.apache.zookeeper.Op> readOps = Arrays.asList(
+ org.apache.zookeeper.Op.getData(path1),
+ org.apache.zookeeper.Op.getData(path2)
+ );
+ zk.multi(readOps);
+
+ zk.delete(path1, -1);
+ zk.delete(path2, -1);
+ });
+ }
+
+ @Test
+ public void testGetEphemeralsOpCount() throws Exception {
+ testSingleOpCount(OP_COUNT_GET_EPHEMERALS_METRIC_NAME, (zk, path) -> {
+ zk.create(path, "test".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
+ zk.getEphemerals("/");
+ });
+ }
+
+ @Test
+ public void testWhoAmIOpCount() throws Exception {
+ testSingleOpCount(OP_COUNT_WHO_AM_I_METRIC_NAME, (zk, path) ->
zk.whoAmI());
+ }
+
+ @Test
+ public void testSessionOperationCounts() throws Exception {
+ final Map<String, Object> initialMetrics =
MetricsUtils.currentServerMetrics();
+ final long initialCreateCount = (Long)
initialMetrics.getOrDefault(OP_COUNT_CREATE_SESSION_METRIC_NAME, 0L);
+ final long initialCloseCount = (Long)
initialMetrics.getOrDefault(OP_COUNT_CLOSE_SESSION_METRIC_NAME, 0L);
+
+ // Create and close a new client to trigger session operations
+ final ZooKeeper zk = createClient();
+ zk.close();
+
+ final Map<String, Object> finalMetrics =
MetricsUtils.currentServerMetrics();
+ final long finalCreateCount = (Long)
finalMetrics.getOrDefault(OP_COUNT_CREATE_SESSION_METRIC_NAME, 0L);
+ final long finalCloseCount = (Long)
finalMetrics.getOrDefault(OP_COUNT_CLOSE_SESSION_METRIC_NAME, 0L);
+
+ assertTrue(finalCreateCount > initialCreateCount, "Create session
count should not decrease");
+ assertTrue(finalCloseCount > initialCloseCount, "Close session count
should not decrease");
+ }
+
+ private void testSingleOpCount(final String metricName, final
OperationExecutor executor) throws Exception {
+ final Map<String, Object> initialMetrics =
MetricsUtils.currentServerMetrics();
+ final long initialOpCount = (Long)
initialMetrics.getOrDefault(metricName, 0L);
+ final long initialTotalCount = (Long)
initialMetrics.getOrDefault(OP_COUNT_TOTAL_METRIC_NAME, 0L);
+
+ try (final ZooKeeper zk = createClient()) {
+ final String path = generateUniquePath("test" +
metricName.replace("_op_count", ""));
+
+ // Execute the operation
+ executor.execute(zk, path);
+
+ // Verify metrics increased
+ final Map<String, Object> finalMetrics =
MetricsUtils.currentServerMetrics();
+ final long finalOpCount = (Long)
finalMetrics.getOrDefault(metricName, 0L);
+ final long finalTotalCount = (Long)
finalMetrics.getOrDefault(OP_COUNT_TOTAL_METRIC_NAME, 0L);
+
+ assertTrue(finalTotalCount > initialTotalCount,
+ "Total count should increase after " + metricName + "
operations"
+ + " initial: " + initialTotalCount + ", final: " +
finalTotalCount);
+
+ assertTrue(finalOpCount > initialOpCount,
+ metricName + " should increase, initial: " + initialOpCount
+ + ", final: " + finalOpCount);
+ }
+ }
+
+ @FunctionalInterface
+ private interface OperationExecutor {
+ void execute(ZooKeeper zk, String path) throws Exception;
+ }
+
+ private String generateUniquePath(final String baseName) {
+ return "/" + baseName + "_" + System.nanoTime();
+ }
+}
+