feat: Integration test for End to End tracing (#3691) · googleapis/java-spanner@bf1a07a (original) (raw)
``
1
`+
/*
`
``
2
`+
- Copyright 2025 Google LLC
`
``
3
`+
`
``
4
`+
- Licensed under the Apache License, Version 2.0 (the "License");
`
``
5
`+
- you may not use this file except in compliance with the License.
`
``
6
`+
- You may obtain a copy of the License at
`
``
7
`+
`
``
8
`+
`
``
9
`+
`
``
10
`+
- Unless required by applicable law or agreed to in writing, software
`
``
11
`+
- distributed under the License is distributed on an "AS IS" BASIS,
`
``
12
`+
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
`
``
13
`+
- See the License for the specific language governing permissions and
`
``
14
`+
- limitations under the License.
`
``
15
`+
*/
`
``
16
+
``
17
`+
package com.google.cloud.spanner.it;
`
``
18
+
``
19
`+
import static com.google.common.truth.Truth.assertThat;
`
``
20
`+
import static org.junit.Assert.assertTrue;
`
``
21
`+
import static org.junit.Assume.assumeTrue;
`
``
22
+
``
23
`+
import com.google.api.gax.core.FixedCredentialsProvider;
`
``
24
`+
import com.google.api.gax.rpc.ApiException;
`
``
25
`+
import com.google.api.gax.rpc.ResourceExhaustedException;
`
``
26
`+
import com.google.api.gax.rpc.StatusCode;
`
``
27
`+
import com.google.cloud.spanner.Database;
`
``
28
`+
import com.google.cloud.spanner.DatabaseClient;
`
``
29
`+
import com.google.cloud.spanner.IntegrationTestEnv;
`
``
30
`+
import com.google.cloud.spanner.IntegrationTestEnv.TestEnvOptions;
`
``
31
`+
import com.google.cloud.spanner.ParallelIntegrationTest;
`
``
32
`+
import com.google.cloud.spanner.ResultSet;
`
``
33
`+
import com.google.cloud.spanner.SpannerOptions;
`
``
34
`+
import com.google.cloud.spanner.SpannerOptionsHelper;
`
``
35
`+
import com.google.cloud.spanner.Statement;
`
``
36
`+
import com.google.cloud.spanner.Struct;
`
``
37
`+
import com.google.cloud.spanner.Type;
`
``
38
`+
import com.google.cloud.spanner.Type.StructField;
`
``
39
`+
import com.google.cloud.spanner.connection.ConnectionOptions;
`
``
40
`+
import com.google.cloud.trace.v1.TraceServiceClient;
`
``
41
`+
import com.google.cloud.trace.v1.TraceServiceSettings;
`
``
42
`+
import com.google.common.base.Stopwatch;
`
``
43
`+
import io.opentelemetry.api.trace.Span;
`
``
44
`+
import io.opentelemetry.api.trace.Tracer;
`
``
45
`+
import io.opentelemetry.context.Scope;
`
``
46
`+
import java.io.IOException;
`
``
47
`+
import java.util.Arrays;
`
``
48
`+
import java.util.Collection;
`
``
49
`+
import java.util.concurrent.TimeUnit;
`
``
50
`+
import org.junit.AfterClass;
`
``
51
`+
import org.junit.BeforeClass;
`
``
52
`+
import org.junit.ClassRule;
`
``
53
`+
import org.junit.Test;
`
``
54
`+
import org.junit.experimental.categories.Category;
`
``
55
`+
import org.junit.runner.RunWith;
`
``
56
`+
import org.junit.runners.JUnit4;
`
``
57
+
``
58
`+
/** Integration tests for End to End Tracing. */
`
``
59
`+
@Category(ParallelIntegrationTest.class)
`
``
60
`+
@RunWith(JUnit4.class)
`
``
61
`+
public class ITEndToEndTracingTest {
`
``
62
`+
public static Collection testEnvOptions =
`
``
63
`+
Arrays.asList(TestEnvOptions.USE_END_TO_END_TRACING);
`
``
64
`+
@ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(testEnvOptions);
`
``
65
`+
private static DatabaseClient googleStandardSQLClient;
`
``
66
+
``
67
`+
static {
`
``
68
`+
SpannerOptionsHelper.resetActiveTracingFramework();
`
``
69
`+
SpannerOptions.enableOpenTelemetryTraces();
`
``
70
`+
}
`
``
71
+
``
72
`+
private static String selectValueQuery = "SELECT @p1 + @p1";
`
``
73
+
``
74
`+
@BeforeClass
`
``
75
`+
public static void setUp() {
`
``
76
`+
setUpDatabase();
`
``
77
`+
}
`
``
78
+
``
79
`+
public static void setUpDatabase() {
`
``
80
`+
// Empty database.
`
``
81
`+
Database googleStandardSQLDatabase = env.getTestHelper().createTestDatabase();
`
``
82
`+
googleStandardSQLClient = env.getTestHelper().getDatabaseClient(googleStandardSQLDatabase);
`
``
83
`+
}
`
``
84
+
``
85
`+
@AfterClass
`
``
86
`+
public static void teardown() {
`
``
87
`+
ConnectionOptions.closeSpanner();
`
``
88
`+
}
`
``
89
+
``
90
`+
private void assertTrace(String traceId) throws IOException, InterruptedException {
`
``
91
`+
TraceServiceSettings settings =
`
``
92
`+
env.getTestHelper().getOptions().getCredentials() == null
`
``
93
`+
? TraceServiceSettings.newBuilder().build()
`
``
94
`+
: TraceServiceSettings.newBuilder()
`
``
95
`+
.setCredentialsProvider(
`
``
96
`+
FixedCredentialsProvider.create(
`
``
97
`+
env.getTestHelper().getOptions().getCredentials()))
`
``
98
`+
.build();
`
``
99
`+
try (TraceServiceClient client = TraceServiceClient.create(settings)) {
`
``
100
`+
boolean foundTrace = false;
`
``
101
`+
Stopwatch metricsPollingStopwatch = Stopwatch.createStarted();
`
``
102
`+
while (!foundTrace && metricsPollingStopwatch.elapsed(TimeUnit.SECONDS) < 30) {
`
``
103
`+
// Try every 5 seconds
`
``
104
`+
Thread.sleep(5000);
`
``
105
`+
try {
`
``
106
`+
foundTrace =
`
``
107
`+
client.getTrace(env.getTestHelper().getInstanceId().getProject(), traceId)
`
``
108
`+
.getSpansList().stream()
`
``
109
`+
.anyMatch(span -> "Spanner.ExecuteStreamingSql".equals(span.getName()));
`
``
110
`+
} catch (ApiException apiException) {
`
``
111
`+
assumeTrue(
`
``
112
`+
apiException.getStatusCode() != null
`
``
113
`+
&& StatusCode.Code.NOT_FOUND.equals(apiException.getStatusCode().getCode()));
`
``
114
`+
System.out.println("Trace NOT_FOUND error ignored");
`
``
115
`+
}
`
``
116
`+
}
`
``
117
`+
assertTrue(foundTrace);
`
``
118
`+
} catch (ResourceExhaustedException resourceExhaustedException) {
`
``
119
`+
if (resourceExhaustedException
`
``
120
`+
.getMessage()
`
``
121
`+
.contains("Quota exceeded for quota metric 'Read requests (free)'")) {
`
``
122
`+
// Ignore and allow the test to succeed.
`
``
123
`+
System.out.println("RESOURCE_EXHAUSTED error ignored");
`
``
124
`+
} else {
`
``
125
`+
throw resourceExhaustedException;
`
``
126
`+
}
`
``
127
`+
}
`
``
128
`+
}
`
``
129
+
``
130
`+
private Struct executeWithRowResultType(Statement statement, Type expectedRowType) {
`
``
131
`+
ResultSet resultSet = statement.executeQuery(googleStandardSQLClient.singleUse());
`
``
132
`+
assertThat(resultSet.next()).isTrue();
`
``
133
`+
assertThat(resultSet.getType()).isEqualTo(expectedRowType);
`
``
134
`+
Struct row = resultSet.getCurrentRowAsStruct();
`
``
135
`+
assertThat(resultSet.next()).isFalse();
`
``
136
`+
return row;
`
``
137
`+
}
`
``
138
+
``
139
`+
@Test
`
``
140
`+
public void simpleSelect() throws IOException, InterruptedException {
`
``
141
`+
Tracer tracer =
`
``
142
`+
env.getTestHelper()
`
``
143
`+
.getOptions()
`
``
144
`+
.getOpenTelemetry()
`
``
145
`+
.getTracer(ITEndToEndTracingTest.class.getName());
`
``
146
`+
Span span = tracer.spanBuilder("simpleSelect").startSpan();
`
``
147
`+
Scope scope = span.makeCurrent();
`
``
148
`+
Type rowType = Type.struct(StructField.of("", Type.int64()));
`
``
149
`+
Struct row =
`
``
150
`+
executeWithRowResultType(
`
``
151
`+
Statement.newBuilder(selectValueQuery).bind("p1").to(1234).build(), rowType);
`
``
152
`+
assertThat(row.isNull(0)).isFalse();
`
``
153
`+
assertThat(row.getLong(0)).isEqualTo(2468);
`
``
154
`+
scope.close();
`
``
155
`+
span.end();
`
``
156
`+
assertTrace(span.getSpanContext().getTraceId());
`
``
157
`+
}
`
``
158
`+
}
`