|
19 | 19 | import static com.google.common.truth.Truth.assertThat; |
20 | 20 | import static org.junit.Assert.fail; |
21 | 21 |
|
| 22 | +import com.google.api.core.ApiFunction; |
22 | 23 | import com.google.api.gax.grpc.testing.LocalChannelProvider; |
| 24 | +import com.google.api.gax.retrying.RetrySettings; |
| 25 | +import com.google.api.gax.rpc.UnaryCallSettings.Builder; |
23 | 26 | import com.google.cloud.NoCredentials; |
| 27 | +import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime; |
24 | 28 | import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; |
25 | 29 | import com.google.cloud.spanner.TransactionRunner.TransactionCallable; |
26 | 30 | import com.google.protobuf.ListValue; |
|
30 | 34 | import com.google.spanner.v1.TypeCode; |
31 | 35 | import io.grpc.Server; |
32 | 36 | import io.grpc.Status; |
| 37 | +import io.grpc.StatusRuntimeException; |
33 | 38 | import io.grpc.inprocess.InProcessServerBuilder; |
34 | 39 | import io.opencensus.trace.Tracing; |
35 | | -import java.io.IOException; |
36 | 40 | import java.lang.reflect.Modifier; |
37 | 41 | import java.util.Map; |
38 | 42 | import java.util.concurrent.ScheduledThreadPoolExecutor; |
39 | 43 | import org.junit.After; |
40 | 44 | import org.junit.AfterClass; |
41 | 45 | import org.junit.Before; |
42 | 46 | import org.junit.BeforeClass; |
| 47 | +import org.junit.Rule; |
43 | 48 | import org.junit.Test; |
44 | 49 | import org.junit.experimental.categories.Category; |
| 50 | +import org.junit.rules.ExpectedException; |
45 | 51 | import org.junit.runner.RunWith; |
46 | 52 | import org.junit.runners.JUnit4; |
| 53 | +import org.threeten.bp.Duration; |
47 | 54 |
|
48 | 55 | @RunWith(JUnit4.class) |
49 | 56 | @Category(TracerTest.class) |
@@ -84,9 +91,36 @@ public class SpanTest { |
84 | 91 | .build(); |
85 | 92 | private Spanner spanner; |
86 | 93 | private DatabaseClient client; |
| 94 | + private Spanner spannerWithTimeout; |
| 95 | + private DatabaseClient clientWithTimeout; |
87 | 96 | private static FailOnOverkillTraceComponentImpl failOnOverkillTraceComponent = |
88 | 97 | new FailOnOverkillTraceComponentImpl(); |
89 | 98 |
|
| 99 | + private static final SimulatedExecutionTime ONE_SECOND = |
| 100 | + SimulatedExecutionTime.ofMinimumAndRandomTime(1000, 0); |
| 101 | + private static final Statement SELECT1AND2 = |
| 102 | + Statement.of("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL1"); |
| 103 | + private static final ResultSetMetadata SELECT1AND2_METADATA = |
| 104 | + ResultSetMetadata.newBuilder() |
| 105 | + .setRowType( |
| 106 | + StructType.newBuilder() |
| 107 | + .addFields( |
| 108 | + Field.newBuilder() |
| 109 | + .setName("COL1") |
| 110 | + .setType( |
| 111 | + com.google.spanner.v1.Type.newBuilder() |
| 112 | + .setCode(TypeCode.INT64) |
| 113 | + .build()) |
| 114 | + .build()) |
| 115 | + .build()) |
| 116 | + .build(); |
| 117 | + private static final StatusRuntimeException FAILED_PRECONDITION = |
| 118 | + io.grpc.Status.FAILED_PRECONDITION |
| 119 | + .withDescription("Non-retryable test exception.") |
| 120 | + .asRuntimeException(); |
| 121 | + |
| 122 | + @Rule public ExpectedException expectedException = ExpectedException.none(); |
| 123 | + |
90 | 124 | @BeforeClass |
91 | 125 | public static void startStaticServer() throws Exception { |
92 | 126 | mockSpanner = new MockSpannerServiceImpl(); |
@@ -126,27 +160,93 @@ public static void stopServer() throws InterruptedException { |
126 | 160 | } |
127 | 161 |
|
128 | 162 | @Before |
129 | | - public void setUp() throws IOException { |
130 | | - spanner = |
| 163 | + public void setUp() throws Exception { |
| 164 | + SpannerOptions.Builder builder = |
131 | 165 | SpannerOptions.newBuilder() |
132 | 166 | .setProjectId(TEST_PROJECT) |
133 | 167 | .setChannelProvider(channelProvider) |
134 | 168 | .setCredentials(NoCredentials.getInstance()) |
135 | | - .setSessionPoolOption(SessionPoolOptions.newBuilder().setMinSessions(0).build()) |
136 | | - .build() |
137 | | - .getService(); |
| 169 | + .setSessionPoolOption(SessionPoolOptions.newBuilder().setMinSessions(0).build()); |
| 170 | + |
| 171 | + spanner = builder.build().getService(); |
| 172 | + |
138 | 173 | client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); |
139 | 174 |
|
| 175 | + final RetrySettings retrySettings = |
| 176 | + RetrySettings.newBuilder() |
| 177 | + .setInitialRetryDelay(Duration.ofMillis(1L)) |
| 178 | + .setMaxRetryDelay(Duration.ofMillis(1L)) |
| 179 | + .setInitialRpcTimeout(Duration.ofMillis(75L)) |
| 180 | + .setMaxRpcTimeout(Duration.ofMillis(75L)) |
| 181 | + .setMaxAttempts(3) |
| 182 | + .setTotalTimeout(Duration.ofMillis(200L)) |
| 183 | + .build(); |
| 184 | + RetrySettings commitRetrySettings = |
| 185 | + RetrySettings.newBuilder() |
| 186 | + .setInitialRetryDelay(Duration.ofMillis(1L)) |
| 187 | + .setMaxRetryDelay(Duration.ofMillis(1L)) |
| 188 | + .setInitialRpcTimeout(Duration.ofMillis(5000L)) |
| 189 | + .setMaxRpcTimeout(Duration.ofMillis(10000L)) |
| 190 | + .setMaxAttempts(1) |
| 191 | + .setTotalTimeout(Duration.ofMillis(20000L)) |
| 192 | + .build(); |
| 193 | + builder |
| 194 | + .getSpannerStubSettingsBuilder() |
| 195 | + .applyToAllUnaryMethods( |
| 196 | + new ApiFunction<Builder<?, ?>, Void>() { |
| 197 | + @Override |
| 198 | + public Void apply(Builder<?, ?> input) { |
| 199 | + input.setRetrySettings(retrySettings); |
| 200 | + return null; |
| 201 | + } |
| 202 | + }); |
| 203 | + builder |
| 204 | + .getSpannerStubSettingsBuilder() |
| 205 | + .executeStreamingSqlSettings() |
| 206 | + .setRetrySettings(retrySettings); |
| 207 | + builder.getSpannerStubSettingsBuilder().commitSettings().setRetrySettings(commitRetrySettings); |
| 208 | + builder |
| 209 | + .getSpannerStubSettingsBuilder() |
| 210 | + .executeStreamingSqlSettings() |
| 211 | + .setRetrySettings(retrySettings); |
| 212 | + builder.getSpannerStubSettingsBuilder().streamingReadSettings().setRetrySettings(retrySettings); |
| 213 | + spannerWithTimeout = builder.build().getService(); |
| 214 | + clientWithTimeout = |
| 215 | + spannerWithTimeout.getDatabaseClient( |
| 216 | + DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); |
| 217 | + |
140 | 218 | failOnOverkillTraceComponent.clearSpans(); |
141 | 219 | } |
142 | 220 |
|
143 | 221 | @After |
144 | | - public void tearDown() throws Exception { |
| 222 | + public void tearDown() { |
145 | 223 | spanner.close(); |
146 | 224 | mockSpanner.reset(); |
147 | 225 | mockSpanner.removeAllExecutionTimes(); |
148 | 226 | } |
149 | 227 |
|
| 228 | + @Test |
| 229 | + public void singleUseNonRetryableErrorOnNext() { |
| 230 | + expectedException.expect(SpannerMatchers.isSpannerException(ErrorCode.FAILED_PRECONDITION)); |
| 231 | + try (ResultSet rs = client.singleUse().executeQuery(SELECT1AND2)) { |
| 232 | + mockSpanner.addException(FAILED_PRECONDITION); |
| 233 | + while (rs.next()) { |
| 234 | + // Just consume the result set. |
| 235 | + } |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + @Test |
| 240 | + public void singleUseExecuteStreamingSqlTimeout() { |
| 241 | + expectedException.expect(SpannerMatchers.isSpannerException(ErrorCode.DEADLINE_EXCEEDED)); |
| 242 | + try (ResultSet rs = clientWithTimeout.singleUse().executeQuery(SELECT1AND2)) { |
| 243 | + mockSpanner.setExecuteStreamingSqlExecutionTime(ONE_SECOND); |
| 244 | + while (rs.next()) { |
| 245 | + // Just consume the result set. |
| 246 | + } |
| 247 | + } |
| 248 | + } |
| 249 | + |
150 | 250 | @Test |
151 | 251 | public void singleUse() { |
152 | 252 | try (ResultSet rs = client.singleUse().executeQuery(SELECT1)) { |
@@ -193,7 +293,6 @@ public Void run(TransactionContext transaction) throws Exception { |
193 | 293 | return null; |
194 | 294 | } |
195 | 295 | }); |
196 | | - |
197 | 296 | Map<String, Boolean> spans = failOnOverkillTraceComponent.getSpans(); |
198 | 297 | assertThat(spans.size()).isEqualTo(6); |
199 | 298 | assertThat(spans).containsEntry("CloudSpanner.ReadWriteTransaction", true); |
|
0 commit comments