diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Instance.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Instance.java index 1d4bb804d1..04933a6712 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Instance.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Instance.java @@ -23,6 +23,7 @@ import com.google.cloud.spanner.Options.ListOption; import com.google.longrunning.Operation; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata; import java.util.Map; @@ -86,6 +87,12 @@ public Builder setProcessingUnits(int processingUnits) { return this; } + @Override + public Builder setAutoscalingConfig(AutoscalingConfig autoscalingConfig) { + infoBuilder.setAutoscalingConfig(autoscalingConfig); + return this; + } + @Override public Builder setState(State state) { infoBuilder.setState(state); @@ -220,6 +227,7 @@ static Instance fromProto( .setNodeCount(proto.getNodeCount()) .setCreateTime(Timestamp.fromProto(proto.getCreateTime())) .setUpdateTime(Timestamp.fromProto(proto.getUpdateTime())) + .setAutoscalingConfig(proto.getAutoscalingConfig()) .setProcessingUnits(proto.getProcessingUnits()); State state; switch (proto.getState()) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java index 195de83382..46780e55ba 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceAdminClientImpl.java @@ -193,9 +193,6 @@ public Operation fromProto(Operation proto) { @Override public OperationFuture createInstance(InstanceInfo instance) throws SpannerException { - Preconditions.checkArgument( - instance.getNodeCount() == 0 || instance.getProcessingUnits() == 0, - "Only one of nodeCount and processingUnits can be set when creating a new instance"); String projectName = PROJECT_NAME_TEMPLATE.instantiate("project", projectId); OperationFuture rawOperationFuture = diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceInfo.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceInfo.java index d11b47e424..ebe0514adf 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceInfo.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/InstanceInfo.java @@ -23,6 +23,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.protobuf.FieldMask; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -35,13 +36,16 @@ public enum InstanceField implements FieldSelector { DISPLAY_NAME("display_name"), NODE_COUNT("node_count"), PROCESSING_UNITS("processing_units"), + AUTOSCALING_CONFIG("autoscaling_config"), LABELS("labels"); static InstanceField[] defaultFieldsToUpdate(InstanceInfo info) { - if (info.getNodeCount() > 0) { - return new InstanceField[] {DISPLAY_NAME, NODE_COUNT, LABELS}; + if (info.getAutoscalingConfig() != null) { + return new InstanceField[] {DISPLAY_NAME, AUTOSCALING_CONFIG, LABELS}; + } else if (info.getNodeCount() > 0) { + return new InstanceField[] {DISPLAY_NAME, AUTOSCALING_CONFIG, NODE_COUNT, LABELS}; } else { - return new InstanceField[] {DISPLAY_NAME, PROCESSING_UNITS, LABELS}; + return new InstanceField[] {DISPLAY_NAME, AUTOSCALING_CONFIG, PROCESSING_UNITS, LABELS}; } } @@ -87,21 +91,31 @@ Builder setCreateTime(Timestamp createTime) { } /** - * Sets the number of nodes for the instance. Exactly one of processing units or node count must - * be set when creating a new instance. + * Sets the number of nodes for the instance. Exactly one of processing units, node count or + * autoscaling config must be set when creating a new instance. */ public abstract Builder setNodeCount(int nodeCount); /** - * Sets the number of processing units for the instance. Exactly one of processing units or node - * count must be set when creating a new instance. Processing units must be between 1 and 999 - * (inclusive) when creating a new instance with node count = 0. Processing units from 1000 and - * up must always be a multiple of 1000 (that is equal to an integer number of nodes). + * Sets the number of processing units for the instance. Exactly one of processing units, node + * count, or autoscaling config must be set when creating a new instance. Processing units must + * be between 1 and 999 (inclusive) when creating a new instance with node count = 0. Processing + * units from 1000 and up must always be a multiple of 1000 (that is equal to an integer number + * of nodes). */ public Builder setProcessingUnits(int processingUnits) { throw new UnsupportedOperationException("Unimplemented"); } + /** + * Sets the autoscaling config for the instance, which will enable the autoscaling for this + * instance. Exactly one of processing units, node count, or autoscaling config must be set when + * creating a new instance. + */ + public Builder setAutoscalingConfig(AutoscalingConfig autoscalingConfig) { + throw new UnsupportedOperationException("Unimplemented"); + } + public abstract Builder setState(State state); public abstract Builder addLabel(String key, String value); @@ -117,6 +131,7 @@ static class BuilderImpl extends Builder { private String displayName; private int nodeCount; private int processingUnits; + private AutoscalingConfig autoscalingConfig; private State state; private Map labels; private Timestamp updateTime; @@ -133,6 +148,7 @@ static class BuilderImpl extends Builder { this.displayName = instance.displayName; this.nodeCount = instance.nodeCount; this.processingUnits = instance.processingUnits; + this.autoscalingConfig = instance.autoscalingConfig; this.state = instance.state; this.labels = new HashMap<>(instance.labels); this.updateTime = instance.updateTime; @@ -175,6 +191,12 @@ public BuilderImpl setProcessingUnits(int processingUnits) { return this; } + @Override + public BuilderImpl setAutoscalingConfig(AutoscalingConfig autoscalingConfig) { + this.autoscalingConfig = autoscalingConfig; + return this; + } + @Override public BuilderImpl setState(State state) { this.state = state; @@ -204,6 +226,7 @@ public InstanceInfo build() { private final String displayName; private final int nodeCount; private final int processingUnits; + private final AutoscalingConfig autoscalingConfig; private final State state; private final ImmutableMap labels; private final Timestamp updateTime; @@ -215,6 +238,7 @@ public InstanceInfo build() { this.displayName = builder.displayName; this.nodeCount = builder.nodeCount; this.processingUnits = builder.processingUnits; + this.autoscalingConfig = builder.autoscalingConfig; this.state = builder.state; this.labels = ImmutableMap.copyOf(builder.labels); this.updateTime = builder.updateTime; @@ -254,6 +278,11 @@ public int getProcessingUnits() { return processingUnits; } + /** Returns the autoscaling config of the instance. */ + public AutoscalingConfig getAutoscalingConfig() { + return autoscalingConfig; + } + /** Returns the current state of the instance. */ public State getState() { return state; @@ -276,6 +305,7 @@ public String toString() { .add("displayName", displayName) .add("nodeCount", nodeCount) .add("processingUnits", processingUnits) + .add("autoscaling_config", autoscalingConfig) .add("state", state) .add("labels", labels) .add("createTime", createTime) @@ -297,6 +327,7 @@ public boolean equals(Object o) { && Objects.equals(displayName, that.displayName) && nodeCount == that.nodeCount && processingUnits == that.processingUnits + && Objects.equals(autoscalingConfig, that.autoscalingConfig) && state == that.state && Objects.equals(labels, that.labels) && Objects.equals(updateTime, that.updateTime) @@ -311,6 +342,7 @@ public int hashCode() { displayName, nodeCount, processingUnits, + autoscalingConfig, state, labels, updateTime, @@ -330,6 +362,9 @@ com.google.spanner.admin.instance.v1.Instance toProto() { if (getInstanceConfigId() != null) { builder.setConfig(getInstanceConfigId().getName()); } + if (getAutoscalingConfig() != null) { + builder.setAutoscalingConfig(getAutoscalingConfig()); + } return builder.build(); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminClientImplTest.java index 7084290b24..58373bcca0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceAdminClientImplTest.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -41,6 +40,7 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.FieldMask; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; import com.google.spanner.admin.instance.v1.InstanceConfig; @@ -242,6 +242,26 @@ private com.google.spanner.admin.instance.v1.Instance getInstanceProtoWithProces .build(); } + private AutoscalingConfig getAutoscalingConfigProto() { + return AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(2).setMaxNodes(10)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + } + + private com.google.spanner.admin.instance.v1.Instance getAutoscalingInstanceProto() { + + return com.google.spanner.admin.instance.v1.Instance.newBuilder() + .setConfig(CONFIG_NAME) + .setName(INSTANCE_NAME) + .setAutoscalingConfig(getAutoscalingConfigProto()) + .build(); + } + private com.google.spanner.admin.instance.v1.Instance getAnotherInstanceProto() { return com.google.spanner.admin.instance.v1.Instance.newBuilder() .setConfig(CONFIG_NAME) @@ -294,21 +314,26 @@ public void testCreateInstanceWithProcessingUnits() throws Exception { } @Test - public void testCreateInstanceWithBothNodeCountAndProcessingUnits() throws Exception { - try { - client.createInstance( - InstanceInfo.newBuilder(InstanceId.of(PROJECT_ID, INSTANCE_ID)) - .setInstanceConfigId(InstanceConfigId.of(PROJECT_ID, CONFIG_ID)) - .setNodeCount(1) - .setProcessingUnits(100) - .build()); - fail("missing expected exception"); - } catch (IllegalArgumentException e) { - assertTrue( - e.getMessage() - .contains( - "Only one of nodeCount and processingUnits can be set when creating a new instance")); - } + public void testCreateInstanceWithAutoscalingConfig() throws Exception { + OperationFuture + rawOperationFuture = + OperationFutureUtil.immediateOperationFuture( + "createInstance", + getAutoscalingInstanceProto(), + CreateInstanceMetadata.getDefaultInstance()); + when(rpc.createInstance("projects/" + PROJECT_ID, INSTANCE_ID, getAutoscalingInstanceProto())) + .thenReturn(rawOperationFuture); + OperationFuture operation = + client.createInstance( + InstanceInfo.newBuilder(InstanceId.of(PROJECT_ID, INSTANCE_ID)) + .setInstanceConfigId(InstanceConfigId.of(PROJECT_ID, CONFIG_ID)) + .setAutoscalingConfig(getAutoscalingInstanceProto().getAutoscalingConfig()) + .build()); + assertTrue(operation.isDone()); + Instance instance = operation.get(); + assertEquals(INSTANCE_NAME, instance.getId().getName()); + assertEquals( + getAutoscalingInstanceProto().getAutoscalingConfig(), instance.getAutoscalingConfig()); } @Test @@ -319,6 +344,15 @@ public void testGetInstance() { assertEquals(1000, instance.getProcessingUnits()); } + @Test + public void testGetAutoscalingInstance() { + when(rpc.getInstance(INSTANCE_NAME)).thenReturn(getAutoscalingInstanceProto()); + Instance instance = client.getInstance(INSTANCE_ID); + assertEquals(INSTANCE_NAME, instance.getId().getName()); + assertEquals( + getAutoscalingInstanceProto().getAutoscalingConfig(), instance.getAutoscalingConfig()); + } + @Test public void dropInstance() { client.deleteInstance(INSTANCE_ID); @@ -379,7 +413,96 @@ public void testUpdateInstanceProcessingUnits() throws Exception { when(rpc.updateInstance( instance, FieldMask.newBuilder() - .addAllPaths(Arrays.asList("display_name", "processing_units", "labels")) + .addAllPaths( + Arrays.asList( + "display_name", "autoscaling_config", "processing_units", "labels")) + .build())) + .thenReturn(rawOperationFuture); + OperationFuture operation = + client.updateInstance(instanceInfo); + assertTrue(operation.isDone()); + assertEquals(INSTANCE_NAME, operation.get().getId().getName()); + } + + @Test + public void testEnableInstanceAutoscaling() throws Exception { + com.google.spanner.admin.instance.v1.Instance instance = + com.google.spanner.admin.instance.v1.Instance.newBuilder() + .setName(INSTANCE_NAME) + .setConfig(CONFIG_NAME) + .setAutoscalingConfig(getAutoscalingConfigProto()) + .build(); + OperationFuture + rawOperationFuture = + OperationFutureUtil.immediateOperationFuture( + "updateInstance", + getAutoscalingInstanceProto(), + UpdateInstanceMetadata.getDefaultInstance()); + when(rpc.updateInstance( + instance, FieldMask.newBuilder().addPaths("autoscaling_config").build())) + .thenReturn(rawOperationFuture); + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(INSTANCE_NAME)) + .setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME)) + .setAutoscalingConfig(getAutoscalingConfigProto()) + .build(); + OperationFuture operationWithFieldMask = + client.updateInstance(instanceInfo, InstanceInfo.InstanceField.AUTOSCALING_CONFIG); + assertTrue(operationWithFieldMask.isDone()); + assertEquals(INSTANCE_NAME, operationWithFieldMask.get().getId().getName()); + + when(rpc.updateInstance( + instance, + FieldMask.newBuilder() + .addAllPaths(Arrays.asList("display_name", "autoscaling_config", "labels")) + .build())) + .thenReturn(rawOperationFuture); + OperationFuture operation = + client.updateInstance(instanceInfo); + assertTrue(operation.isDone()); + assertEquals(INSTANCE_NAME, operation.get().getId().getName()); + } + + @Test + public void testDisableInstanceAutoscaling() throws Exception { + com.google.spanner.admin.instance.v1.Instance instance = + com.google.spanner.admin.instance.v1.Instance.newBuilder() + .setName(INSTANCE_NAME) + .setConfig(CONFIG_NAME) + .setProcessingUnits(10) + .build(); + OperationFuture + rawOperationFuture = + OperationFutureUtil.immediateOperationFuture( + "updateInstance", + getInstanceProtoWithProcessingUnits(), + UpdateInstanceMetadata.getDefaultInstance()); + when(rpc.updateInstance( + instance, + FieldMask.newBuilder() + .addPaths("autoscaling_config") + .addPaths("processing_units") + .build())) + .thenReturn(rawOperationFuture); + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(INSTANCE_NAME)) + .setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME)) + .setProcessingUnits(10) + .build(); + OperationFuture operationWithFieldMask = + client.updateInstance( + instanceInfo, + InstanceInfo.InstanceField.AUTOSCALING_CONFIG, + InstanceInfo.InstanceField.PROCESSING_UNITS); + assertTrue(operationWithFieldMask.isDone()); + assertEquals(INSTANCE_NAME, operationWithFieldMask.get().getId().getName()); + + when(rpc.updateInstance( + instance, + FieldMask.newBuilder() + .addAllPaths( + Arrays.asList( + "display_name", "autoscaling_config", "processing_units", "labels")) .build())) .thenReturn(rawOperationFuture); OperationFuture operation = @@ -408,7 +531,45 @@ public void testUpdateInstanceWithNodeCountAndProcessingUnits() throws Exception when(rpc.updateInstance( instance, FieldMask.newBuilder() - .addAllPaths(Arrays.asList("display_name", "node_count", "labels")) + .addAllPaths( + Arrays.asList("display_name", "autoscaling_config", "node_count", "labels")) + .build())) + .thenReturn(rawOperationFuture); + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(INSTANCE_NAME)) + .setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME)) + .setNodeCount(3) + .setProcessingUnits(3000) + .build(); + OperationFuture operationWithFieldMask = + client.updateInstance(instanceInfo); + assertTrue(operationWithFieldMask.isDone()); + assertEquals(INSTANCE_NAME, operationWithFieldMask.get().getId().getName()); + } + + @Test + public void testUpdateInstanceWithNodeCountAndProcessingUnitsAndAutoscalingConfig() + throws Exception { + com.google.spanner.admin.instance.v1.Instance instance = + com.google.spanner.admin.instance.v1.Instance.newBuilder() + .setName(INSTANCE_NAME) + .setConfig(CONFIG_NAME) + .setNodeCount(3) + .setProcessingUnits(3000) + .setAutoscalingConfig(getAutoscalingConfigProto()) + .build(); + OperationFuture + rawOperationFuture = + OperationFutureUtil.immediateOperationFuture( + "updateInstance", + getAutoscalingInstanceProto(), + UpdateInstanceMetadata.getDefaultInstance()); + // autoscaling_config should take precedence over node_count or processing_units when + // autoscaling_config is not null and no specific field mask is set by the caller. + when(rpc.updateInstance( + instance, + FieldMask.newBuilder() + .addAllPaths(Arrays.asList("display_name", "autoscaling_config", "labels")) .build())) .thenReturn(rawOperationFuture); InstanceInfo instanceInfo = @@ -416,6 +577,7 @@ public void testUpdateInstanceWithNodeCountAndProcessingUnits() throws Exception .setInstanceConfigId(InstanceConfigId.of(CONFIG_NAME)) .setNodeCount(3) .setProcessingUnits(3000) + .setAutoscalingConfig(getAutoscalingConfigProto()) .build(); OperationFuture operationWithFieldMask = client.updateInstance(instanceInfo); @@ -428,14 +590,14 @@ public void testListInstances() { String nextToken = "token"; String filter = "env:dev"; when(rpc.listInstances(1, null, filter)) - .thenReturn(new Paginated<>(ImmutableList.of(getInstanceProto()), nextToken)); + .thenReturn(new Paginated<>(ImmutableList.of(getAutoscalingInstanceProto()), nextToken)); when(rpc.listInstances(1, nextToken, filter)) .thenReturn(new Paginated<>(ImmutableList.of(getAnotherInstanceProto()), "")); List instances = Lists.newArrayList( client.listInstances(Options.pageSize(1), Options.filter(filter)).iterateAll()); assertEquals(INSTANCE_NAME, instances.get(0).getId().getName()); - assertEquals(1000, instances.get(0).getProcessingUnits()); + assertEquals(getAutoscalingConfigProto(), instances.get(0).getAutoscalingConfig()); assertEquals(INSTANCE_NAME2, instances.get(1).getId().getName()); assertEquals(2000, instances.get(1).getProcessingUnits()); assertEquals(2, instances.size()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceInfoTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceInfoTest.java index 8b2cae50e8..e12bb17382 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceInfoTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceInfoTest.java @@ -23,6 +23,7 @@ import com.google.cloud.Timestamp; import com.google.common.testing.EqualsTester; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -44,18 +45,32 @@ public void testEmptyBuilder() { assertTrue(info.getLabels().isEmpty()); assertNull(info.getUpdateTime()); assertNull(info.getCreateTime()); + assertNull(info.getAutoscalingConfig()); } @Test public void testBuildInstanceInfo() { InstanceId id = new InstanceId("test-project", "test-instance"); InstanceConfigId configId = new InstanceConfigId("test-project", "test-instance-config"); + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(5000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + InstanceInfo info = InstanceInfo.newBuilder(id) .setInstanceConfigId(configId) .setDisplayName("test instance") .setNodeCount(1) .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig) .setState(InstanceInfo.State.READY) .addLabel("env", "prod") .addLabel("region", "us") @@ -67,17 +82,29 @@ public void testBuildInstanceInfo() { assertThat(info.getDisplayName()).isEqualTo("test instance"); assertThat(info.getNodeCount()).isEqualTo(1); assertThat(info.getProcessingUnits()).isEqualTo(2000); + assertThat(info.getAutoscalingConfig()).isEqualTo(autoscalingConfig); assertThat(info.getState()).isEqualTo(InstanceInfo.State.READY); assertThat(info.getLabels()).containsExactly("env", "prod", "region", "us"); assertEquals(Timestamp.ofTimeMicroseconds(86000), info.getUpdateTime()); assertEquals(Timestamp.ofTimeMicroseconds(46000), info.getCreateTime()); - info = info.toBuilder().setDisplayName("new test instance").build(); + AutoscalingConfig newAutoscalingConfig = + autoscalingConfig + .toBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(10).setMaxNodes(100)) + .build(); + info = + info.toBuilder() + .setDisplayName("new test instance") + .setAutoscalingConfig(newAutoscalingConfig) + .build(); assertThat(info.getId()).isEqualTo(id); assertThat(info.getInstanceConfigId()).isEqualTo(configId); assertThat(info.getDisplayName()).isEqualTo("new test instance"); assertThat(info.getNodeCount()).isEqualTo(1); assertThat(info.getProcessingUnits()).isEqualTo(2000); + assertThat(info.getAutoscalingConfig()).isEqualTo(newAutoscalingConfig); assertThat(info.getState()).isEqualTo(InstanceInfo.State.READY); assertThat(info.getLabels()).containsExactly("env", "prod", "region", "us"); assertEquals(Timestamp.ofTimeMicroseconds(86000), info.getUpdateTime()); @@ -88,12 +115,24 @@ public void testBuildInstanceInfo() { public void testToBuilder() { InstanceId id = new InstanceId("test-project", "test-instance"); InstanceConfigId configId = new InstanceConfigId("test-project", "test-instance-config"); + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(5000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); InstanceInfo info = InstanceInfo.newBuilder(id) .setInstanceConfigId(configId) .setDisplayName("test instance") .setNodeCount(1) .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig) .setState(InstanceInfo.State.READY) .addLabel("env", "prod") .addLabel("region", "us") @@ -107,6 +146,7 @@ public void testToBuilder() { assertThat(rebuilt.getDisplayName()).isEqualTo("new test instance"); assertThat(rebuilt.getNodeCount()).isEqualTo(1); assertThat(rebuilt.getProcessingUnits()).isEqualTo(2000); + assertThat(info.getAutoscalingConfig()).isEqualTo(autoscalingConfig); assertThat(rebuilt.getState()).isEqualTo(InstanceInfo.State.READY); assertThat(rebuilt.getLabels()).containsExactly("env", "prod", "region", "us"); assertEquals(Timestamp.ofTimeMicroseconds(86000), rebuilt.getUpdateTime()); @@ -119,12 +159,36 @@ public void testEquals() { InstanceConfigId configId1 = new InstanceConfigId("test-project", "test-instance-config"); InstanceConfigId configId2 = new InstanceConfigId("test-project", "other-test-instance-config"); + AutoscalingConfig autoscalingConfig1 = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(5000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + + AutoscalingConfig autoscalingConfig2 = + autoscalingConfig1 + .toBuilder() + .setAutoscalingLimits( + autoscalingConfig1 + .getAutoscalingLimits() + .toBuilder() + .setMinNodes(50) + .setMaxNodes(100)) + .build(); + InstanceInfo instance = InstanceInfo.newBuilder(id) .setInstanceConfigId(configId1) .setDisplayName("test instance") .setNodeCount(1) .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig1) .setState(InstanceInfo.State.READY) .addLabel("env", "prod") .addLabel("region", "us") @@ -137,6 +201,7 @@ public void testEquals() { .setDisplayName("test instance") .setNodeCount(1) .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig1) .setState(InstanceInfo.State.READY) .addLabel("region", "us") .addLabel("env", "prod") @@ -154,9 +219,22 @@ public void testEquals() { .setUpdateTime(Timestamp.ofTimeMicroseconds(8000)) .setCreateTime(Timestamp.ofTimeMicroseconds(4000)) .build(); + InstanceInfo instance4 = + InstanceInfo.newBuilder(id) + .setInstanceConfigId(configId2) + .setDisplayName("other test instance") + .setNodeCount(1) + .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig2) + .setState(InstanceInfo.State.READY) + .addLabel("env", "prod") + .setUpdateTime(Timestamp.ofTimeMicroseconds(8000)) + .setCreateTime(Timestamp.ofTimeMicroseconds(4000)) + .build(); EqualsTester tester = new EqualsTester(); tester.addEqualityGroup(instance, instance2); tester.addEqualityGroup(instance3); + tester.addEqualityGroup(instance4); tester.testEquals(); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceTest.java index 75b511c028..2dfa08ef36 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InstanceTest.java @@ -26,6 +26,7 @@ import com.google.cloud.Role; import com.google.cloud.Timestamp; import com.google.common.testing.EqualsTester; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import java.util.Collections; import org.junit.Before; import org.junit.Test; @@ -49,12 +50,24 @@ public void setUp() { public void buildInstance() { InstanceId id = new InstanceId("test-project", "test-instance"); InstanceConfigId configId = new InstanceConfigId("test-project", "test-instance-config"); + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(5000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); Instance instance = new Instance.Builder(instanceClient, dbClient, id) .setInstanceConfigId(configId) .setDisplayName("test instance") .setNodeCount(1) .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig) .setState(InstanceInfo.State.READY) .addLabel("env", "prod") .addLabel("region", "us") @@ -66,17 +79,30 @@ public void buildInstance() { assertThat(instance.getDisplayName()).isEqualTo("test instance"); assertThat(instance.getNodeCount()).isEqualTo(1); assertThat(instance.getProcessingUnits()).isEqualTo(2000); + assertThat(instance.getAutoscalingConfig()).isEqualTo(autoscalingConfig); assertThat(instance.getState()).isEqualTo(InstanceInfo.State.READY); assertThat(instance.getLabels()).containsExactly("env", "prod", "region", "us"); assertEquals(Timestamp.ofTimeMicroseconds(86000), instance.getUpdateTime()); assertEquals(Timestamp.ofTimeMicroseconds(46000), instance.getCreateTime()); - instance = instance.toBuilder().setDisplayName("new test instance").build(); + AutoscalingConfig newAutoscalingConfig = + autoscalingConfig + .toBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(10).setMaxNodes(100)) + .build(); + instance = + instance + .toBuilder() + .setDisplayName("new test instance") + .setAutoscalingConfig(newAutoscalingConfig) + .build(); assertThat(instance.getId()).isEqualTo(id); assertThat(instance.getInstanceConfigId()).isEqualTo(configId); assertThat(instance.getDisplayName()).isEqualTo("new test instance"); assertThat(instance.getNodeCount()).isEqualTo(1); assertThat(instance.getProcessingUnits()).isEqualTo(2000); + assertThat(instance.getAutoscalingConfig()).isEqualTo(newAutoscalingConfig); assertThat(instance.getState()).isEqualTo(InstanceInfo.State.READY); assertThat(instance.getLabels()).containsExactly("env", "prod", "region", "us"); assertEquals(Timestamp.ofTimeMicroseconds(86000), instance.getUpdateTime()); @@ -129,6 +155,91 @@ public void equality() { tester.testEquals(); } + @Test + public void equalityWithAutoscalingConfig() { + InstanceId id = new InstanceId("test-project", "test-instance"); + InstanceConfigId configId = new InstanceConfigId("test-project", "test-instance-config"); + AutoscalingConfig autoscalingConfig1 = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(5000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + + AutoscalingConfig autoscalingConfig2 = + autoscalingConfig1 + .toBuilder() + .setAutoscalingLimits( + autoscalingConfig1 + .getAutoscalingLimits() + .toBuilder() + .setMinNodes(50) + .setMaxNodes(100)) + .build(); + + Instance instance = + new Instance.Builder(instanceClient, dbClient, id) + .setInstanceConfigId(configId) + .setDisplayName("test instance") + .setNodeCount(1) + .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig1) + .setState(InstanceInfo.State.READY) + .addLabel("env", "prod") + .addLabel("region", "us") + .setUpdateTime(Timestamp.ofTimeMicroseconds(86000)) + .setCreateTime(Timestamp.ofTimeMicroseconds(46000)) + .build(); + Instance instance2 = + new Instance.Builder(instanceClient, dbClient, id) + .setInstanceConfigId(configId) + .setDisplayName("test instance") + .setNodeCount(1) + .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig1) + .setState(InstanceInfo.State.READY) + .addLabel("region", "us") + .addLabel("env", "prod") + .setUpdateTime(Timestamp.ofTimeMicroseconds(86000)) + .setCreateTime(Timestamp.ofTimeMicroseconds(46000)) + .build(); + Instance instance3 = + new Instance.Builder(instanceClient, dbClient, id) + .setInstanceConfigId(configId) + .setDisplayName("test instance") + .setNodeCount(1) + .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig1) + .setState(InstanceInfo.State.READY) + .addLabel("env", "prod") + .setUpdateTime(Timestamp.ofTimeMicroseconds(8000)) + .setCreateTime(Timestamp.ofTimeMicroseconds(4000)) + .build(); + Instance instance4 = + new Instance.Builder(instanceClient, dbClient, id) + .setInstanceConfigId(configId) + .setDisplayName("test instance") + .setNodeCount(1) + .setProcessingUnits(2000) + .setAutoscalingConfig(autoscalingConfig2) + .setState(InstanceInfo.State.READY) + .addLabel("region", "us") + .addLabel("env", "prod") + .setUpdateTime(Timestamp.ofTimeMicroseconds(86000)) + .setCreateTime(Timestamp.ofTimeMicroseconds(46000)) + .build(); + EqualsTester tester = new EqualsTester(); + tester.addEqualityGroup(instance, instance2); + tester.addEqualityGroup(instance3); + tester.addEqualityGroup(instance4); + tester.testEquals(); + } + @Test public void listDatabases() { InstanceId id = new InstanceId("test-project", "test-instance"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java index e8e8ce2b4b..f21441f30a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITInstanceAdminTest.java @@ -29,6 +29,7 @@ import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.common.collect.Iterators; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; import com.google.spanner.admin.instance.v1.UpdateInstanceMetadata; import java.util.ArrayList; import java.util.List; @@ -121,6 +122,52 @@ public void updateInstance() throws Exception { instanceClient.updateInstance(toUpdate, InstanceInfo.InstanceField.DISPLAY_NAME).get(); } + @Test + public void updateInstanceWithAutoscalingConfig() throws Exception { + assumeFalse( + "The emulator does not support updating instances with autoscaler", isUsingEmulator()); + + Instance instance = + instanceClient.getInstance(env.getTestHelper().getInstanceId().getInstance()); + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder() + .setMinProcessingUnits(1000) + .setMaxProcessingUnits(2000)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + InstanceInfo toUpdate = + InstanceInfo.newBuilder(env.getTestHelper().getInstanceId()) + .setNodeCount(0) + .setAutoscalingConfig(autoscalingConfig) + .build(); + OperationFuture op = + instanceClient.updateInstance(toUpdate, InstanceInfo.InstanceField.AUTOSCALING_CONFIG); + Instance newInstance = op.get(); + assertThat(newInstance.getAutoscalingConfig()).isEqualTo(autoscalingConfig); + + Instance newInstanceFromGet = + instanceClient.getInstance(env.getTestHelper().getInstanceId().getInstance()); + assertThat(newInstanceFromGet).isEqualTo(newInstance); + + // Revert back to the instance original state. + toUpdate = + InstanceInfo.newBuilder(instance.getId()) + .setAutoscalingConfig(null) + .setNodeCount(instance.getNodeCount()) + .build(); + instanceClient + .updateInstance( + toUpdate, + InstanceInfo.InstanceField.AUTOSCALING_CONFIG, + InstanceInfo.InstanceField.NODE_COUNT) + .get(); + } + @Test public void updateInstanceViaEntity() throws Exception { assumeFalse("The emulator does not support updating instances", isUsingEmulator());