Skip to content

build: remove native image tests from required checks #2584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
43f5b0a
test: backoff and retry for RESOURCE_EXHAUSTED when creating test ins…
olavloite Aug 11, 2023
b695537
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 11, 2023
38a124c
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 11, 2023
b5a14f5
Merge branch 'retry-resource-exhausted-in-integration-tests' of https…
gcf-owl-bot[bot] Aug 11, 2023
52bb841
fix: correctly unwrap SpannerException
olavloite Aug 11, 2023
e1c8065
Merge branch 'retry-resource-exhausted-in-integration-tests' of githu…
olavloite Aug 11, 2023
c87c4cb
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 11, 2023
3fed4bf
test: back off more and retry more
olavloite Aug 11, 2023
a88a0d3
fix: prevent -1 as sleep value
olavloite Aug 11, 2023
64dfbeb
build: reuse test instance
olavloite Aug 11, 2023
fa55600
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 11, 2023
8afc669
build: try to get native build to pick up instance
olavloite Aug 11, 2023
ef714d3
Merge branch 'reuse-integration-test-instance' of github.com:googleap…
olavloite Aug 11, 2023
0a3f343
build: remove native image tests from required checks
olavloite Aug 11, 2023
c153bf7
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 11, 2023
4a42189
Merge branch 'retry-resource-exhausted-in-integration-tests' into mak…
olavloite Aug 11, 2023
56a7a72
Merge branch 'reuse-integration-test-instance' into make-native-tests…
olavloite Aug 11, 2023
b8db11f
test: ignore creation error
olavloite Aug 11, 2023
9ddae56
test: use us-central config
olavloite Aug 11, 2023
4bec8f3
fix: fall back to first config for emulator
olavloite Aug 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/sync-repo-settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ branchProtectionRules:
- compile (8)
- compile (11)
- OwlBot Post Processor
- 'Kokoro - Test: Java GraalVM Native Image'
- 'Kokoro - Test: Java 17 GraalVM Native Image'
- pattern: 3.3.x
isAdminEnforced: true
requiredApprovingReviewCount: 1
Expand Down
6 changes: 4 additions & 2 deletions .kokoro/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ integration)
-Dclirr.skip=true \
-Denforcer.skip=true \
-Dmaven.main.skip=true \
-Dspanner.gce.config.project_id=gcloud-devel \
-Dspanner.testenv.instance=projects/gcloud-devel/instances/java-client-integration-tests \
-fae \
verify
RETURN_CODE=$?
Expand Down Expand Up @@ -126,12 +128,12 @@ integration-cloud-staging)
;;
graalvm)
# Run Unit and Integration Tests with Native Image
mvn test -Pnative -Penable-integration-tests
mvn test -Pnative -Penable-integration-tests -Dspanner.gce.config.project_id=gcloud-devel -Dspanner.testenv.instance=projects/gcloud-devel/instances/java-client-integration-tests
RETURN_CODE=$?
;;
graalvm17)
# Run Unit and Integration Tests with Native Image
mvn test -Pnative -Penable-integration-tests
mvn test -Pnative -Penable-integration-tests -Dspanner.gce.config.project_id=gcloud-devel -Dspanner.testenv.instance=projects/gcloud-devel/instances/java-client-integration-tests
RETURN_CODE=$?
;;
slowtests)
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ If you are using Maven without the BOM, add this to your dependencies:
If you are using Gradle 5.x or later, add this to your dependencies:

```Groovy
implementation platform('com.google.cloud:libraries-bom:26.21.0')
implementation platform('com.google.cloud:libraries-bom:26.22.0')

implementation 'com.google.cloud:google-cloud-spanner'
```
If you are using Gradle without BOM, add this to your dependencies:

```Groovy
implementation 'com.google.cloud:google-cloud-spanner:6.44.0'
implementation 'com.google.cloud:google-cloud-spanner:6.45.0'
```

