Skip to content

Commit 9c1c8e9

Browse files
feat: add support for instance processing units (#665)
* feat: add support for instance processing units * docs: add sample for instance processing units * fix: remove commented code + use junit asserts * fix: remove Spanner snapshot reference * test: add tests for InstanceInfo * cleanup: fix formatting + tests * chore: fix formatting * test: fixes instanceinfo test Due to master merge the compilation was failing. Fixes the builder construction here. * refactor: adds default impl for processing units Adds default implementation for the InstanceInfo set processing units in order to avoid a breaking change. * samples: remove LCI samples for now Removes the LCI samples for now, because they won't compile. We will re-add them once the main implementation is released. * fix: addresses PR comments Co-authored-by: Thiago Tasca Nunes <[email protected]>
1 parent 9641bc1 commit 9c1c8e9

File tree

7 files changed

+345
-12
lines changed

7 files changed

+345
-12
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/Instance.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ public Builder setNodeCount(int nodeCount) {
6767
return this;
6868
}
6969

70+
@Override
71+
public Builder setProcessingUnits(int processingUnits) {
72+
infoBuilder.setProcessingUnits(processingUnits);
73+
return this;
74+
}
75+
7076
@Override
7177
public Builder setState(State state) {
7278
infoBuilder.setState(state);
@@ -198,7 +204,8 @@ static Instance fromProto(
198204
new Builder(instanceClient, dbClient, id)
199205
.setInstanceConfigId(InstanceConfigId.of(proto.getConfig()))
200206
.setDisplayName(proto.getDisplayName())
201-
.setNodeCount(proto.getNodeCount());
207+
.setNodeCount(proto.getNodeCount())
208+
.setProcessingUnits(proto.getProcessingUnits());
202209
State state;
203210
switch (proto.getState()) {
204211
case STATE_UNSPECIFIED:

google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ public InstanceConfig fromProto(
9696
@Override
9797
public OperationFuture<Instance, CreateInstanceMetadata> createInstance(InstanceInfo instance)
9898
throws SpannerException {
99+
Preconditions.checkArgument(
100+
instance.getNodeCount() == 0 || instance.getProcessingUnits() == 0,
101+
"Only one of nodeCount and processingUnits can be set when creating a new instance");
99102
String projectName = PROJECT_NAME_TEMPLATE.instantiate("project", projectId);
100103
OperationFuture<com.google.spanner.admin.instance.v1.Instance, CreateInstanceMetadata>
101104
rawOperationFuture =
@@ -158,7 +161,8 @@ public OperationFuture<Instance, UpdateInstanceMetadata> updateInstance(
158161
InstanceInfo instance, InstanceInfo.InstanceField... fieldsToUpdate) {
159162
FieldMask fieldMask =
160163
fieldsToUpdate.length == 0
161-
? InstanceInfo.InstanceField.toFieldMask(InstanceInfo.InstanceField.values())
164+
? InstanceInfo.InstanceField.toFieldMask(
165+
InstanceInfo.InstanceField.defaultFieldsToUpdate(instance))
162166
: InstanceInfo.InstanceField.toFieldMask(fieldsToUpdate);
163167

164168
OperationFuture<com.google.spanner.admin.instance.v1.Instance, UpdateInstanceMetadata>

google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceInfo.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,17 @@ public class InstanceInfo {
3333
public enum InstanceField implements FieldSelector {
3434
DISPLAY_NAME("display_name"),
3535
NODE_COUNT("node_count"),
36+
PROCESSING_UNITS("processing_units"),
3637
LABELS("labels");
3738

39+
static InstanceField[] defaultFieldsToUpdate(InstanceInfo info) {
40+
if (info.getNodeCount() > 0) {
41+
return new InstanceField[] {DISPLAY_NAME, NODE_COUNT, LABELS};
42+
} else {
43+
return new InstanceField[] {DISPLAY_NAME, PROCESSING_UNITS, LABELS};
44+
}
45+
}
46+
3847
private final String selector;
3948

4049
InstanceField(String selector) {
@@ -68,8 +77,22 @@ public abstract static class Builder {
6877

6978
public abstract Builder setDisplayName(String displayName);
7079

80+
/**
81+
* Sets the number of nodes for the instance. Exactly one of processing units or node count must
82+
* be set when creating a new instance.
83+
*/
7184
public abstract Builder setNodeCount(int nodeCount);
7285

86+
/**
87+
* Sets the number of processing units for the instance. Exactly one of processing units or node
88+
* count must be set when creating a new instance. Processing units must be between 1 and 999
89+
* (inclusive) when creating a new instance with node count = 0. Processing units from 1000 and
90+
* up must always be a multiple of 1000 (that is equal to an integer number of nodes).
91+
*/
92+
public Builder setProcessingUnits(int processingUnits) {
93+
throw new UnsupportedOperationException("Unimplemented");
94+
}
95+
7396
public abstract Builder setState(State state);
7497

7598
public abstract Builder addLabel(String key, String value);
@@ -84,6 +107,7 @@ static class BuilderImpl extends Builder {
84107
private InstanceConfigId configId;
85108
private String displayName;
86109
private int nodeCount;
110+
private int processingUnits;
87111
private State state;
88112
private Map<String, String> labels;
89113

@@ -97,6 +121,7 @@ static class BuilderImpl extends Builder {
97121
this.configId = instance.configId;
98122
this.displayName = instance.displayName;
99123
this.nodeCount = instance.nodeCount;
124+
this.processingUnits = instance.processingUnits;
100125
this.state = instance.state;
101126
this.labels = new HashMap<>(instance.labels);
102127
}
@@ -119,6 +144,12 @@ public BuilderImpl setNodeCount(int nodeCount) {
119144
return this;
120145
}
121146

147+
@Override
148+
public BuilderImpl setProcessingUnits(int processingUnits) {
149+
this.processingUnits = processingUnits;
150+
return this;
151+
}
152+
122153
@Override
123154
public BuilderImpl setState(State state) {
124155
this.state = state;
@@ -147,6 +178,7 @@ public InstanceInfo build() {
147178
private final InstanceConfigId configId;
148179
private final String displayName;
149180
private final int nodeCount;
181+
private final int processingUnits;
150182
private final State state;
151183
private final ImmutableMap<String, String> labels;
152184

@@ -155,6 +187,7 @@ public InstanceInfo build() {
155187
this.configId = builder.configId;
156188
this.displayName = builder.displayName;
157189
this.nodeCount = builder.nodeCount;
190+
this.processingUnits = builder.processingUnits;
158191
this.state = builder.state;
159192
this.labels = ImmutableMap.copyOf(builder.labels);
160193
}
@@ -179,6 +212,11 @@ public int getNodeCount() {
179212
return nodeCount;
180213
}
181214

215+
/** Returns the number of processing units of the instance. */
216+
public int getProcessingUnits() {
217+
return processingUnits;
218+
}
219+
182220
/** Returns the current state of the instance. */
183221
public State getState() {
184222
return state;
@@ -200,6 +238,7 @@ public String toString() {
200238
.add("configName", configId == null ? null : configId.getName())
201239
.add("displayName", displayName)
202240
.add("nodeCount", nodeCount)
241+
.add("processingUnits", processingUnits)
203242
.add("state", state)
204243
.add("labels", labels)
205244
.toString();
@@ -218,20 +257,22 @@ public boolean equals(Object o) {
218257
&& Objects.equals(configId, that.configId)
219258
&& Objects.equals(displayName, that.displayName)
220259
&& nodeCount == that.nodeCount
260+
&& processingUnits == that.processingUnits
221261
&& state == that.state
222262
&& Objects.equals(labels, that.labels);
223263
}
224264

225265
@Override
226266
public int hashCode() {
227-
return Objects.hash(id, configId, displayName, nodeCount, state, labels);
267+
return Objects.hash(id, configId, displayName, nodeCount, processingUnits, state, labels);
228268
}
229269

230270
com.google.spanner.admin.instance.v1.Instance toProto() {
231271
com.google.spanner.admin.instance.v1.Instance.Builder builder =
232272
com.google.spanner.admin.instance.v1.Instance.newBuilder()
233273
.setName(getId().getName())
234274
.setNodeCount(getNodeCount())
275+
.setProcessingUnits(getProcessingUnits())
235276
.putAllLabels(getLabels());
236277
if (getDisplayName() != null) {
237278
builder.setDisplayName(getDisplayName());

google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminClientImplTest.java

+140-8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package com.google.cloud.spanner;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertTrue;
22+
import static org.junit.Assert.fail;
2023
import static org.mockito.Mockito.verify;
2124
import static org.mockito.Mockito.when;
2225
import static org.mockito.MockitoAnnotations.initMocks;
@@ -98,14 +101,24 @@ private com.google.spanner.admin.instance.v1.Instance getInstanceProto() {
98101
.setConfig(CONFIG_NAME)
99102
.setName(INSTANCE_NAME)
100103
.setNodeCount(1)
104+
.setProcessingUnits(1000)
105+
.build();
106+
}
107+
108+
private com.google.spanner.admin.instance.v1.Instance getInstanceProtoWithProcessingUnits() {
109+
return com.google.spanner.admin.instance.v1.Instance.newBuilder()
110+
.setConfig(CONFIG_NAME)
111+
.setName(INSTANCE_NAME)
112+
.setProcessingUnits(10)
101113
.build();
102114
}
103115

104116
private com.google.spanner.admin.instance.v1.Instance getAnotherInstanceProto() {
105117
return com.google.spanner.admin.instance.v1.Instance.newBuilder()
106118
.setConfig(CONFIG_NAME)
107119
.setName(INSTANCE_NAME2)
108-
.setNodeCount(1)
120+
.setNodeCount(2)
121+
.setProcessingUnits(2000)
109122
.build();
110123
}
111124

@@ -115,7 +128,10 @@ public void createInstance() throws Exception {
115128
rawOperationFuture =
116129
OperationFutureUtil.immediateOperationFuture(
117130
"createInstance", getInstanceProto(), CreateInstanceMetadata.getDefaultInstance());
118-
when(rpc.createInstance("projects/" + PROJECT_ID, INSTANCE_ID, getInstanceProto()))
131+
when(rpc.createInstance(
132+
"projects/" + PROJECT_ID,
133+
INSTANCE_ID,
134+
getInstanceProto().toBuilder().setProcessingUnits(0).build()))
119135
.thenReturn(rawOperationFuture);
120136
OperationFuture<Instance, CreateInstanceMetadata> op =
121137
client.createInstance(
@@ -128,9 +144,50 @@ public void createInstance() throws Exception {
128144
}
129145

130146
@Test
131-
public void getInstance() {
147+
public void testCreateInstanceWithProcessingUnits() throws Exception {
148+
OperationFuture<com.google.spanner.admin.instance.v1.Instance, CreateInstanceMetadata>
149+
rawOperationFuture =
150+
OperationFutureUtil.immediateOperationFuture(
151+
"createInstance",
152+
getInstanceProtoWithProcessingUnits(),
153+
CreateInstanceMetadata.getDefaultInstance());
154+
when(rpc.createInstance(
155+
"projects/" + PROJECT_ID, INSTANCE_ID, getInstanceProtoWithProcessingUnits()))
156+
.thenReturn(rawOperationFuture);
157+
OperationFuture<Instance, CreateInstanceMetadata> operation =
158+
client.createInstance(
159+
InstanceInfo.newBuilder(InstanceId.of(PROJECT_ID, INSTANCE_ID))
160+
.setInstanceConfigId(InstanceConfigId.of(PROJECT_ID, CONFIG_ID))
161+
.setProcessingUnits(10)
162+
.build());
163+
assertTrue(operation.isDone());
164+
assertEquals(INSTANCE_NAME, operation.get().getId().getName());
165+
}
166+
167+
@Test
168+
public void testCreateInstanceWithBothNodeCountAndProcessingUnits() throws Exception {
169+
try {
170+
client.createInstance(
171+
InstanceInfo.newBuilder(InstanceId.of(PROJECT_ID, INSTANCE_ID))
172+
.setInstanceConfigId(InstanceConfigId.of(PROJECT_ID, CONFIG_ID))
173+
.setNodeCount(1)
174+
.setProcessingUnits(100)
175+
.build());
176+
fail("missing expected exception");
177+
} catch (IllegalArgumentException e) {
178+
assertTrue(
179+
e.getMessage()
180+
.contains(
181+
"Only one of nodeCount and processingUnits can be set when creating a new instance"));
182+
}
183+
}
184+
185+
@Test
186+
public void testGetInstance() {
132187
when(rpc.getInstance(INSTANCE_NAME)).thenReturn(getInstanceProto());
133-
assertThat(client.getInstance(INSTANCE_ID).getId().getName()).isEqualTo(INSTANCE_NAME);
188+
Instance instance = client.getInstance(INSTANCE_ID);
189+
assertEquals(INSTANCE_NAME, instance.getId().getName());
190+
assertEquals(1000, instance.getProcessingUnits());
134191
}
135192

136193
@Test
@@ -165,7 +222,80 @@ public void updateInstanceMetadata() throws Exception {
165222
}
166223

167224
@Test
168-
public void listInstances() {
225+
public void testUpdateInstanceProcessingUnits() throws Exception {
226+
com.google.spanner.admin.instance.v1.Instance instance =
227+
com.google.spanner.admin.instance.v1.Instance.newBuilder()
228+
.setName(INSTANCE_NAME)
229+
.setConfig(CONFIG_NAME)
230+
.setProcessingUnits(10)
231+
.build();
232+
OperationFuture<com.google.spanner.admin.instance.v1.Instance, UpdateInstanceMetadata>
233+
rawOperationFuture =
234+
OperationFutureUtil.immediateOperationFuture(
235+
"updateInstance",
236+
getInstanceProtoWithProcessingUnits(),
237+
UpdateInstanceMetadata.getDefaultInstance());
238+
when(rpc.updateInstance(instance, FieldMask.newBuilder().addPaths("processing_units").build()))
239+
.thenReturn(rawOperationFuture);
240+
InstanceInfo instanceInfo =
241+
InstanceInfo.newBuilder(InstanceId.of(INSTANCE_NAME))
242+
.setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME))
243+
.setProcessingUnits(10)
244+
.build();
245+
OperationFuture<Instance, UpdateInstanceMetadata> operationWithFieldMask =
246+
client.updateInstance(instanceInfo, InstanceInfo.InstanceField.PROCESSING_UNITS);
247+
assertTrue(operationWithFieldMask.isDone());
248+
assertEquals(INSTANCE_NAME, operationWithFieldMask.get().getId().getName());
249+
250+
when(rpc.updateInstance(
251+
instance,
252+
FieldMask.newBuilder()
253+
.addAllPaths(Arrays.asList("display_name", "processing_units", "labels"))
254+
.build()))
255+
.thenReturn(rawOperationFuture);
256+
OperationFuture<Instance, UpdateInstanceMetadata> operation =
257+
client.updateInstance(instanceInfo);
258+
assertTrue(operation.isDone());
259+
assertEquals(INSTANCE_NAME, operation.get().getId().getName());
260+
}
261+
262+
@Test
263+
public void testUpdateInstanceWithNodeCountAndProcessingUnits() throws Exception {
264+
com.google.spanner.admin.instance.v1.Instance instance =
265+
com.google.spanner.admin.instance.v1.Instance.newBuilder()
266+
.setName(INSTANCE_NAME)
267+
.setConfig(CONFIG_NAME)
268+
.setNodeCount(3)
269+
.setProcessingUnits(3000)
270+
.build();
271+
OperationFuture<com.google.spanner.admin.instance.v1.Instance, UpdateInstanceMetadata>
272+
rawOperationFuture =
273+
OperationFutureUtil.immediateOperationFuture(
274+
"updateInstance",
275+
getInstanceProtoWithProcessingUnits(),
276+
UpdateInstanceMetadata.getDefaultInstance());
277+
// node_count should take precedence over processing_units when node_count>0 and no specific
278+
// field mask is set by the caller.
279+
when(rpc.updateInstance(
280+
instance,
281+
FieldMask.newBuilder()
282+
.addAllPaths(Arrays.asList("display_name", "node_count", "labels"))
283+
.build()))
284+
.thenReturn(rawOperationFuture);
285+
InstanceInfo instanceInfo =
286+
InstanceInfo.newBuilder(InstanceId.of(INSTANCE_NAME))
287+
.setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME))
288+
.setNodeCount(3)
289+
.setProcessingUnits(3000)
290+
.build();
291+
OperationFuture<Instance, UpdateInstanceMetadata> operationWithFieldMask =
292+
client.updateInstance(instanceInfo);
293+
assertTrue(operationWithFieldMask.isDone());
294+
assertEquals(INSTANCE_NAME, operationWithFieldMask.get().getId().getName());
295+
}
296+
297+
@Test
298+
public void testListInstances() {
169299
String nextToken = "token";
170300
String filter = "env:dev";
171301
when(rpc.listInstances(1, null, filter))
@@ -175,9 +305,11 @@ public void listInstances() {
175305
List<Instance> instances =
176306
Lists.newArrayList(
177307
client.listInstances(Options.pageSize(1), Options.filter(filter)).iterateAll());
178-
assertThat(instances.get(0).getId().getName()).isEqualTo(INSTANCE_NAME);
179-
assertThat(instances.get(1).getId().getName()).isEqualTo(INSTANCE_NAME2);
180-
assertThat(instances.size()).isEqualTo(2);
308+
assertEquals(INSTANCE_NAME, instances.get(0).getId().getName());
309+
assertEquals(1000, instances.get(0).getProcessingUnits());
310+
assertEquals(INSTANCE_NAME2, instances.get(1).getId().getName());
311+
assertEquals(2000, instances.get(1).getProcessingUnits());
312+
assertEquals(2, instances.size());
181313
}
182314

183315
@Test

0 commit comments

Comments
 (0)