Skip to content

Commit 5e13d9b

Browse files
committed
Add otel spans for load, save, delete, and query
1 parent c96a82d commit 5e13d9b

8 files changed

Lines changed: 264 additions & 152 deletions

File tree

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,13 @@
273273
<version>${slf4j.version}</version>
274274
<scope>test</scope>
275275
</dependency>
276+
277+
<dependency>
278+
<groupId>io.opentelemetry</groupId>
279+
<artifactId>opentelemetry-exporter-jaeger</artifactId>
280+
<version>1.34.1</version>
281+
<scope>test</scope>
282+
</dependency>
276283
</dependencies>
277284

278285
</project>

src/main/java/com/googlecode/objectify/ObjectifyFactory.java

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@
2424
import com.googlecode.objectify.impl.translate.Translators;
2525
import com.googlecode.objectify.util.Closeable;
2626
import io.opentelemetry.api.OpenTelemetry;
27+
import io.opentelemetry.api.trace.Span;
28+
import io.opentelemetry.api.trace.SpanKind;
29+
import io.opentelemetry.api.trace.Tracer;
30+
import io.opentelemetry.context.Scope;
2731
import net.spy.memcached.MemcachedClient;
2832

33+
import javax.annotation.Nullable;
2934
import java.lang.reflect.Constructor;
3035
import java.util.ArrayDeque;
3136
import java.util.ArrayList;
@@ -41,6 +46,7 @@
4146
import java.util.SortedSet;
4247
import java.util.TreeMap;
4348
import java.util.TreeSet;
49+
import java.util.function.Supplier;
4450
import java.util.stream.Collectors;
4551