If you are using SBT, add this to your dependencies:

```Scala
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.44.0"
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.45.0"
```
<!-- {x-version-update-end} -->

Expand Down Expand Up @@ -430,7 +430,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html
[stability-image]: https://img.shields.io/badge/stability-stable-green
[maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.44.0
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.45.0
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles
Expand Down
3 changes: 3 additions & 0 deletions google-cloud-spanner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
<configuration>
<buildArgs combine.children="append">
<buildArg>-Dspanner.testenv.config.class=${spanner.testenv.config.class}</buildArg>
<buildArg>-Dspanner.testenv.instance=${spanner.testenv.instance}</buildArg>
<buildArg>-Dspanner.gce.config.project_id=${spanner.gce.config.project_id}</buildArg>
<buildArg>-Dspanner.testenv.kms_key.name=${spanner.testenv.kms_key.name}</buildArg>
</buildArgs>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@

import static com.google.common.base.Preconditions.checkState;

import com.google.api.client.util.ExponentialBackOff;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.api.gax.paging.Page;
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.testing.EmulatorSpannerHelper;
import com.google.cloud.spanner.testing.RemoteSpannerHelper;
import com.google.common.collect.Iterators;
import com.google.spanner.admin.instance.v1.CreateInstanceMetadata;
import io.grpc.Status;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -52,6 +53,9 @@ public class IntegrationTestEnv extends ExternalResource {
*/
public static final String TEST_INSTANCE_PROPERTY = "spanner.testenv.instance";

public static final String MAX_CREATE_INSTANCE_ATTEMPTS =
"spanner.testenv.max_create_instance_attempts";

private static final Logger logger = Logger.getLogger(IntegrationTestEnv.class.getName());

private TestEnvConfig config;
Expand Down Expand Up @@ -126,9 +130,14 @@ protected void after() {
this.config.tearDown();
}

private void initializeInstance(InstanceId instanceId) {
InstanceConfig instanceConfig =
Iterators.get(instanceAdminClient.listInstanceConfigs().iterateAll().iterator(), 0, null);
private void initializeInstance(InstanceId instanceId) throws Exception {
InstanceConfig instanceConfig;
try {
instanceConfig = instanceAdminClient.getInstanceConfig("regional-us-central1");
} catch (Throwable ignore) {
instanceConfig =
Iterators.get(instanceAdminClient.listInstanceConfigs().iterateAll().iterator(), 0, null);
}
checkState(instanceConfig != null, "No instance configs found");

InstanceConfigId configId = instanceConfig.getId();
Expand All @@ -142,40 +151,62 @@ private void initializeInstance(InstanceId instanceId) {
OperationFuture<Instance, CreateInstanceMetadata> op =
instanceAdminClient.createInstance(instance);
Instance createdInstance;
int maxAttempts = 25;
try {
createdInstance = op.get();
} catch (Exception e) {
boolean cancelled = false;
maxAttempts =
Integer.parseInt(
System.getProperty(MAX_CREATE_INSTANCE_ATTEMPTS, String.valueOf(maxAttempts)));
} catch (NumberFormatException ignore) {
// Ignore and fall back to the default.
}
ExponentialBackOff backOff =
new ExponentialBackOff.Builder()
.setInitialIntervalMillis(5_000)
.setMaxIntervalMillis(500_000)
.setMultiplier(2.0)
.build();
int attempts = 0;
while (true) {
try {
// Try to cancel the createInstance operation.
instanceAdminClient.cancelOperation(op.getName());
com.google.longrunning.Operation createOperation =
instanceAdminClient.getOperation(op.getName());
cancelled =
createOperation.hasError()
&& createOperation.getError().getCode() == Status.CANCELLED.getCode().value();
if (cancelled) {
logger.info("Cancelled the createInstance operation because the operation failed");
} else {
logger.info(
"Tried to cancel the createInstance operation because the operation failed, but the operation could not be cancelled. Current status: "
+ createOperation.getError().getCode());
}
} catch (Throwable t) {
logger.log(Level.WARNING, "Failed to cancel the createInstance operation", t);
}
if (!cancelled) {
try {
instanceAdminClient.deleteInstance(instanceId.getInstance());
logger.info(
"Deleted the test instance because the createInstance operation failed and cancelling the operation did not succeed");
} catch (Throwable t) {
logger.log(Level.WARNING, "Failed to delete the test instance", t);
createdInstance = op.get();
} catch (Exception e) {
SpannerException spannerException =
(e instanceof ExecutionException && e.getCause() != null)
? SpannerExceptionFactory.asSpannerException(e.getCause())
: SpannerExceptionFactory.asSpannerException(e);
if (attempts < maxAttempts && isRetryableResourceExhaustedException(spannerException)) {
attempts++;
if (spannerException.getRetryDelayInMillis() > 0L) {
//noinspection BusyWait
Thread.sleep(spannerException.getRetryDelayInMillis());
} else {
// The Math.max(...) prevents Backoff#STOP (=-1) to be used as the sleep value.
//noinspection BusyWait
Thread.sleep(Math.max(backOff.getMaxIntervalMillis(), backOff.nextBackOffMillis()));
}
continue;
}
throw SpannerExceptionFactory.newSpannerException(
spannerException.getErrorCode(),
String.format(
"Could not create test instance and giving up after %d attempts: %s",
attempts, e.getMessage()),
e);
}
throw SpannerExceptionFactory.newSpannerException(e);
logger.log(Level.INFO, "Created test instance: {0}", createdInstance.getId());
break;
}
}

static boolean isRetryableResourceExhaustedException(SpannerException exception) {
if (exception.getErrorCode() != ErrorCode.RESOURCE_EXHAUSTED) {
return false;
}
logger.log(Level.INFO, "Created test instance: {0}", createdInstance.getId());
return exception
.getMessage()
.contains(
"Quota exceeded for quota metric 'Instance create requests' and limit 'Instance create requests per minute'")
|| exception.getMessage().matches(".*cannot add \\d+ nodes in region.*");
}

private void cleanUpOldDatabases(InstanceId instanceId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spanner;

import static com.google.cloud.spanner.IntegrationTestEnv.isRetryableResourceExhaustedException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class IntegrationTestEnvTest {

@Test
public void testIsRetryableResourceExhaustedException() {
assertFalse(
isRetryableResourceExhaustedException(
SpannerExceptionFactory.newSpannerException(ErrorCode.INVALID_ARGUMENT, "test")));
assertFalse(
isRetryableResourceExhaustedException(
SpannerExceptionFactory.newSpannerException(ErrorCode.RESOURCE_EXHAUSTED, "test")));
assertTrue(
isRetryableResourceExhaustedException(
SpannerExceptionFactory.newSpannerException(
ErrorCode.RESOURCE_EXHAUSTED,
"Operation with name \"projects/my-project/instances/my-instance/operations/32bb3dccf4243afc\" failed with status = GrpcStatusCode{transportCode=RESOURCE_EXHAUSTED} and message = Project 123 cannot add 1 nodes in region .")));
assertTrue(
isRetryableResourceExhaustedException(
SpannerExceptionFactory.newSpannerException(
ErrorCode.RESOURCE_EXHAUSTED,
"Operation with name \"projects/my-project/instances/my-instance/operations/32bb3dccf4243afc\" failed with status = GrpcStatusCode{transportCode=RESOURCE_EXHAUSTED} and message = Project 123 cannot add 99 nodes in region .")));
assertTrue(
isRetryableResourceExhaustedException(
SpannerExceptionFactory.newSpannerException(
ErrorCode.RESOURCE_EXHAUSTED,
"Could not create instance. Quota exceeded for quota metric 'Instance create requests' and limit 'Instance create requests per minute'")));
}
}