Skip to content

Commit befd140

Browse files
authored
feat: Add APIs to enable request priorities (#1959)
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/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) - [ ] Rollback plan is reviewed and LGTMed 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 bf5a9b7 commit befd140

File tree

8 files changed

+393
-1
lines changed

8 files changed

+393
-1
lines changed

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

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import com.google.api.core.InternalApi;
1919
import com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny;
20+
import com.google.bigtable.admin.v2.AppProfile.Priority;
21+
import com.google.bigtable.admin.v2.AppProfile.StandardIsolation;
2022
import com.google.bigtable.admin.v2.AppProfileName;
2123
import com.google.common.base.Objects;
2224
import com.google.common.base.Preconditions;
@@ -76,6 +78,15 @@ public RoutingPolicy getPolicy() {
7678
}
7779
}
7880

81+
public IsolationPolicy getIsolationPolicy() {
82+
if (proto.hasStandardIsolation()) {
83+
return new StandardIsolationPolicy(proto.getStandardIsolation());
84+
} else {
85+
// Should never happen because the constructor verifies that one must exist.
86+
throw new IllegalStateException();
87+
}
88+
}
89+
7990
/** Gets the id of this AppProfile. */
8091
@SuppressWarnings("WeakerAccess")
8192
public String getId() {
@@ -292,4 +303,110 @@ public int hashCode() {
292303
return Objects.hashCode(proto);
293304
}
294305
}
306+
307+
/** Represents the options for isolating this app profile's traffic from other use cases. */
308+
@SuppressWarnings("WeakerAccess")
309+
public interface IsolationPolicy {}
310+
311+
/**
312+
* The possible priorities for an app profile. Note that higher priority writes can sometimes
313+
* queue behind lower priority writes to the same tablet, as writes must be strictly sequenced in
314+
* the durability log.
315+
*/
316+
public static enum Priority {
317+
LOW(com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_LOW),
318+
MEDIUM(com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_MEDIUM),
319+
HIGH(com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_HIGH);
320+
321+
private final com.google.bigtable.admin.v2.AppProfile.Priority proto;
322+
323+
/**
324+
* Wraps the protobuf. This method is considered an internal implementation detail and not meant
325+
* to be used by applications.
326+
*/
327+
@InternalApi
328+
public static Priority fromProto(com.google.bigtable.admin.v2.AppProfile.Priority proto) {
329+
Preconditions.checkNotNull(proto);
330+
331+
for (Priority priority : values()) {
332+
if (priority.proto.equals(proto)) {
333+
return priority;
334+
}
335+
}
336+
337+
throw new IllegalArgumentException("Unknown priority: " + proto);
338+
}
339+
340+
Priority(com.google.bigtable.admin.v2.AppProfile.Priority proto) {
341+
this.proto = proto;
342+
}
343+
344+
/**
345+
* Creates the request protobuf. This method is considered an internal implementation detail and
346+
* not meant to be used by applications.
347+
*/
348+
@InternalApi
349+
public com.google.bigtable.admin.v2.AppProfile.Priority toProto() {
350+
return proto;
351+
}
352+
}
353+
354+
/**
355+
* A standard {@link IsolationPolicy} for isolating this app profile's traffic from other use
356+
* cases. This accomplished by assigning different priorities to app profiles. A request that uses
357+
* an app profile with a StandardIsolationPolicy with a HIGH priority will likely run before a
358+
* request with a LOW priority.
359+
*/
360+
public static class StandardIsolationPolicy implements IsolationPolicy {
361+
private final StandardIsolation proto;
362+
363+
/** Creates a new instance of {@link StandardIsolationPolicy}. */
364+
public static StandardIsolationPolicy of() {
365+
return new StandardIsolationPolicy(StandardIsolation.getDefaultInstance());
366+
}
367+
368+
/** Creates a new instance of {@link StandardIsolationPolicy} with the specified priority. */
369+
public static StandardIsolationPolicy of(Priority priority) {
370+
return new StandardIsolationPolicy(
371+
StandardIsolation.newBuilder().setPriority(priority.toProto()).build());
372+
}
373+
374+
/*
375+
* Returns the priority for this app profile.
376+
*/
377+
public Priority getPriority() {
378+
return Priority.fromProto(proto.getPriority());
379+
}
380+
381+
private StandardIsolationPolicy(
382+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation proto) {
383+
this.proto = proto;
384+
}
385+
386+
/**
387+
* Creates the request protobuf. This method is considered an internal implementation detail and
388+
* not meant to be used by applications.
389+
*/
390+
@InternalApi
391+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation toProto() {
392+
return proto;
393+
}
394+
395+
@Override
396+
public boolean equals(Object o) {
397+
if (this == o) {
398+
return true;
399+
}
400+
if (o == null || getClass() != o.getClass()) {
401+
return false;
402+
}
403+
StandardIsolationPolicy that = (StandardIsolationPolicy) o;
404+
return Objects.equal(proto, that.proto);
405+
}
406+
407+
@Override
408+
public int hashCode() {
409+
return Objects.hashCode(proto);
410+
}
411+
}
295412
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
import com.google.api.core.InternalApi;
1919
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
20+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.IsolationPolicy;
2021
import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy;
2122
import com.google.cloud.bigtable.admin.v2.models.AppProfile.RoutingPolicy;
2223
import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy;
24+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.StandardIsolationPolicy;
2325
import com.google.common.base.Preconditions;
2426
import javax.annotation.Nonnull;
2527