4652
/**
@@ -59,8 +65,11 @@
5965
*
6066
* @author Jeff Schnitzer <jeff@infohazard.org>
6167
*/
62-
public class ObjectifyFactory implements Forge
63-
{
68+
public class ObjectifyFactory implements Forge {
69+
70+
/** For OpenTelemetry */
71+
private static final String TRACER_NAME = "Objectify";
72+
6473
/** Default memcache namespace */
6574
public static final String MEMCACHE_NAMESPACE = "ObjectifyCache";
6675

@@ -70,25 +79,29 @@ public class ObjectifyFactory implements Forge
7079
private final ThreadLocal<Deque<Objectify>> stacks = ThreadLocal.withInitial(ArrayDeque::new);
7180

7281
/** The raw interface to the datastore from the Cloud SDK */
73-
protected Datastore datastore;
82+
protected final Datastore datastore;
7483

7584
/** The low-level interface to memcache */
76-
protected MemcacheService memcache;
85+
protected final MemcacheService memcache;
7786

7887
/** Encapsulates entity registration info */
79-
protected Registrar registrar;
88+
protected final Registrar registrar;
8089

8190
/** Some useful tools for working with keys */
82-
protected Keys keys;
91+
protected final Keys keys;
8392

8493
/** */
85-
protected Translators translators;
94+
protected final Translators translators;
8695

8796
/** */
88-
protected EntityMemcacheStats memcacheStats = new EntityMemcacheStats();
97+
protected final EntityMemcacheStats memcacheStats = new EntityMemcacheStats();
8998

9099
/** Manages caching of entities; might be null to indicate "no cache" */
91-
protected EntityMemcache entityMemcache;
100+
protected final EntityMemcache entityMemcache;
101+
102+
/** This will be null if opentelemetry is not configured */
103+
@Nullable
104+
protected final Tracer tracer;
92105

93106
/** Uses default datastore, no memcache */
94107
public ObjectifyFactory() {
@@ -143,6 +156,9 @@ public ObjectifyFactory(final Datastore datastore, final MemcacheService memcach
143156
this.memcache = memcache;
144157

145158
this.entityMemcache = memcache == null ? null : new EntityMemcache(memcache, MEMCACHE_NAMESPACE, new CacheControlImpl(this), this.memcacheStats);
159+
160+
final OpenTelemetry openTelemetry = datastore.getOptions().getOpenTelemetryOptions().getOpenTelemetry();
161+
this.tracer = openTelemetry == null ? null : openTelemetry.getTracer(TRACER_NAME);
146162
}
147163

148164
/** */
@@ -538,4 +554,20 @@ public <T> Ref<T> ref(final Key<T> key) {
538554
public <T> Ref<T> ref(final T value) {
539555
return ref(key(value));
540556
}
557+
558+
/**
559+
* For internal use, hides the optionality of otel.
560+
*/
561+
public <T> T span(final String name, final Supplier<T> work) {
562+
if (tracer == null) {
563+
return work.get();
564+
} else {
565+
final Span span = tracer.spanBuilder(name).setSpanKind(SpanKind.CLIENT).startSpan();
566+
try (final Scope scope = span.makeCurrent()) {
567+
return work.get();
568+
} finally {
569+
span.end();
570+
}
571+
}
572+
}
541573
}

src/main/java/com/googlecode/objectify/impl/DeleterImpl.java

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,75 +15,66 @@
1515
*
1616
* @author Jeff Schnitzer <jeff@infohazard.org>
1717
*/
18-
public class DeleterImpl implements Deleter
19-
{
20-
/** */
18+
public class DeleterImpl implements Deleter {
19+
2120
final ObjectifyImpl ofy;
2221

23-
/** */
2422
public DeleterImpl(final ObjectifyImpl ofy) {
2523
this.ofy = ofy;
2624
}
2725

28-
/* (non-Javadoc)
29-
* @see com.googlecode.objectify.cmd.Delete#type(java.lang.Class)
30-
*/
3126
@Override
3227
public DeleteType type(final Class<?> type) {
3328
return new DeleteTypeImpl(this, type);
3429
}
3530

36-
/* (non-Javadoc)
37-
* @see com.googlecode.objectify.cmd.Deleter#key(com.googlecode.objectify.Key)
38-
*/
3931
@Override
4032
public Result<Void> key(final Key<?> key) {
4133
return this.keys(key);
4234
}
4335

44-
/* (non-Javadoc)
45-
* @see com.googlecode.objectify.cmd.Deleter#keys(com.googlecode.objectify.Key<?>[])
46-
*/
4736
@Override
4837
public Result<Void> keys(final Key<?>... keys) {
4938
return this.keys(Arrays.asList(keys));
5039
}
5140

52-
/* (non-Javadoc)
53-
* @see com.googlecode.objectify.cmd.Delete#values(java.lang.Iterable)
54-
*/
5541
@Override
5642
public Result<Void> keys(final Iterable<? extends Key<?>> keys) {
57-
final List<com.google.cloud.datastore.Key> rawKeys = new ArrayList<>();
58-
for (final Key<?> key: keys)
59-
rawKeys.add(key.getRaw());
43+
return ofy.factory().span("delete", () -> {
44+
final List<com.google.cloud.datastore.Key> rawKeys = new ArrayList<>();
45+
for (final Key<?> key: keys)
46+
rawKeys.add(key.getRaw());
47+
48+
final Result<Void> result = ofy.createWriteEngine().delete(rawKeys);
49+
50+
// The low level API is not async, so let's ensure work is finished in the span.
51+
result.now();
6052

61-
return ofy.createWriteEngine().delete(rawKeys);
53+
return result;
54+
});
6255
}
6356

64-
/* (non-Javadoc)
65-
* @see com.googlecode.objectify.cmd.Deleter#entity(java.lang.Object)
66-
*/
6757
@Override
6858
public Result<Void> entity(final Object entity) {
6959
return this.entities(entity);
7060
}
7161

72-
/* (non-Javadoc)
73-
* @see com.googlecode.objectify.cmd.Deleter#entities(java.lang.Iterable)
74-
*/
7562
@Override
7663
public Result<Void> entities(final Iterable<?> entities) {
77-
final List<com.google.cloud.datastore.Key> keys = new ArrayList<>();
78-
for (final Object obj: entities)
79-
keys.add(ofy.factory().keys().anythingToRawKey(obj, ofy.getOptions().getNamespace()));
64+
return ofy.factory().span("delete", () -> {
65+
final List<com.google.cloud.datastore.Key> keys = new ArrayList<>();
66+
for (final Object obj : entities)
67+
keys.add(ofy.factory().keys().anythingToRawKey(obj, ofy.getOptions().getNamespace()));
68+
69+
final Result<Void> result = ofy.createWriteEngine().delete(keys);
70+
71+
// The low level API is not async, so let's ensure work is finished in the span.
72+
result.now();
8073

81-
return ofy.createWriteEngine().delete(keys);
74+
return result;
75+
});
8276
}
8377

84-
/* (non-Javadoc)
85-
* @see com.googlecode.objectify.cmd.Deleter#entities(java.lang.Object[])
86-
*/
8778
@Override
8879
public Result<Void> entities(final Object... entities) {
8980
return this.entities(Arrays.asList(entities));

0 commit comments

Comments
 (0)