Skip to content

Commit

Permalink
docs: improve timeout and retry sample (#2630)
Browse files Browse the repository at this point in the history
* docs: improve timeout and retry sample

The timeout and retry sample used 60 seconds for both initial,
max, and total timeout. It also used a 1.0 multiplier for the
timeout value. This made it impossible to explain how to set
an increasing RPC timeout value. It also rendered adding the
DEADLINE_EXCEEDED error code as a retryable code superfluous.

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* chore: update timeout values based on what is possible with other languages

* test: move sample test to separate class

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
olavloite and gcf-owl-bot[bot] authored Sep 28, 2023
1 parent 3f48624 commit f03ce56
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 com.google.api.gax.grpc.testing.LocalChannelProvider;
import com.google.cloud.NoCredentials;
import io.grpc.Server;
import io.grpc.inprocess.InProcessServerBuilder;
import java.io.IOException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;

abstract class AbstractMockServerTest {
protected static MockSpannerServiceImpl mockSpanner;
protected static Server server;
protected static LocalChannelProvider channelProvider;

private Spanner spanner;

@BeforeClass
public static void startMockServer() throws IOException {
mockSpanner = new MockSpannerServiceImpl();
mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions.

String uniqueName = InProcessServerBuilder.generateName();
server = InProcessServerBuilder.forName(uniqueName).addService(mockSpanner).build().start();
channelProvider = LocalChannelProvider.create(uniqueName);
}

@AfterClass
public static void stopMockServer() throws InterruptedException {
server.shutdown();
server.awaitTermination();
}

@Before
public void createSpannerInstance() {
spanner =
SpannerOptions.newBuilder()
.setProjectId("test-project")
.setChannelProvider(channelProvider)
.setCredentials(NoCredentials.getInstance())
.setSessionPoolOption(SessionPoolOptions.newBuilder().setFailOnSessionLeak().build())
.build()
.getService();
}

@After
public void cleanup() {
spanner.close();
mockSpanner.reset();
mockSpanner.removeAllExecutionTimes();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import com.google.cloud.spanner.AbstractResultSet.GrpcStreamIterator;
import com.google.cloud.spanner.AsyncResultSet.CallbackResponse;
import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture;
import com.google.cloud.spanner.BaseSessionPoolTest.FakeClock;
import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime;
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
import com.google.cloud.spanner.Options.RpcPriority;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.StatusCode;
import com.google.cloud.NoCredentials;
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.threeten.bp.Duration;

/** Tests for samples that use an in-mem mock server instead of running on real Cloud Spanner. */
@RunWith(JUnit4.class)
public class SamplesMockServerTest extends AbstractMockServerTest {

@Test
public void testSampleRetrySettings() {
String sql =
"INSERT INTO Singers (SingerId, FirstName, LastName)\n"
+ "VALUES (20, 'George', 'Washington')";
mockSpanner.putStatementResult(StatementResult.update(Statement.of(sql), 1L));

SpannerOptions.Builder builder =
SpannerOptions.newBuilder()
.setProjectId("p")
.setCredentials(NoCredentials.getInstance())
.setChannelProvider(channelProvider);
// Set a timeout value for the ExecuteSql RPC that is so low that it will always be triggered.
// This should cause the RPC to fail with a DEADLINE_EXCEEDED error.
builder
.getSpannerStubSettingsBuilder()
.executeSqlSettings()
.setRetryableCodes(StatusCode.Code.UNAVAILABLE)
.setRetrySettings(
RetrySettings.newBuilder()
.setInitialRetryDelay(Duration.ofMillis(500))
.setMaxRetryDelay(Duration.ofSeconds(16))
.setRetryDelayMultiplier(1.5)
.setInitialRpcTimeout(Duration.ofNanos(1L))
.setMaxRpcTimeout(Duration.ofNanos(1L))
.setRpcTimeoutMultiplier(1.0)
.setTotalTimeout(Duration.ofNanos(1L))
.build());
// Create a Spanner client using the custom retry and timeout settings.
try (Spanner spanner = builder.build().getService()) {
DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of("p", "i", "d"));
SpannerException exception =
assertThrows(
SpannerException.class,
() ->
client
.readWriteTransaction()
.run(transaction -> transaction.executeUpdate(Statement.of(sql))));
assertEquals(ErrorCode.DEADLINE_EXCEEDED, exception.getErrorCode());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ static void executeSqlWithCustomTimeoutAndRetrySettings(
.getSpannerStubSettingsBuilder()
.executeSqlSettings()
// Configure which errors should be retried.
.setRetryableCodes(Code.DEADLINE_EXCEEDED, Code.UNAVAILABLE)
.setRetryableCodes(Code.UNAVAILABLE)
.setRetrySettings(
RetrySettings.newBuilder()
// Configure retry delay settings.
// The initial amount of time to wait before retrying the request.
.setInitialRetryDelay(Duration.ofMillis(500))
// The maximum amount of time to wait before retrying. I.e. after this value is
// reached, the wait time will not increase further by the multiplier.
.setMaxRetryDelay(Duration.ofSeconds(64))
.setMaxRetryDelay(Duration.ofSeconds(16))
// The previous wait time is multiplied by this multiplier to come up with the next
// wait time, until the max is reached.
.setRetryDelayMultiplier(1.5)
Expand Down

0 comments on commit f03ce56

Please sign in to comment.