Skip to content

Commit f4c5c32

Browse files
authored
feat: app profile multi cluster routing support with specified cluster ids (#961)
* feat: app profile multi cluster routing support with specified cluster ids * fix mocked tests * add additional ITs
1 parent 39622f3 commit f4c5c32

File tree

4 files changed

+297
-7
lines changed

4 files changed

+297
-7
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import com.google.common.base.Preconditions;
2323
import com.google.common.base.Verify;
2424
import com.google.common.base.VerifyException;
25+
import com.google.common.collect.ImmutableSet;
26+
import java.util.Set;
2527
import javax.annotation.Nonnull;
2628

2729
/**
@@ -64,7 +66,8 @@ private AppProfile(@Nonnull com.google.bigtable.admin.v2.AppProfile proto) {
6466
@SuppressWarnings("WeakerAccess")
6567
public RoutingPolicy getPolicy() {
6668
if (proto.hasMultiClusterRoutingUseAny()) {
67-
return MultiClusterRoutingPolicy.of();
69+
return MultiClusterRoutingPolicy.of(
70+
ImmutableSet.copyOf(proto.getMultiClusterRoutingUseAny().getClusterIdsList()));
6871
} else if (proto.hasSingleClusterRouting()) {
6972
return new SingleClusterRoutingPolicy(proto.getSingleClusterRouting());
7073
} else {
@@ -226,15 +229,42 @@ public int hashCode() {
226229
* available cluster.
227230
*/
228231
public static class MultiClusterRoutingPolicy implements RoutingPolicy {
229-
private static final MultiClusterRoutingUseAny proto =
230-
MultiClusterRoutingUseAny.getDefaultInstance();
232+
private final MultiClusterRoutingUseAny proto;
231233

232234
/** Creates a new instance of {@link MultiClusterRoutingPolicy}. */
233235
public static MultiClusterRoutingPolicy of() {
234-
return new MultiClusterRoutingPolicy();
236+
return new MultiClusterRoutingPolicy(MultiClusterRoutingUseAny.getDefaultInstance());
235237
}
236238

237-
private MultiClusterRoutingPolicy() {}
239+
/**
240+
* Creates a new instance of {@link MultiClusterRoutingPolicy} with specified cluster ids to
241+
* route to.
242+
*/
243+
public static MultiClusterRoutingPolicy of(String... clusterIds) {
244+
return of(ImmutableSet.copyOf(clusterIds));
245+
}
246+
247+
/**
248+
* Creates a new instance of {@link MultiClusterRoutingPolicy} with specified cluster ids to
249+
* route to.
250+
*/
251+
public static MultiClusterRoutingPolicy of(Set<String> clusterIds) {
252+
return new MultiClusterRoutingPolicy(
253+
MultiClusterRoutingUseAny.newBuilder().addAllClusterIds(clusterIds).build());
254+
}
255+
256+
/*
257+
* Returns the set of clusters to route to. The order is ignored; clusters will be
258+
* tried in order of distance. If empty, all clusters are eligible.
259+
*/
260+
public Set<String> getClusterIds() {
261+
return ImmutableSet.copyOf(proto.getClusterIdsList());
262+
}
263+
264+
private MultiClusterRoutingPolicy(
265+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny proto) {
266+
this.proto = proto;
267+
}
238268

239269
/**
240270
* Creates the request protobuf. This method is considered an internal implementation detail and
@@ -253,8 +283,8 @@ public boolean equals(Object o) {
253283
if (o == null || getClass() != o.getClass()) {
254284
return false;
255285
}
256-
257-
return true;
286+
MultiClusterRoutingPolicy that = (MultiClusterRoutingPolicy) o;
287+
return Objects.equal(proto, that.proto);
258288
}
259289

260290
@Override

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java

+126
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.google.cloud.bigtable.admin.v2.models.UpdateAppProfileRequest;
5151
import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest;
5252
import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
53+
import com.google.common.collect.ImmutableList;
5354
import com.google.common.collect.Lists;
5455
import com.google.common.io.BaseEncoding;
5556
import com.google.protobuf.ByteString;
@@ -617,6 +618,131 @@ public void testCreateAppProfile() {
617618
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
618619
}
619620

621+
@Test
622+
public void testCreateAppProfileAddSingleClusterId() {
623+
// Setup
624+
Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable);
625+
626+
com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest =
627+
com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder()
628+
.setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID))
629+
.setAppProfileId(APP_PROFILE_ID)
630+
.setAppProfile(
631+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
632+
.setDescription("my description")
633+
.setMultiClusterRoutingUseAny(
634+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny
635+
.newBuilder()
636+
.addClusterIds("cluster-id-1")))
637+
.build();
638+
639+
com.google.bigtable.admin.v2.AppProfile expectedResponse =
640+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
641+
.setName(APP_PROFILE_NAME)
642+
.setDescription("my description")
643+
.setMultiClusterRoutingUseAny(
644+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
645+
.addClusterIds("cluster-id-1"))
646+
.build();
647+
648+
Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest))
649+
.thenReturn(ApiFutures.immediateFuture(expectedResponse));
650+
651+
// Execute
652+
AppProfile actualResult =
653+
adminClient.createAppProfile(
654+
CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID)
655+
.setDescription("my description")
656+
.setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1")));
657+
658+
// Verify
659+
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
660+
}
661+
662+
@Test
663+
public void testCreateAppProfileAddMultipleClusterIds() {
664+
// Setup
665+
Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable);
666+
667+
com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest =
668+
com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder()
669+
.setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID))
670+
.setAppProfileId(APP_PROFILE_ID)
671+
.setAppProfile(
672+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
673+
.setDescription("my description")
674+
.setMultiClusterRoutingUseAny(
675+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny
676+
.newBuilder()
677+
.addClusterIds("cluster-id-1")
678+
.addClusterIds("cluster-id-2")))
679+
.build();
680+
681+
com.google.bigtable.admin.v2.AppProfile expectedResponse =
682+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
683+
.setName(APP_PROFILE_NAME)
684+
.setDescription("my description")
685+
.setMultiClusterRoutingUseAny(
686+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
687+
.addClusterIds("cluster-id-1")
688+
.addClusterIds("cluster-id-2"))
689+
.build();
690+
691+
Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest))
692+
.thenReturn(ApiFutures.immediateFuture(expectedResponse));
693+
694+
// Execute
695+
AppProfile actualResult =
696+
adminClient.createAppProfile(
697+
CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID)
698+
.setDescription("my description")
699+
.setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2")));
700+
701+
// Verify
702+
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
703+
}
704+
705+
@Test
706+
public void testCreateAppProfileAddMultipleClusterIdsWithList() {
707+
// Setup
708+
Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable);
709+
710+
com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest =
711+
com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder()
712+
.setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID))
713+
.setAppProfileId(APP_PROFILE_ID)
714+
.setAppProfile(
715+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
716+
.setDescription("my description")
717+
.setMultiClusterRoutingUseAny(
718+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny
719+
.newBuilder()
720+
.addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2"))))
721+
.build();
722+
723+
com.google.bigtable.admin.v2.AppProfile expectedResponse =
724+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
725+
.setName(APP_PROFILE_NAME)
726+
.setDescription("my description")
727+
.setMultiClusterRoutingUseAny(
728+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
729+
.addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2")))
730+
.build();
731+
732+
Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest))
733+
.thenReturn(ApiFutures.immediateFuture(expectedResponse));
734+
735+
// Execute
736+
AppProfile actualResult =
737+
adminClient.createAppProfile(
738+
CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID)
739+
.setDescription("my description")
740+
.setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2")));
741+
742+
// Verify
743+
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
744+
}
745+
620746
@Test
621747
public void testGetAppProfile() {
622748
// Setup

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/it/BigtableInstanceAdminClientIT.java

+93
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,99 @@ public void appProfileTest() {
9696
assertThat(actualEx).isNull();
9797
}
9898

99+
@Test
100+
public void appProfileTestMultiClusterWithIds() {
101+
String newInstanceId = prefixGenerator.newPrefix();
102+
String newClusterId = newInstanceId + "-c1";
103+
String newClusterId2 = newInstanceId + "-c2";
104+
105+
client.createInstance(
106+
CreateInstanceRequest.of(newInstanceId)
107+
.addCluster(newClusterId, testEnvRule.env().getPrimaryZone(), 1, StorageType.SSD)
108+
.addCluster(newClusterId2, testEnvRule.env().getSecondaryZone(), 1, StorageType.SSD)
109+
.setDisplayName("Multi-Cluster-Instance-Test")
110+
.addLabel("state", "readytodelete")
111+
.setType(Type.PRODUCTION));
112+
113+
try {
114+
assertThat(client.exists(newInstanceId)).isTrue();
115+
116+
String testAppProfile = "test-app-profile";
117+
118+
CreateAppProfileRequest request =
119+
CreateAppProfileRequest.of(newInstanceId, testAppProfile)
120+
.setRoutingPolicy(AppProfile.MultiClusterRoutingPolicy.of(newClusterId))
121+
.setDescription("This is to test app profile");
122+
123+
AppProfile newlyCreatedAppProfile = client.createAppProfile(request);
124+
125+
AppProfile updated =
126+
client.updateAppProfile(
127+
UpdateAppProfileRequest.of(newlyCreatedAppProfile).setDescription("new description"));
128+
129+
AppProfile freshAppProfile = client.getAppProfile(newInstanceId, testAppProfile);
130+
assertThat(freshAppProfile.getDescription()).isEqualTo(updated.getDescription());
131+
132+
AppProfile.MultiClusterRoutingPolicy freshAppProfilePolicy =
133+
(AppProfile.MultiClusterRoutingPolicy) freshAppProfile.getPolicy();
134+
AppProfile.MultiClusterRoutingPolicy updatedAppProfilePolicy =
135+
(AppProfile.MultiClusterRoutingPolicy) updated.getPolicy();
136+
137+
assertThat(freshAppProfilePolicy.getClusterIds()).containsExactly(newClusterId);
138+
assertThat(freshAppProfilePolicy.getClusterIds())
139+
.isEqualTo(updatedAppProfilePolicy.getClusterIds());
140+
assertThat(freshAppProfilePolicy).isEqualTo(updatedAppProfilePolicy);
141+
142+
assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile);
143+
144+
// update again with routing policy
145+
AppProfile updated2 =
146+
client.updateAppProfile(
147+
UpdateAppProfileRequest.of(updated)
148+
.setRoutingPolicy(AppProfile.MultiClusterRoutingPolicy.of(newClusterId2)));
149+
150+
AppProfile freshAppProfile2 = client.getAppProfile(newInstanceId, testAppProfile);
151+
assertThat(freshAppProfile2.getDescription()).isEqualTo(updated2.getDescription());
152+
153+
AppProfile.MultiClusterRoutingPolicy freshAppProfilePolicy2 =
154+
(AppProfile.MultiClusterRoutingPolicy) freshAppProfile2.getPolicy();
155+
AppProfile.MultiClusterRoutingPolicy updatedAppProfilePolicy2 =
156+
(AppProfile.MultiClusterRoutingPolicy) updated2.getPolicy();
157+
158+
assertThat(freshAppProfilePolicy2.getClusterIds()).containsExactly(newClusterId2);
159+
assertThat(freshAppProfilePolicy2.getClusterIds())
160+
.isEqualTo(updatedAppProfilePolicy2.getClusterIds());
161+
assertThat(freshAppProfilePolicy2).isEqualTo(updatedAppProfilePolicy2);
162+
163+
assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile2);
164+
165+
// update to single routing policy
166+
AppProfile updated3 =
167+
client.updateAppProfile(
168+
UpdateAppProfileRequest.of(updated)
169+
.setRoutingPolicy(AppProfile.SingleClusterRoutingPolicy.of(newClusterId)));
170+
171+
AppProfile freshAppProfile3 = client.getAppProfile(newInstanceId, testAppProfile);
172+
assertThat(freshAppProfile3.getDescription()).isEqualTo(updated3.getDescription());
173+
174+
AppProfile.SingleClusterRoutingPolicy freshAppProfilePolicy3 =
175+
(AppProfile.SingleClusterRoutingPolicy) freshAppProfile3.getPolicy();
176+
AppProfile.SingleClusterRoutingPolicy updatedAppProfilePolicy3 =
177+
(AppProfile.SingleClusterRoutingPolicy) updated3.getPolicy();
178+
179+
assertThat(freshAppProfilePolicy3.getClusterId()).isEqualTo(newClusterId);
180+
assertThat(freshAppProfilePolicy3.getClusterId())
181+
.isEqualTo(updatedAppProfilePolicy3.getClusterId());
182+
assertThat(freshAppProfilePolicy3).isEqualTo(updatedAppProfilePolicy3);
183+
184+
assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile3);
185+
} finally {
186+
if (client.exists(newInstanceId)) {
187+
client.deleteInstance(newInstanceId);
188+
}
189+
}
190+
}
191+
99192
@Test
100193
public void iamUpdateTest() {
101194
Policy policy = client.getIamPolicy(instanceId);

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java

+41
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting;
2121
import com.google.bigtable.admin.v2.AppProfileName;
2222
import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy;
23+
import com.google.common.collect.ImmutableList;
2324
import org.junit.Test;
2425
import org.junit.runner.RunWith;
2526
import org.junit.runners.JUnit4;
@@ -48,6 +49,46 @@ public void testFromProto() {
4849
assertThat(profile.getPolicy()).isEqualTo(SingleClusterRoutingPolicy.of("my-cluster", true));
4950
}
5051

52+
@Test
53+
public void testFromProtoWithMultiCluster() {
54+
AppProfile profile =
55+
AppProfile.fromProto(
56+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
57+
.setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString())
58+
.setDescription("my description")
59+
.setMultiClusterRoutingUseAny(
60+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
61+
.build())
62+
.setEtag("my-etag")
63+
.build());
64+
65+
assertThat(profile.getInstanceId()).isEqualTo("my-instance");
66+
assertThat(profile.getId()).isEqualTo("my-profile");
67+
assertThat(profile.getDescription()).isEqualTo("my description");
68+
assertThat(profile.getPolicy()).isEqualTo(AppProfile.MultiClusterRoutingPolicy.of());
69+
}
70+
71+
@Test
72+
public void testFromProtoWithMultiClusterWithIds() {
73+
AppProfile profile =
74+
AppProfile.fromProto(
75+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
76+
.setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString())
77+
.setDescription("my description")
78+
.setMultiClusterRoutingUseAny(
79+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
80+
.addAllClusterIds(ImmutableList.of("cluster-id-1", "cluster-id-2"))
81+
.build())
82+
.setEtag("my-etag")
83+
.build());
84+
85+
assertThat(profile.getInstanceId()).isEqualTo("my-instance");
86+
assertThat(profile.getId()).isEqualTo("my-profile");
87+
assertThat(profile.getDescription()).isEqualTo("my description");
88+
assertThat(profile.getPolicy())
89+
.isEqualTo(AppProfile.MultiClusterRoutingPolicy.of("cluster-id-1", "cluster-id-2"));
90+
}
91+
5192
@Test
5293
public void testNoNameError() {
5394
Exception actualException = null;

0 commit comments

Comments
 (0)