Skip to content

Commit 7be62ea

Browse files
feat: support the bare minimum of RegionMetrics to support RegionSize… (#4342)
…Calculator in hbase 2x Change-Id: I4d3c9176966772a0f6dac5dd4494142dbe49791a Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Make sure to open an issue as a [bug/issue](https://togithub.com/googleapis/java-bigtable-hbase/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) Fixes #<issue_number_goes_here> ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
1 parent af1f0a1 commit 7be62ea

File tree

4 files changed

+208
-1
lines changed

4 files changed

+208
-1
lines changed

bigtable-hbase-2.x-parent/bigtable-hbase-2.x/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ limitations under the License.
101101
<version>${junit.version}</version>
102102
<scope>test</scope>
103103
</dependency>
104+
<dependency>
105+
<groupId>com.google.truth</groupId>
106+
<artifactId>truth</artifactId>
107+
<version>${truth.version}</version>
108+
<scope>test</scope>
109+
</dependency>
104110
<dependency>
105111
<groupId>org.mockito</groupId>
106112
<artifactId>mockito-core</artifactId>

bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/main/java/com/google/cloud/bigtable/hbase2_x/BigtableAdmin.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import org.apache.hadoop.hbase.ClusterStatus;
4040
import org.apache.hadoop.hbase.HRegionInfo;
4141
import org.apache.hadoop.hbase.HTableDescriptor;
42+
import org.apache.hadoop.hbase.RegionMetrics;
43+
import org.apache.hadoop.hbase.ServerName;
4244
import org.apache.hadoop.hbase.TableName;
4345
import org.apache.hadoop.hbase.client.AbstractBigtableAdmin;
4446
import org.apache.hadoop.hbase.client.AbstractBigtableConnection;
@@ -54,7 +56,6 @@
5456
*/
5557
@InternalApi("For internal usage only")
5658
public abstract class BigtableAdmin extends AbstractBigtableAdmin {
57-
5859
private final BigtableAsyncAdmin asyncAdmin;
5960

6061
public BigtableAdmin(AbstractBigtableConnection connection) throws IOException {
@@ -295,6 +296,12 @@ public ClusterStatus getClusterStatus() throws IOException {
295296
-1);
296297
}
297298

299+
@Override
300+
public List<RegionMetrics> getRegionMetrics(ServerName serverName, TableName tableName)
301+
throws IOException {
302+
return FutureUtil.unwrap(asyncAdmin.getRegionMetrics(serverName, tableName));
303+
}
304+
298305
private static Class<? extends BigtableAdmin> adminClass = null;
299306

300307
/**

bigtable-hbase-2.x-parent/bigtable-hbase-2.x/src/main/java/com/google/cloud/bigtable/hbase2_x/BigtableAsyncAdmin.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
2626
import com.google.cloud.bigtable.admin.v2.models.RestoreTableRequest;
2727
import com.google.cloud.bigtable.admin.v2.models.Table;
28+
import com.google.cloud.bigtable.data.v2.models.KeyOffset;
2829
import com.google.cloud.bigtable.hbase.BigtableOptionsFactory;
2930
import com.google.cloud.bigtable.hbase.util.Logger;
3031
import com.google.cloud.bigtable.hbase.util.ModifyTableBuilder;
@@ -35,9 +36,13 @@
3536
import com.google.common.base.Strings;
3637
import com.google.common.base.Throwables;
3738
import com.google.common.collect.ImmutableList;
39+
import com.google.protobuf.ByteString;
3840
import io.grpc.Status;
3941
import java.io.IOException;
42+
import java.lang.reflect.InvocationHandler;
4043
import java.lang.reflect.InvocationTargetException;
44+
import java.lang.reflect.Method;
45+
import java.util.ArrayList;
4146
import java.util.Collection;
4247
import java.util.Collections;
4348
import java.util.List;
@@ -55,6 +60,10 @@
5560
import net.bytebuddy.matcher.ElementMatchers;
5661
import org.apache.hadoop.conf.Configuration;
5762
import org.apache.hadoop.hbase.HTableDescriptor;
63+
import org.apache.hadoop.hbase.RegionMetrics;
64+
import org.apache.hadoop.hbase.ServerName;
65+
import org.apache.hadoop.hbase.Size;
66+
import org.apache.hadoop.hbase.Size.Unit;
5867
import org.apache.hadoop.hbase.TableName;
5968
import org.apache.hadoop.hbase.TableNotDisabledException;
6069
import org.apache.hadoop.hbase.TableNotEnabledException;
@@ -555,6 +564,36 @@ public CompletableFuture<List<RegionInfo>> getRegions(TableName tableName) {
555564
});
556565
}
557566

567+
@Override
568+
public CompletableFuture<List<RegionMetrics>> getRegionMetrics(
569+
ServerName ignored, TableName tableName) {
570+
// TODO: implement caching
571+
CompletableFuture<List<KeyOffset>> keyOffsetsFuture =
572+
toCompletableFuture(
573+
asyncConnection
574+
.getBigtableApi()
575+
.getDataClient()
576+
.sampleRowKeysAsync(tableName.getNameAsString()));
577+
578+
return keyOffsetsFuture.thenApply(
579+
keyOffsets -> {
580+
long now = System.currentTimeMillis();
581+
List<RegionMetrics> metrics = new ArrayList<>();
582+
ByteString lastKey = ByteString.EMPTY;
583+
long lastOffset = 0;
584+
for (KeyOffset keyOffset : keyOffsets) {
585+
byte[] regionName =
586+
RegionInfo.createRegionName(
587+
tableName, lastKey.toByteArray(), Bytes.toBytes(now), false);
588+
metrics.add(
589+
BasicRegionMetrics.create(regionName, keyOffset.getOffsetBytes() - lastOffset));
590+
lastKey = keyOffset.getKey();
591+
lastOffset = keyOffset.getOffsetBytes();
592+
}
593+
return metrics;
594+
});
595+
}
596+
558597
@Override
559598
public CompletableFuture<Void> snapshot(SnapshotDescription snapshot) {
560599
Objects.requireNonNull(snapshot);
@@ -601,4 +640,67 @@ public static BigtableAsyncAdmin createInstance(CommonConnection connection) thr
601640
throw new IOException(e);
602641
}
603642
}
643+
644+
private static Class<? extends RegionMetrics> regionMetricsClass = null;
645+
646+
private static synchronized Class<? extends RegionMetrics> getRegionMetricsSubclass()
647+
throws NoSuchMethodException {
648+
if (regionMetricsClass == null) {
649+
regionMetricsClass =
650+
new ByteBuddy()
651+
.subclass(BasicRegionMetrics.class)
652+
.name(BasicRegionMetrics.class.getName() + "Impl")
653+
.method(ElementMatchers.isAbstract())
654+
.intercept(InvocationHandlerAdapter.of(new UnsupportedOperationsHandler()))
655+
.make()
656+
.load(BigtableAsyncAdmin.class.getClassLoader())
657+
.getLoaded();
658+
}
659+
return regionMetricsClass;
660+
}
661+
662+
public abstract static class BasicRegionMetrics implements RegionMetrics {
663+
private final byte[] regionName;
664+
private final long size;
665+
666+
static RegionMetrics create(byte[] regionName, long size) {
667+
try {
668+
return getRegionMetricsSubclass()
669+
.getConstructor(byte[].class, long.class)
670+
.newInstance(regionName, size);
671+
} catch (NoSuchMethodException
672+
| InstantiationException
673+
| IllegalAccessException
674+
| InvocationTargetException e) {
675+
throw new IllegalStateException("Failed to instantiate RegionMetrics subclass", e);
676+
}
677+
}
678+
679+
public BasicRegionMetrics(byte[] regionName, long size) {
680+
this.regionName = regionName;
681+
this.size = size;
682+
}
683+
684+
@Override
685+
public byte[] getRegionName() {
686+
return regionName;
687+
}
688+
689+
@Override
690+
public int getStoreFileCount() {
691+
return 1;
692+
}
693+
694+
@Override
695+
public Size getStoreFileSize() {
696+
return new Size(size, Unit.BYTE);
697+
}
698+
}
699+
/** Handler for unsupported operations for generating Admin class at runtime. */
700+
public static class UnsupportedOperationsHandler implements InvocationHandler {
701+
@Override
702+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
703+
throw new UnsupportedOperationException(method.getName());
704+
}
705+
}
604706
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
import static org.mockito.Mockito.when;
20+
21+
import com.google.api.core.SettableApiFuture;
22+
import com.google.cloud.bigtable.data.v2.models.KeyOffset;
23+
import com.google.cloud.bigtable.hbase.wrappers.AdminClientWrapper;
24+
import com.google.cloud.bigtable.hbase.wrappers.BigtableApi;
25+
import com.google.cloud.bigtable.hbase.wrappers.BigtableHBaseSettings;
26+
import com.google.cloud.bigtable.hbase.wrappers.DataClientWrapper;
27+
import com.google.cloud.bigtable.hbase2_x.BigtableAsyncAdmin;
28+
import com.google.protobuf.ByteString;
29+
import java.util.Arrays;
30+
import java.util.HashSet;
31+
import java.util.List;
32+
import java.util.concurrent.ExecutionException;
33+
import org.apache.hadoop.conf.Configuration;
34+
import org.apache.hadoop.hbase.RegionMetrics;
35+
import org.apache.hadoop.hbase.ServerName;
36+
import org.apache.hadoop.hbase.Size;
37+
import org.apache.hadoop.hbase.Size.Unit;
38+
import org.apache.hadoop.hbase.TableName;
39+
import org.apache.hadoop.hbase.client.CommonConnection;
40+
import org.junit.Before;
41+
import org.junit.Rule;
42+
import org.junit.Test;
43+
import org.junit.runner.RunWith;
44+
import org.junit.runners.JUnit4;
45+
import org.mockito.Mock;
46+
import org.mockito.junit.MockitoJUnit;
47+
import org.mockito.junit.MockitoRule;
48+
49+
@RunWith(JUnit4.class)
50+
public class BigtableAsyncAdminTest {
51+
@Rule public final MockitoRule mockitoRule = MockitoJUnit.rule();
52+
53+
@Mock CommonConnection connection;
54+
@Mock BigtableHBaseSettings settings;
55+
@Mock BigtableApi bigtableApi;
56+
@Mock AdminClientWrapper adminClientWrapper;
57+
@Mock DataClientWrapper dataClientWrapper;
58+
59+
private BigtableAsyncAdmin admin;
60+
61+
@Before
62+
public void setUp() throws Exception {
63+
when(connection.getBigtableSettings()).thenReturn(settings);
64+
when(connection.getBigtableApi()).thenReturn(bigtableApi);
65+
when(bigtableApi.getAdminClient()).thenReturn(adminClientWrapper);
66+
when(bigtableApi.getDataClient()).thenReturn(dataClientWrapper);
67+
when(connection.getDisabledTables()).thenReturn(new HashSet<>());
68+
when(connection.getConfiguration()).thenReturn(new Configuration(false));
69+
70+
admin = BigtableAsyncAdmin.createInstance(connection);
71+
}
72+
73+
@Test
74+
public void testRegionMetricsFileSize() throws ExecutionException, InterruptedException {
75+
String tableId = "fake-table";
76+
SettableApiFuture<List<KeyOffset>> resultFuture = SettableApiFuture.create();
77+
resultFuture.set(
78+
Arrays.asList(
79+
KeyOffset.create(ByteString.copyFromUtf8("a"), 100),
80+
KeyOffset.create(ByteString.copyFromUtf8("b"), 250),
81+
KeyOffset.create(ByteString.copyFromUtf8(""), 401)));
82+
when(dataClientWrapper.sampleRowKeysAsync(tableId)).thenReturn(resultFuture);
83+
List<RegionMetrics> results =
84+
admin
85+
.getRegionMetrics(ServerName.valueOf("host", 123, 0), TableName.valueOf("fake-table"))
86+
.get();
87+
assertThat(results).hasSize(3);
88+
assertThat(results.get(0).getStoreFileSize()).isEqualTo(new Size(100, Unit.BYTE));
89+
assertThat(results.get(1).getStoreFileSize()).isEqualTo(new Size(150, Unit.BYTE));
90+
assertThat(results.get(2).getStoreFileSize()).isEqualTo(new Size(151, Unit.BYTE));
91+
}
92+
}

0 commit comments

Comments
 (0)