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();
+    }
+}
+

Reply via email to