You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by cc...@apache.org on 2026/08/25 15:27:43 UTC
(hbase) branch branch-2 updated: HBASE-30332 Preserve QueryMetrics for empty results (#8555) (#8565)
This is an automated email from the ASF dual-hosted git repository.
charlesconnell pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2 by this push:
new a152d46786e HBASE-30332 Preserve QueryMetrics for empty results (#8555) (#8565)
a152d46786e is described below
commit a152d46786ef8395da9c1c0a207fb3c9b514c3e3
Author: Ma Zhengxuan <49...@users.noreply.github.com>
AuthorDate: Tue Aug 25 23:27:35 2026 +0800
HBASE-30332 Preserve QueryMetrics for empty results (#8555) (#8565)
Co-authored-by: mazhengxuan <ma...@didiglobal.com>
Signed-off by: Peng Lu <lu...@apache.org>
Signed-off by: Charles Connell <cc...@apache.org>
---
.../hadoop/hbase/shaded/protobuf/ProtobufUtil.java | 55 +++++++++++++++++-----
.../hbase/shaded/protobuf/TestProtobufUtil.java | 38 +++++++++++++++
.../hadoop/hbase/regionserver/RSRpcServices.java | 4 +-
.../hbase/client/TestAsyncTableQueryMetrics.java | 19 ++++++++
4 files changed, 102 insertions(+), 14 deletions(-)
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
index feaa3a4ec7f..1680406a2ad 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
@@ -1382,12 +1382,13 @@ public final class ProtobufUtil {
*/
public static ClientProtos.Result toResult(final Result result, boolean encodeTags) {
if (result.getExists() != null) {
- return toResult(result.getExists(), result.isStale());
+ return toResult(result.getExists(), result.isStale(), result.getMetrics());
}
Cell[] cells = result.rawCells();
if (cells == null || cells.length == 0) {
- return result.isStale() ? EMPTY_RESULT_PB_STALE : EMPTY_RESULT_PB;
+ return withMetrics(result.isStale() ? EMPTY_RESULT_PB_STALE : EMPTY_RESULT_PB,
+ result.getMetrics());
}
ClientProtos.Result.Builder builder = ClientProtos.Result.newBuilder();
@@ -1418,6 +1419,24 @@ public final class ProtobufUtil {
}
}
+ /**
+ * Convert a client Result to a protocol buffer Result
+ * @param existence the client existence to send
+ * @param stale whether the result is stale
+ * @param metrics query metrics associated with the result
+ * @return the converted protocol buffer Result
+ */
+ public static ClientProtos.Result toResult(final boolean existence, boolean stale,
+ QueryMetrics metrics) {
+ return withMetrics(toResult(existence, stale), metrics);
+ }
+
+ private static ClientProtos.Result withMetrics(ClientProtos.Result result, QueryMetrics metrics) {
+ return metrics == null
+ ? result
+ : result.toBuilder().setMetrics(toQueryMetrics(metrics)).build();
+ }
+
/**
* Convert a client Result to a protocol buffer Result. The pb Result does not include the Cell
* data. That is for transport otherwise.
@@ -1425,9 +1444,14 @@ public final class ProtobufUtil {
* @return the converted protocol buffer Result
*/
public static ClientProtos.Result toResultNoData(final Result result) {
- if (result.getExists() != null) return toResult(result.getExists(), result.isStale());
+ if (result.getExists() != null) {
+ return toResult(result.getExists(), result.isStale(), result.getMetrics());
+ }
int size = result.size();
- if (size == 0) return result.isStale() ? EMPTY_RESULT_PB_STALE : EMPTY_RESULT_PB;
+ if (size == 0) {
+ return withMetrics(result.isStale() ? EMPTY_RESULT_PB_STALE : EMPTY_RESULT_PB,
+ result.getMetrics());
+ }
ClientProtos.Result.Builder builder = ClientProtos.Result.newBuilder();
builder.setAssociatedCellCount(size);
builder.setStale(result.isStale());
@@ -1458,6 +1482,11 @@ public final class ProtobufUtil {
*/
public static Result toResult(final ClientProtos.Result proto, boolean decodeTags) {
if (proto.hasExists()) {
+ if (proto.hasMetrics()) {
+ Result result = Result.create((Cell[]) null, proto.getExists(), proto.getStale());
+ result.setMetrics(toQueryMetrics(proto.getMetrics()));
+ return result;
+ }
if (proto.getStale()) {
return proto.getExists() ? EMPTY_RESULT_EXISTS_TRUE_STALE : EMPTY_RESULT_EXISTS_FALSE_STALE;
}
@@ -1465,7 +1494,7 @@ public final class ProtobufUtil {
}
List<CellProtos.Cell> values = proto.getCellList();
- if (values.isEmpty()) {
+ if (values.isEmpty() && !proto.hasMetrics()) {
return proto.getStale() ? EMPTY_RESULT_STALE : EMPTY_RESULT;
}
@@ -1498,10 +1527,7 @@ public final class ProtobufUtil {
) {
throw new IllegalArgumentException("bad proto: exists with cells is no allowed " + proto);
}
- if (proto.getStale()) {
- return proto.getExists() ? EMPTY_RESULT_EXISTS_TRUE_STALE : EMPTY_RESULT_EXISTS_FALSE_STALE;
- }
- return proto.getExists() ? EMPTY_RESULT_EXISTS_TRUE : EMPTY_RESULT_EXISTS_FALSE;
+ return toResult(proto);
}
// TODO: Unit test that has some Cells in scanner and some in the proto.
@@ -1523,9 +1549,14 @@ public final class ProtobufUtil {
}
}
- Result r = (cells == null || cells.isEmpty())
- ? (proto.getStale() ? EMPTY_RESULT_STALE : EMPTY_RESULT)
- : Result.create(cells, null, proto.getStale());
+ Result r;
+ if (cells == null || cells.isEmpty()) {
+ r = proto.hasMetrics()
+ ? Result.create(EMPTY_CELL_ARRAY, null, proto.getStale())
+ : (proto.getStale() ? EMPTY_RESULT_STALE : EMPTY_RESULT);
+ } else {
+ r = Result.create(cells, null, proto.getStale());
+ }
if (proto.hasMetrics()) {
r.setMetrics(toQueryMetrics(proto.getMetrics()));
diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/shaded/protobuf/TestProtobufUtil.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/shaded/protobuf/TestProtobufUtil.java
index 8dd124b3cec..4846601445c 100644
--- a/hbase-client/src/test/java/org/apache/hadoop/hbase/shaded/protobuf/TestProtobufUtil.java
+++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/shaded/protobuf/TestProtobufUtil.java
@@ -19,11 +19,13 @@ package org.apache.hadoop.hbase.shaded.protobuf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.hbase.ArrayBackedTag;
@@ -31,6 +33,7 @@ import org.apache.hadoop.hbase.ByteBufferKeyValue;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellBuilderType;
import org.apache.hadoop.hbase.CellComparatorImpl;
+import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.ExtendedCellBuilder;
import org.apache.hadoop.hbase.ExtendedCellBuilderFactory;
import org.apache.hadoop.hbase.HConstants;
@@ -42,6 +45,8 @@ import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Increment;
import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.QueryMetrics;
+import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.SlowLogParams;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.testclassification.SmallTests;
@@ -131,6 +136,39 @@ public class TestProtobufUtil {
assertEquals(getBuilder.build(), ProtobufUtil.toGet(get));
}
+ @Test
+ public void testEmptyResultWithQueryMetrics() throws IOException {
+ long blockBytesScanned = 123L;
+ for (Boolean exists : Arrays.asList(null, false, true)) {
+ Result result = Result.create(Collections.emptyList(), exists);
+ result.setMetrics(new QueryMetrics(blockBytesScanned));
+
+ for (ClientProtos.Result proto : Arrays.asList(ProtobufUtil.toResult(result),
+ ProtobufUtil.toResultNoData(result))) {
+ assertEquals(exists, proto.hasExists() ? proto.getExists() : null);
+ assertTrue(proto.hasMetrics());
+ assertEquals(blockBytesScanned, proto.getMetrics().getBlockBytesScanned());
+
+ Result roundTrip = ProtobufUtil.toResult(proto);
+ assertEquals(exists, roundTrip.getExists());
+ assertNotNull(roundTrip.getMetrics());
+ assertEquals(blockBytesScanned, roundTrip.getMetrics().getBlockBytesScanned());
+
+ roundTrip =
+ ProtobufUtil.toResult(proto, CellUtil.createCellScanner(Collections.<Cell> emptyList()));
+ assertEquals(exists, roundTrip.getExists());
+ assertNotNull(roundTrip.getMetrics());
+ assertEquals(blockBytesScanned, roundTrip.getMetrics().getBlockBytesScanned());
+ }
+ }
+
+ ClientProtos.Result emptyProto = ClientProtos.Result.getDefaultInstance();
+ assertNull(ProtobufUtil.toResult(emptyProto).getMetrics());
+ assertNull(
+ ProtobufUtil.toResult(emptyProto, CellUtil.createCellScanner(Collections.<Cell> emptyList()))
+ .getMetrics());
+ }
+
/**
* Test Delete Mutate conversions.
* @throws IOException if the conversion to a {@link Delete} or a
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
index e246da4bd83..70605579bb4 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
@@ -2617,8 +2617,8 @@ public class RSRpcServices implements HBaseRPCErrorHandler, AdminService.Blockin
}
}
if (existence != null) {
- ClientProtos.Result pbr =
- ProtobufUtil.toResult(existence, region.getRegionInfo().getReplicaId() != 0);
+ ClientProtos.Result pbr = ProtobufUtil.toResult(existence,
+ region.getRegionInfo().getReplicaId() != 0, r != null ? r.getMetrics() : null);
builder.setResult(pbr);
} else if (r != null) {
ClientProtos.Result pbr;
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTableQueryMetrics.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTableQueryMetrics.java
index bc666916a45..d7deffced67 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTableQueryMetrics.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTableQueryMetrics.java
@@ -110,6 +110,25 @@ public class TestAsyncTableQueryMetrics {
}
assertEquals(getClusterBlockBytesScanned(), bbs);
+
+ g1.setCheckExistenceOnly(true);
+ g2.setCheckExistenceOnly(true);
+ g3.setCheckExistenceOnly(true);
+
+ result = CONN.getTable(TABLE_NAME).get(g1).get();
+ assertEquals(Boolean.TRUE, result.getExists());
+ assertNotNull(result.getMetrics());
+ bbs += result.getMetrics().getBlockBytesScanned();
+ assertEquals(getClusterBlockBytesScanned(), bbs);
+
+ futures = CONN.getTable(TABLE_NAME).get(ImmutableList.of(g1, g2, g3));
+ for (CompletableFuture<Result> future : futures) {
+ result = future.join();
+ assertEquals(Boolean.TRUE, result.getExists());
+ assertNotNull(result.getMetrics());
+ bbs += result.getMetrics().getBlockBytesScanned();
+ }
+ assertEquals(getClusterBlockBytesScanned(), bbs);
}
@Test