Skip to content

Commit df54343

Browse files
committed
feat(spanner): Add x-goog-spanner-end-to-end-tracing header for requests to SpanFE
1 parent 34edbd9 commit df54343

File tree

6 files changed

+142
-0
lines changed

6 files changed

+142
-0
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
150150
private final boolean useVirtualThreads;
151151
private final OpenTelemetry openTelemetry;
152152
private final boolean enableExtendedTracing;
153+
private final boolean enableEndToEndTracing;
153154

154155
enum TracingFramework {
155156
OPEN_CENSUS,
@@ -655,6 +656,7 @@ protected SpannerOptions(Builder builder) {
655656
useVirtualThreads = builder.useVirtualThreads;
656657
openTelemetry = builder.openTelemetry;
657658
enableExtendedTracing = builder.enableExtendedTracing;
659+
enableEndToEndTracing = builder.enableEndToEndTracing;
658660
}
659661

660662
/**
@@ -779,6 +781,7 @@ public static class Builder
779781
private boolean useVirtualThreads = false;
780782
private OpenTelemetry openTelemetry;
781783
private boolean enableExtendedTracing = SpannerOptions.environment.isEnableExtendedTracing();
784+
private boolean enableEndToEndTracing = false;
782785

783786
private static String createCustomClientLibToken(String token) {
784787
return token + " " + ServiceOptions.getGoogApiClientLibName();
@@ -843,6 +846,7 @@ protected Builder() {
843846
this.directedReadOptions = options.directedReadOptions;
844847
this.useVirtualThreads = options.useVirtualThreads;
845848
this.enableExtendedTracing = options.enableExtendedTracing;
849+
this.enableEndToEndTracing = options.enableEndToEndTracing;
846850
}
847851

848852
@Override
@@ -1352,6 +1356,15 @@ public Builder setEnableExtendedTracing(boolean enableExtendedTracing) {
13521356
return this;
13531357
}
13541358

1359+
/**
1360+
* Sets whether to enable end to end tracing. Enabling this option will create the
1361+
* trace spans at the Spanner layer.
1362+
*/
1363+
public Builder setEnableEndToEndTracing(boolean enableEndToEndTracing) {
1364+
this.enableEndToEndTracing = enableEndToEndTracing;
1365+
return this;
1366+
}
1367+
13551368
@SuppressWarnings("rawtypes")
13561369
@Override
13571370
public SpannerOptions build() {
@@ -1606,6 +1619,14 @@ public boolean isEnableExtendedTracing() {
16061619
return enableExtendedTracing;
16071620
}
16081621

1622+
/**
1623+
* Returns whether end to end tracing is enabled. If this option is enabled then trace spans
1624+
* will be created at the Spanner layer.
1625+
*/
1626+
public boolean isEndToEndTracingEnabled() {
1627+
return enableEndToEndTracing;
1628+
}
1629+
16091630
/** Returns the default query options to use for the specific database. */
16101631
public QueryOptions getDefaultQueryOptions(DatabaseId databaseId) {
16111632
// Use the specific query options for the database if any have been specified. These have

google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ public class GapicSpannerRpc implements SpannerRpc {
266266
private static final ConcurrentMap<String, RateLimiter> ADMINISTRATIVE_REQUESTS_RATE_LIMITERS =
267267
new ConcurrentHashMap<>();
268268
private final boolean leaderAwareRoutingEnabled;
269+
private final boolean endToEndTracingEnabled;
269270

270271
public static GapicSpannerRpc create(SpannerOptions options) {
271272
return new GapicSpannerRpc(options);
@@ -317,6 +318,7 @@ public GapicSpannerRpc(final SpannerOptions options) {
317318
this.callCredentialsProvider = options.getCallCredentialsProvider();
318319
this.compressorName = options.getCompressorName();
319320
this.leaderAwareRoutingEnabled = options.isLeaderAwareRoutingEnabled();
321+
this.endToEndTracingEnabled = options.isEndToEndTracingEnabled();
320322

321323
if (initializeStubs) {
322324
// First check if SpannerOptions provides a TransportChannelProvider. Create one
@@ -1956,6 +1958,9 @@ <ReqT, RespT> GrpcCallContext newCallContext(
19561958
if (routeToLeader && leaderAwareRoutingEnabled) {
19571959
context = context.withExtraHeaders(metadataProvider.newRouteToLeaderHeader());
19581960
}
1961+
if (endToEndTracingEnabled) {
1962+
context = context.withExtraHeaders(metadataProvider.newEndToEndTracingHeader());
1963+
}
19591964
if (callCredentialsProvider != null) {
19601965
CallCredentials callCredentials = callCredentialsProvider.getCallCredentials();
19611966
if (callCredentials != null) {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerMetadataProvider.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,16 @@ class SpannerMetadataProvider {
3737
private final Map<Metadata.Key<String>, String> headers;
3838
private final String resourceHeaderKey;
3939
private static final String ROUTE_TO_LEADER_HEADER_KEY = "x-goog-spanner-route-to-leader";
40+
private static final String END_TO_END_TRACING_HEADER_KEY = "x-goog-spanner-end-to-end-tracing";
4041
private static final Pattern[] RESOURCE_TOKEN_PATTERNS = {
4142
Pattern.compile("^(?<headerValue>projects/[^/]*/instances/[^/]*/databases/[^/]*)(.*)?"),
4243
Pattern.compile("^(?<headerValue>projects/[^/]*/instances/[^/]*)(.*)?")
4344
};
4445

4546
private static final Map<String, List<String>> ROUTE_TO_LEADER_HEADER_MAP =
4647
ImmutableMap.of(ROUTE_TO_LEADER_HEADER_KEY, Collections.singletonList("true"));
48+
private static final Map<String, List<String>> END_TO_END_TRACING_HEADER_MAP =
49+
ImmutableMap.of(END_TO_END_TRACING_HEADER_KEY, Collections.singletonList("true"));
4750

4851
private SpannerMetadataProvider(Map<String, String> headers, String resourceHeaderKey) {
4952
this.resourceHeaderKey = resourceHeaderKey;
@@ -89,6 +92,10 @@ Map<String, List<String>> newRouteToLeaderHeader() {
8992
return ROUTE_TO_LEADER_HEADER_MAP;
9093
}
9194

95+
Map<String, List<String>> newEndToEndTracingHeader() {
96+
return END_TO_END_TRACING_HEADER_MAP;
97+
}
98+
9299
private Map<Metadata.Key<String>, String> constructHeadersAsMetadata(
93100
Map<String, String> headers) {
94101
ImmutableMap.Builder<Metadata.Key<String>, String> headersAsMetadataBuilder =

google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,24 @@ public void testLeaderAwareRoutingEnablement() {
731731
.isLeaderAwareRoutingEnabled());
732732
}
733733

734+
@Test
735+
public void testEndToEndTracingEnablement() {
736+
// Test that end to end tracing is disabled by default.
737+
assertFalse(SpannerOptions.newBuilder().setProjectId("p").build().isEndToEndTracingEnabled());
738+
assertTrue(
739+
SpannerOptions.newBuilder()
740+
.setProjectId("p")
741+
.setEnableEndToEndTracing(true)
742+
.build()
743+
.isEndToEndTracingEnabled());
744+
assertFalse(
745+
SpannerOptions.newBuilder()
746+
.setProjectId("p")
747+
.setEnableEndToEndTracing(false)
748+
.build()
749+
.isEndToEndTracingEnabled());
750+
}
751+
734752
@Test
735753
public void testSetDirectedReadOptions() {
736754
final DirectedReadOptions directedReadOptions =

google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public class GapicSpannerRpcTest {
148148
private static String defaultUserAgent;
149149
private static Spanner spanner;
150150
private static boolean isRouteToLeader;
151+
private static boolean isEndToEndTracing;
151152

152153
@Parameter public Dialect dialect;
153154

@@ -199,8 +200,15 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
199200
Key.of(
200201
"x-goog-spanner-route-to-leader",
201202
Metadata.ASCII_STRING_MARSHALLER));
203+
String endToEndTracingHeader =
204+
headers.get(
205+
Key.of(
206+
"x-goog-spanner-end-to-end-tracing",
207+
Metadata.ASCII_STRING_MARSHALLER));
202208
isRouteToLeader =
203209
(routeToLeaderHeader != null && routeToLeaderHeader.equals("true"));
210+
isEndToEndTracing =
211+
(endToEndTracingHeader != null && endToEndTracingHeader.equals("true"));
204212
}
205213
return Contexts.interceptCall(Context.current(), call, headers, next);
206214
}
@@ -224,6 +232,7 @@ public void reset() throws InterruptedException {
224232
server.awaitTermination();
225233
}
226234
isRouteToLeader = false;
235+
isEndToEndTracing = false;
227236
}
228237

229238
@Test
@@ -464,6 +473,77 @@ public void testNewCallContextWithRouteToLeaderHeaderAndLarDisabled() {
464473
rpc.shutdown();
465474
}
466475

476+
@Test
477+
public void testNewCallContextWithEndToEndTracingHeader() {
478+
SpannerOptions options =
479+
SpannerOptions.newBuilder().setProjectId("some-project").setEnableEndToEndTracing(true).build();
480+
GapicSpannerRpc rpc = new GapicSpannerRpc(options, false);
481+
GrpcCallContext callContext =
482+
rpc.newCallContext(
483+
optionsMap,
484+
"/some/resource",
485+
ExecuteSqlRequest.getDefaultInstance(),
486+
SpannerGrpc.getExecuteSqlMethod());
487+
assertNotNull(callContext);
488+
assertEquals(
489+
ImmutableList.of("true"),
490+
callContext.getExtraHeaders().get("x-goog-spanner-end-to-end-tracing"));
491+
assertEquals(
492+
ImmutableList.of("projects/some-project"),
493+
callContext.getExtraHeaders().get(ApiClientHeaderProvider.getDefaultResourceHeaderKey()));
494+
rpc.shutdown();
495+
}
496+
497+
@Test
498+
public void testNewCallContextWithoutEndToEndTracingHeader() {
499+
SpannerOptions options =
500+
SpannerOptions.newBuilder().setProjectId("some-project").setEnableEndToEndTracing(false).build();
501+
GapicSpannerRpc rpc = new GapicSpannerRpc(options, false);
502+
GrpcCallContext callContext =
503+
rpc.newCallContext(
504+
optionsMap,
505+
"/some/resource",
506+
ExecuteSqlRequest.getDefaultInstance(),
507+
SpannerGrpc.getExecuteSqlMethod());
508+
assertNotNull(callContext);
509+
assertNull(callContext.getExtraHeaders().get("x-goog-spanner-end-to-end-tracing"));
510+
rpc.shutdown();
511+
}
512+
513+
@Test
514+
public void testEndToEndTracingHeaderWithEnabledTracing() {
515+
final SpannerOptions options =
516+
createSpannerOptions().toBuilder().setEnableEndToEndTracing(true).build();
517+
try (Spanner spanner = options.getService()) {
518+
final DatabaseClient databaseClient =
519+
spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"));
520+
TransactionRunner runner = databaseClient.readWriteTransaction();
521+
runner.run(
522+
transaction -> {
523+
transaction.executeUpdate(UPDATE_FOO_STATEMENT);
524+
return null;
525+
});
526+
}
527+
assertTrue(isEndToEndTracing);
528+
}
529+
530+
@Test
531+
public void testEndToEndTracingHeaderWithDisabledTracing() {
532+
final SpannerOptions options =
533+
createSpannerOptions().toBuilder().setEnableEndToEndTracing(false).build();
534+
try (Spanner spanner = options.getService()) {
535+
final DatabaseClient databaseClient =
536+
spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE]"));
537+
TransactionRunner runner = databaseClient.readWriteTransaction();
538+
runner.run(
539+
transaction -> {
540+
transaction.executeUpdate(UPDATE_FOO_STATEMENT);
541+
return null;
542+
});
543+
}
544+
assertFalse(isEndToEndTracing);
545+
}
546+
467547
@Test
468548
public void testAdminRequestsLimitExceededRetryAlgorithm() {
469549
AdminRequestsLimitExceededRetryAlgorithm<Long> alg =

google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/SpannerMetadataProviderTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ public void testNewRouteToLeaderHeader() {
9494
assertTrue(Maps.difference(extraHeaders, expectedHeaders).areEqual());
9595
}
9696

97+
@Test
98+
public void testNewEndToEndTracingHeader() {
99+
SpannerMetadataProvider metadataProvider =
100+
SpannerMetadataProvider.create(ImmutableMap.of(), "header1");
101+
Map<String, List<String>> extraHeaders = metadataProvider.newEndToEndTracingHeader();
102+
Map<String, List<String>> expectedHeaders =
103+
ImmutableMap.<String, List<String>>of(
104+
"x-goog-spanner-end-to-end-tracing", ImmutableList.of("true"));
105+
assertTrue(Maps.difference(extraHeaders, expectedHeaders).areEqual());
106+
}
107+
97108
private String getResourceHeaderValue(
98109
SpannerMetadataProvider headerProvider, String resourceTokenTemplate) {
99110
Metadata metadata = headerProvider.newMetadata(resourceTokenTemplate, "projects/p");

0 commit comments

Comments
 (0)