@@ -92,6 +94,21 @@ public CreateAppProfileRequest setRoutingPolicy(RoutingPolicy routingPolicy) {
9294
return this;
9395
}
9496

97+
/** Sets the isolation policy for all read/write requests that use this app profile. */
98+
public CreateAppProfileRequest setIsolationPolicy(IsolationPolicy isolationPolicy) {
99+
Preconditions.checkNotNull(isolationPolicy);
100+
101+
if (isolationPolicy instanceof StandardIsolationPolicy) {
102+
proto
103+
.getAppProfileBuilder()
104+
.setStandardIsolation(((StandardIsolationPolicy) isolationPolicy).toProto());
105+
} else {
106+
throw new IllegalArgumentException("Unknown policy type: " + isolationPolicy);
107+
}
108+
109+
return this;
110+
}
111+
95112
/**
96113
* Creates the request protobuf. This method is considered an internal implementation detail and
97114
* not meant to be used by applications.

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
import com.google.api.core.InternalApi;
1919
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
20+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.IsolationPolicy;
2021
import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy;
2122
import com.google.cloud.bigtable.admin.v2.models.AppProfile.RoutingPolicy;
2223
import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy;
24+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.StandardIsolationPolicy;
2325
import com.google.common.base.Objects;
2426
import com.google.common.base.Preconditions;
2527
import com.google.protobuf.FieldMask;
@@ -121,6 +123,22 @@ public UpdateAppProfileRequest setRoutingPolicy(@Nonnull RoutingPolicy routingPo
121123
return this;
122124
}
123125

126+
/** Sets the isolation policy for all read/write requests that use this app profile. */
127+
public UpdateAppProfileRequest setIsolationPolicy(@Nonnull IsolationPolicy isolationPolicy) {
128+
Preconditions.checkNotNull(isolationPolicy);
129+
130+
if (isolationPolicy instanceof StandardIsolationPolicy) {
131+
proto
132+
.getAppProfileBuilder()
133+
.setStandardIsolation(((StandardIsolationPolicy) isolationPolicy).toProto());
134+
updateFieldMask(com.google.bigtable.admin.v2.AppProfile.STANDARD_ISOLATION_FIELD_NUMBER);
135+
} else {
136+
throw new IllegalArgumentException("Unknown policy type: " + isolationPolicy);
137+
}
138+
139+
return this;
140+
}
141+
124142
private void updateFieldMask(int fieldNumber) {
125143
FieldMask newMask =
126144
FieldMaskUtil.fromFieldNumbers(com.google.bigtable.admin.v2.AppProfile.class, fieldNumber);

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

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
4444
import com.google.cloud.bigtable.admin.v2.models.AppProfile;
4545
import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy;
46+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.Priority;
47+
import com.google.cloud.bigtable.admin.v2.models.AppProfile.StandardIsolationPolicy;
4648
import com.google.cloud.bigtable.admin.v2.models.Cluster;
4749
import com.google.cloud.bigtable.admin.v2.models.ClusterAutoscalingConfig;
4850
import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest;
@@ -82,7 +84,7 @@
8284

8385
@RunWith(JUnit4.class)
8486
/**
85-
* Tests for {@link BigtableTableAdminClient}. This test class uses Mockito so it has been
87+
* Tests for {@link BigtableInstanceAdminClient}. This test class uses Mockito so it has been
8688
* explicitly excluded from Native Image testing by not following the naming convention of (IT* and
8789
* *ClientTest).
8890
*/
@@ -983,6 +985,55 @@ public void testCreateAppProfileAddMultipleClusterIdsWithList() {
983985
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
984986
}
985987

988+
@Test
989+
public void testCreateAppProfileAddPriority() {
990+
// Setup
991+
Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable);
992+
993+
com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest =
994+
com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder()
995+
.setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID))
996+
.setAppProfileId(APP_PROFILE_ID)
997+
.setAppProfile(
998+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
999+
.setDescription("my description")
1000+
.setMultiClusterRoutingUseAny(
1001+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny
1002+
.newBuilder()
1003+
.addClusterIds("cluster-id-1"))
1004+
.setStandardIsolation(
1005+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation.newBuilder()
1006+
.setPriority(
1007+
com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_MEDIUM)))
1008+
.build();
1009+
1010+
com.google.bigtable.admin.v2.AppProfile expectedResponse =
1011+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
1012+
.setName(APP_PROFILE_NAME)
1013+
.setDescription("my description")
1014+
.setMultiClusterRoutingUseAny(
1015+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny.newBuilder()
1016+
.addClusterIds("cluster-id-1"))
1017+
.setStandardIsolation(
1018+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation.newBuilder()
1019+
.setPriority(com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_MEDIUM))
1020+
.build();
1021+
1022+
Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest))
1023+
.thenReturn(ApiFutures.immediateFuture(expectedResponse));
1024+
1025+
// Execute
1026+
AppProfile actualResult =
1027+
adminClient.createAppProfile(
1028+
CreateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID)
1029+
.setDescription("my description")
1030+
.setRoutingPolicy(MultiClusterRoutingPolicy.of("cluster-id-1"))
1031+
.setIsolationPolicy(StandardIsolationPolicy.of(Priority.MEDIUM)));
1032+
1033+
// Verify
1034+
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
1035+
}
1036+
9861037
@Test
9871038
public void testGetAppProfile() {
9881039
// Setup
@@ -1101,6 +1152,47 @@ public void testUpdateAppProfile() {
11011152
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
11021153
}
11031154

1155+
@Test
1156+
public void testUpdateAppProfileStandardIsolation() {
1157+
// Setup
1158+
Mockito.when(mockStub.updateAppProfileOperationCallable())
1159+
.thenReturn(mockUpdateAppProfileCallable);
1160+
1161+
com.google.bigtable.admin.v2.UpdateAppProfileRequest expectedRequest =
1162+
com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder()
1163+
.setAppProfile(
1164+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
1165+
.setName(APP_PROFILE_NAME)
1166+
.setStandardIsolation(
1167+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation.newBuilder()
1168+
.setPriority(
1169+
com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_LOW)))
1170+
.setUpdateMask(FieldMask.newBuilder().addPaths("standard_isolation"))
1171+
.build();
1172+
1173+
com.google.bigtable.admin.v2.AppProfile expectedResponse =
1174+
com.google.bigtable.admin.v2.AppProfile.newBuilder()
1175+
.setName(APP_PROFILE_NAME)
1176+
.setMultiClusterRoutingUseAny(
1177+
com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny
1178+
.getDefaultInstance())
1179+
.setStandardIsolation(
1180+
com.google.bigtable.admin.v2.AppProfile.StandardIsolation.newBuilder()
1181+
.setPriority(com.google.bigtable.admin.v2.AppProfile.Priority.PRIORITY_LOW))
1182+
.build();
1183+
1184+
mockOperationResult(mockUpdateAppProfileCallable, expectedRequest, expectedResponse);
1185+
1186+
// Execute
1187+
AppProfile actualResult =
1188+
adminClient.updateAppProfile(
1189+
UpdateAppProfileRequest.of(INSTANCE_ID, APP_PROFILE_ID)
1190+
.setIsolationPolicy(StandardIsolationPolicy.of(Priority.LOW)));
1191+
1192+
// Verify
1193+
assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse));
1194+
}
1195+
11041196
@Test
11051197
public void testDeleteAppProfile() throws Exception {
11061198
// Setup

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,57 @@ public void appProfileTestMultiClusterWithIds() {
191191
}
192192
}
193193

