Skip to content

Commit e4b4af0

Browse files
authored
fix: Create Span hierarchy using parent Span (#1580)
* fix: Replace use of TraceUtil.SpanContext w/ TraceUtil.Context * fix: Fixing how span hierarchy is created across threads - using Span instead of Context * fix: cleaning up startSpan(spanName, parentContext) variant * fix: add TracedReadWriteTransactionCallable to bifurcate tracing enabled/disabled paths for the Transaction callback. - This change implements the idiomatic way to express nested spans as described in https://opentelemetry.io/docs/languages/java/instrumentation/#create-nested-spans * fix: cleanup * fix: cleanup * fix: cleanup
1 parent 7b0b45c commit e4b4af0

File tree

7 files changed

+109
-125
lines changed

7 files changed

+109
-125
lines changed

google-cloud-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java

Lines changed: 88 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818

1919
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DEFERRED;
2020
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DOCUMENT_COUNT;
21-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_EXCEPTION_MESSAGE;
22-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_EXCEPTION_STACKTRACE;
23-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_EXCEPTION_TYPE;
2421
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MISSING;
2522
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MORE_RESULTS;
2623
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_READ_CONSISTENCY;
@@ -37,10 +34,10 @@
3734
import com.google.cloud.ServiceOptions;
3835
import com.google.cloud.datastore.execution.AggregationQueryExecutor;
3936
import com.google.cloud.datastore.spi.v1.DatastoreRpc;
37+
import com.google.cloud.datastore.telemetry.TraceUtil;
4038
import com.google.cloud.datastore.telemetry.TraceUtil.Scope;
4139
import com.google.common.base.MoreObjects;
4240
import com.google.common.base.Preconditions;
43-
import com.google.common.base.Throwables;
4441
import com.google.common.collect.AbstractIterator;
4542
import com.google.common.collect.ImmutableList;
4643
import com.google.common.collect.ImmutableMap;
@@ -53,10 +50,6 @@
5350
import com.google.datastore.v1.RunQueryResponse;
5451
import com.google.datastore.v1.TransactionOptions;
5552
import com.google.protobuf.ByteString;
56-
import io.opentelemetry.api.common.Attributes;
57-
import io.opentelemetry.api.trace.SpanBuilder;
58-
import io.opentelemetry.api.trace.SpanKind;
59-
import io.opentelemetry.api.trace.StatusCode;
6053
import io.opentelemetry.context.Context;
6154
import java.util.ArrayList;
6255
import java.util.Arrays;
@@ -115,25 +108,24 @@ public Transaction newTransaction() {
115108
return new TransactionImpl(this);
116109
}
117110

118-
static class ReadWriteTransactionCallable<T> implements Callable<T> {
119-
111+
static class TracedReadWriteTransactionCallable<T> implements Callable<T> {
120112
private final Datastore datastore;
121113
private final TransactionCallable<T> callable;
122114
private volatile TransactionOptions options;
123115
private volatile Transaction transaction;
124116

125-
private final com.google.cloud.datastore.telemetry.TraceUtil.SpanContext parentSpanContext;
117+
private final TraceUtil.Span parentSpan;
126118

127-
ReadWriteTransactionCallable(
119+
TracedReadWriteTransactionCallable(
128120
Datastore datastore,
129121
TransactionCallable<T> callable,
130122
TransactionOptions options,
131-
@Nullable com.google.cloud.datastore.telemetry.TraceUtil.SpanContext parentSpanContext) {
123+
@Nullable com.google.cloud.datastore.telemetry.TraceUtil.Span parentSpan) {
132124
this.datastore = datastore;
133125
this.callable = callable;
134126
this.options = options;
135127
this.transaction = null;
136-
this.parentSpanContext = parentSpanContext;
128+
this.parentSpan = parentSpan;
137129
}
138130

139131
Datastore getDatastore() {
@@ -154,57 +146,75 @@ void setPrevTransactionId(ByteString transactionId) {
154146
options = options.toBuilder().setReadWrite(readWrite).build();
155147
}
156148

157-
private io.opentelemetry.api.trace.Span startSpanWithParentContext(
158-
String spanName,
159-
com.google.cloud.datastore.telemetry.TraceUtil.SpanContext parentSpanContext) {
160-
com.google.cloud.datastore.telemetry.TraceUtil otelTraceUtil =
161-
datastore.getOptions().getTraceUtil();
162-
SpanBuilder spanBuilder =
163-
otelTraceUtil
164-
.getTracer()
165-
.spanBuilder(com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN)
166-
.setSpanKind(SpanKind.PRODUCER)
167-
.setParent(
168-
Context.current()
169-
.with(
170-
io.opentelemetry.api.trace.Span.wrap(
171-
parentSpanContext.getSpanContext())));
172-
return otelTraceUtil.addSettingsAttributesToCurrentSpan(spanBuilder).startSpan();
149+
@Override
150+
public T call() throws DatastoreException {
151+
try (io.opentelemetry.context.Scope ignored =
152+
Context.current().with(parentSpan.getSpan()).makeCurrent()) {
153+
transaction = datastore.newTransaction(options);
154+
T value = callable.run(transaction);
155+
transaction.commit();
156+
return value;
157+
} catch (Exception ex) {
158+
transaction.rollback();
159+
throw DatastoreException.propagateUserException(ex);
160+
} finally {
161+
if (transaction.isActive()) {
162+
transaction.rollback();
163+
}
164+
if (options != null
165+
&& options.getModeCase().equals(TransactionOptions.ModeCase.READ_WRITE)) {
166+
setPrevTransactionId(transaction.getTransactionId());
167+
}
168+
}
169+
}
170+
}
171+
172+
static class ReadWriteTransactionCallable<T> implements Callable<T> {
173+
private final Datastore datastore;
174+
private final TransactionCallable<T> callable;
175+
private volatile TransactionOptions options;
176+
private volatile Transaction transaction;
177+
178+
ReadWriteTransactionCallable(
179+
Datastore datastore, TransactionCallable<T> callable, TransactionOptions options) {
180+
this.datastore = datastore;
181+
this.callable = callable;
182+
this.options = options;
183+
this.transaction = null;
184+
}
185+
186+
Datastore getDatastore() {
187+
return datastore;
188+
}
189+
190+
TransactionOptions getOptions() {
191+
return options;
192+
}
193+
194+
Transaction getTransaction() {
195+
return transaction;
196+
}
197+
198+
void setPrevTransactionId(ByteString transactionId) {
199+
TransactionOptions.ReadWrite readWrite =
200+
TransactionOptions.ReadWrite.newBuilder().setPreviousTransaction(transactionId).build();
201+
options = options.toBuilder().setReadWrite(readWrite).build();
173202
}
174203

175204
@Override
176205
public T call() throws DatastoreException {
177-
// TODO Instead of using OTel Spans directly, TraceUtil.Span should be used here. However,
178-
// the same code in startSpanInternal doesn't work when EnabledTraceUtil.StartSpan is called
179-
// probably because of some thread-local caching that is getting lost. This needs more
180-
// debugging. The code below works and is idiomatic but could be prettier and more consistent
181-
// with the use of TraceUtil-provided framework.
182-
io.opentelemetry.api.trace.Span span =
183-
startSpanWithParentContext(
184-
com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN,
185-
parentSpanContext);
186-
try (io.opentelemetry.context.Scope ignored = span.makeCurrent()) {
206+
try {
187207
transaction = datastore.newTransaction(options);
188208
T value = callable.run(transaction);
189209
transaction.commit();
190210
return value;
191211
} catch (Exception ex) {
192212
transaction.rollback();
193-
span.setStatus(StatusCode.ERROR, ex.getMessage());
194-
span.recordException(
195-
ex,
196-
Attributes.builder()
197-
.put(ATTRIBUTES_KEY_EXCEPTION_MESSAGE, ex.getMessage())
198-
.put(ATTRIBUTES_KEY_EXCEPTION_TYPE, ex.getClass().getName())
199-
.put(ATTRIBUTES_KEY_EXCEPTION_STACKTRACE, Throwables.getStackTraceAsString(ex))
200-
.build());
201-
span.end();
202213
throw DatastoreException.propagateUserException(ex);
203214
} finally {
204215
if (transaction.isActive()) {
205216
transaction.rollback();
206217
}
207-
span.end();
208218
if (options != null
209219
&& options.getModeCase().equals(TransactionOptions.ModeCase.READ_WRITE)) {
210220
setPrevTransactionId(transaction.getTransactionId());
@@ -215,30 +225,51 @@ public T call() throws DatastoreException {
215225

216226
@Override
217227
public <T> T runInTransaction(final TransactionCallable<T> callable) {
218-
try {
228+
TraceUtil.Span span =
229+
otelTraceUtil.startSpan(
230+
com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN);
231+
Callable<T> transactionCallable =
232+
(getOptions().getOpenTelemetryOptions().isEnabled()
233+
? new TracedReadWriteTransactionCallable<T>(
234+
this, callable, /*transactionOptions=*/ null, span)
235+
: new ReadWriteTransactionCallable<T>(this, callable, /*transactionOptions=*/ null));
236+
try (Scope ignored = span.makeCurrent()) {
219237
return RetryHelper.runWithRetries(
220-
new ReadWriteTransactionCallable<T>(
221-
this, callable, null, otelTraceUtil.getCurrentSpanContext()),
238+
transactionCallable,
222239
retrySettings,
223240
TRANSACTION_EXCEPTION_HANDLER,
224241
getOptions().getClock());
225242
} catch (RetryHelperException e) {
243+
span.end(e);
226244
throw DatastoreException.translateAndThrow(e);
245+
} finally {
246+
span.end();
227247
}
228248
}
229249

230250
@Override
231251
public <T> T runInTransaction(
232252
final TransactionCallable<T> callable, TransactionOptions transactionOptions) {
233-
try {
253+
TraceUtil.Span span =
254+
otelTraceUtil.startSpan(
255+
com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN);
256+
257+
Callable<T> transactionCallable =
258+
(getOptions().getOpenTelemetryOptions().isEnabled()
259+
? new TracedReadWriteTransactionCallable<T>(this, callable, transactionOptions, span)
260+
: new ReadWriteTransactionCallable<T>(this, callable, transactionOptions));
261+
262+
try (Scope ignored = span.makeCurrent()) {
234263
return RetryHelper.runWithRetries(
235-
new ReadWriteTransactionCallable<T>(
236-
this, callable, transactionOptions, otelTraceUtil.getCurrentSpanContext()),
264+
transactionCallable,
237265
retrySettings,
238266
TRANSACTION_EXCEPTION_HANDLER,
239267
getOptions().getClock());
240268
} catch (RetryHelperException e) {
269+
span.end(e);
241270
throw DatastoreException.translateAndThrow(e);
271+
} finally {
272+
span.end();
242273
}
243274
}
244275

@@ -747,8 +778,7 @@ com.google.datastore.v1.BeginTransactionResponse beginTransaction(
747778
final com.google.datastore.v1.BeginTransactionRequest requestPb) {
748779
com.google.cloud.datastore.telemetry.TraceUtil.Span span =
749780
otelTraceUtil.startSpan(
750-
com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_BEGIN_TRANSACTION,
751-
otelTraceUtil.getCurrentSpanContext());
781+
com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_BEGIN_TRANSACTION);
752782
try (com.google.cloud.datastore.telemetry.TraceUtil.Scope scope = span.makeCurrent()) {
753783
return RetryHelper.runWithRetries(
754784
() -> datastoreRpc.beginTransaction(requestPb),

google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/DisabledTraceUtil.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import com.google.api.core.ApiFunction;
2020
import com.google.api.core.ApiFuture;
2121
import com.google.api.core.InternalApi;
22-
import com.google.cloud.datastore.telemetry.TraceUtil.SpanContext;
22+
import com.google.cloud.datastore.telemetry.TraceUtil.Context;
2323
import io.grpc.ManagedChannelBuilder;
2424
import io.opentelemetry.api.trace.Span;
2525
import io.opentelemetry.api.trace.SpanBuilder;
@@ -35,14 +35,6 @@
3535
*/
3636
@InternalApi
3737
public class DisabledTraceUtil implements TraceUtil {
38-
39-
static class SpanContext implements TraceUtil.SpanContext {
40-
@Override
41-
public io.opentelemetry.api.trace.SpanContext getSpanContext() {
42-
return null;
43-
}
44-
}
45-
4638
static class Span implements TraceUtil.Span {
4739
@Override
4840
public void end() {}
@@ -112,7 +104,7 @@ public Span startSpan(String spanName) {
112104
}
113105

114106
@Override
115-
public TraceUtil.Span startSpan(String spanName, TraceUtil.SpanContext parentSpanContext) {
107+
public TraceUtil.Span startSpan(String spanName, TraceUtil.Span parentSpan) {
116108
return new Span();
117109
}
118110

@@ -132,12 +124,6 @@ public TraceUtil.Context getCurrentContext() {
132124
return new Context();
133125
}
134126

135-
@Nonnull
136-
@Override
137-
public TraceUtil.SpanContext getCurrentSpanContext() {
138-
return new SpanContext();
139-
}
140-
141127
@Override
142128
public Tracer getTracer() {
143129
return TracerProvider.noop().get(LIBRARY_NAME);

google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/EnabledTraceUtil.java

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@
2222
import com.google.api.core.ApiFutures;
2323
import com.google.api.core.InternalApi;
2424
import com.google.cloud.datastore.DatastoreOptions;
25-
import com.google.cloud.datastore.telemetry.TraceUtil.SpanContext;
25+
import com.google.cloud.datastore.telemetry.TraceUtil.Context;
26+
import com.google.cloud.datastore.telemetry.TraceUtil.Scope;
27+
import com.google.cloud.datastore.telemetry.TraceUtil.Span;
2628
import com.google.common.base.Throwables;
2729
import io.grpc.ManagedChannelBuilder;
2830
import io.opentelemetry.api.GlobalOpenTelemetry;
2931
import io.opentelemetry.api.OpenTelemetry;
3032
import io.opentelemetry.api.common.Attributes;
3133
import io.opentelemetry.api.common.AttributesBuilder;
32-
import io.opentelemetry.api.trace.Span;
3334
import io.opentelemetry.api.trace.SpanBuilder;
3435
import io.opentelemetry.api.trace.SpanKind;
3536
import io.opentelemetry.api.trace.StatusCode;
3637
import io.opentelemetry.api.trace.Tracer;
37-
import io.opentelemetry.context.Context;
3838
import java.util.Map;
3939
import javax.annotation.Nonnull;
4040
import javax.annotation.Nullable;
@@ -73,19 +73,6 @@ public ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> getChannelConfi
7373
return null;
7474
}
7575

76-
static class SpanContext implements TraceUtil.SpanContext {
77-
private final io.opentelemetry.api.trace.SpanContext spanContext;
78-
79-
public SpanContext(io.opentelemetry.api.trace.SpanContext spanContext) {
80-
this.spanContext = spanContext;
81-
}
82-
83-
@Override
84-
public io.opentelemetry.api.trace.SpanContext getSpanContext() {
85-
return this.spanContext;
86-
}
87-
}
88-
8976
static class Span implements TraceUtil.Span {
9077
private final io.opentelemetry.api.trace.Span span;
9178
private final String spanName;
@@ -95,6 +82,11 @@ public Span(io.opentelemetry.api.trace.Span span, String spanName) {
9582
this.spanName = spanName;
9683
}
9784

85+
@Override
86+
public io.opentelemetry.api.trace.Span getSpan() {
87+
return this.span;
88+
}
89+
9890
/** Ends this span. */
9991
@Override
10092
public void end() {
@@ -197,10 +189,6 @@ public TraceUtil.Span setAttribute(String key, boolean value) {
197189
return this;
198190
}
199191

200-
public io.opentelemetry.api.trace.Span getSpan() {
201-
return this.span;
202-
}
203-
204192
@Override
205193
public Scope makeCurrent() {
206194
try (io.opentelemetry.context.Scope scope = span.makeCurrent()) {
@@ -307,18 +295,13 @@ public Span startSpan(String spanName) {
307295
}
308296

309297
@Override
310-
public TraceUtil.Span startSpan(String spanName, TraceUtil.SpanContext parentSpanContext) {
298+
public TraceUtil.Span startSpan(String spanName, TraceUtil.Span parentSpan) {
311299
SpanBuilder spanBuilder =
312300
tracer
313301
.spanBuilder(spanName)
314302
.setSpanKind(SpanKind.PRODUCER)
315-
.setParent(
316-
io.opentelemetry.context.Context.current()
317-
.with(
318-
io.opentelemetry.api.trace.Span.wrap(parentSpanContext.getSpanContext())));
319-
io.opentelemetry.api.trace.Span span =
320-
addSettingsAttributesToCurrentSpan(spanBuilder).startSpan();
321-
return new Span(span, spanName);
303+
.setParent(io.opentelemetry.context.Context.current().with(parentSpan.getSpan()));
304+
return new Span(addSettingsAttributesToCurrentSpan(spanBuilder).startSpan(), spanName);
322305
}
323306

324307
@Nonnull
@@ -333,12 +316,6 @@ public TraceUtil.Context getCurrentContext() {
333316
return new Context(io.opentelemetry.context.Context.current());
334317
}
335318

336-
@Nonnull
337-
@Override
338-
public TraceUtil.SpanContext getCurrentSpanContext() {
339-
return new SpanContext(io.opentelemetry.api.trace.Span.current().getSpanContext());
340-
}
341-
342319
@Override
343320
public Tracer getTracer() {
344321
return this.tracer;

0 commit comments

Comments
 (0)