194+
@Test
195+
public void appProfileTestPriority() {
196+
String newInstanceId = prefixGenerator.newPrefix();
197+
String newClusterId = newInstanceId + "-c1";
198+
199+
client.createInstance(
200+
CreateInstanceRequest.of(newInstanceId)
201+
.addCluster(newClusterId, testEnvRule.env().getPrimaryZone(), 1, StorageType.SSD)
202+
.setDisplayName("Priority-Instance-Test")
203+
.addLabel("state", "readytodelete")
204+
.setType(Type.PRODUCTION));
205+
206+
try {
207+
assertThat(client.exists(newInstanceId)).isTrue();
208+
209+
String testAppProfile = prefixGenerator.newPrefix();
210+
211+
// This should be created with HIGH priority.
212+
CreateAppProfileRequest request =
213+
CreateAppProfileRequest.of(newInstanceId, testAppProfile)
214+
.setRoutingPolicy(AppProfile.SingleClusterRoutingPolicy.of(newClusterId))
215+
.setDescription("This is to test app profile");
216+
217+
AppProfile newlyCreatedAppProfile = client.createAppProfile(request);
218+
AppProfile.StandardIsolationPolicy newlyCreatedAppProfilePolicy =
219+
(AppProfile.StandardIsolationPolicy) newlyCreatedAppProfile.getIsolationPolicy();
220+
assertThat(newlyCreatedAppProfilePolicy.getPriority()).isEqualTo(AppProfile.Priority.HIGH);
221+
222+
AppProfile updated =
223+
client.updateAppProfile(
224+
UpdateAppProfileRequest.of(newlyCreatedAppProfile)
225+
.setIsolationPolicy(
226+
AppProfile.StandardIsolationPolicy.of(AppProfile.Priority.LOW)));
227+
228+
AppProfile freshAppProfile = client.getAppProfile(newInstanceId, testAppProfile);
229+
AppProfile.StandardIsolationPolicy freshAppProfilePolicy =
230+
(AppProfile.StandardIsolationPolicy) freshAppProfile.getIsolationPolicy();
231+
AppProfile.StandardIsolationPolicy updatedAppProfilePolicy =
232+
(AppProfile.StandardIsolationPolicy) updated.getIsolationPolicy();
233+
234+
assertThat(freshAppProfilePolicy.getPriority()).isEqualTo(AppProfile.Priority.LOW);
235+
assertThat(freshAppProfilePolicy).isEqualTo(updatedAppProfilePolicy);
236+
237+
assertThat(client.listAppProfiles(newInstanceId)).contains(freshAppProfile);
238+
} finally {
239+
if (client.exists(newInstanceId)) {
240+
client.deleteInstance(newInstanceId);
241+
}
242+
}
243+
}
244+
194245
@Test
195246
public void iamUpdateTest() {
196247
Policy policy = client.getIamPolicy(instanceId);

0 commit comments

Comments
 (0)