Skip to content

Commit 3e9c528

Browse files
runningcodeclaude
andcommitted
perf(core): Drop per-instance lock from SentryId and SpanId (JAVA-589)
SentryId and SpanId stored their string value behind a LazyEvaluator<String>, which allocates an AutoClosableReentrantLock (a ReentrantLock with its internal Sync) plus a capturing lambda on every instance. Since one SentryId is created per event/transaction and one SpanId per span, this per-instance lock machinery is far heavier than the single String it guards, and the eager string-arg constructors gained no laziness at all. Replace the LazyEvaluator with a plain volatile String guarded by a double-checked synchronized(this) block. Eager constructors now assign the value directly; the no-arg and UUID constructors still defer UUID-string generation. Synchronization is retained because UUID generation is non-idempotent and two racing threads must not produce different ids. Follow-up to the SDK Overhead Reduction work (#5499). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 307edcd commit 3e9c528

2 files changed

Lines changed: 45 additions & 25 deletions

File tree

sentry/src/main/java/io/sentry/SpanId.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,61 @@
22

33
import static io.sentry.util.StringUtils.PROPER_NIL_UUID;
44

5-
import io.sentry.util.LazyEvaluator;
65
import java.io.IOException;
76
import java.util.Objects;
87
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
99

1010
public final class SpanId implements JsonSerializable {
1111
public static final SpanId EMPTY_ID =
1212
new SpanId(PROPER_NIL_UUID.replace("-", "").substring(0, 16));
1313

14-
private final @NotNull LazyEvaluator<String> lazyValue;
14+
private volatile @Nullable String value;
1515

1616
public SpanId(final @NotNull String value) {
17-
Objects.requireNonNull(value, "value is required");
18-
this.lazyValue = new LazyEvaluator<>(() -> value);
17+
this.value = Objects.requireNonNull(value, "value is required");
1918
}
2019

21-
public SpanId() {
22-
this.lazyValue = new LazyEvaluator<>(SentryUUID::generateSpanId);
20+
public SpanId() {}
21+
22+
private @NotNull String getValue() {
23+
String result = value;
24+
if (result == null) {
25+
synchronized (this) {
26+
result = value;
27+
if (result == null) {
28+
result = SentryUUID.generateSpanId();
29+
value = result;
30+
}
31+
}
32+
}
33+
return result;
2334
}
2435

2536
@Override
2637
public boolean equals(Object o) {
2738
if (this == o) return true;
2839
if (o == null || getClass() != o.getClass()) return false;
2940
SpanId spanId = (SpanId) o;
30-
return lazyValue.getValue().equals(spanId.lazyValue.getValue());
41+
return getValue().equals(spanId.getValue());
3142
}
3243

3344
@Override
3445
public int hashCode() {
35-
return lazyValue.getValue().hashCode();
46+
return getValue().hashCode();
3647
}
3748

3849
@Override
3950
public String toString() {
40-
return lazyValue.getValue();
51+
return getValue();
4152
}
4253

4354
// JsonElementSerializer
4455

4556
@Override
4657
public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger)
4758
throws IOException {
48-
writer.value(lazyValue.getValue());
59+
writer.value(getValue());
4960
}
5061

5162
// JsonElementDeserializer

sentry/src/main/java/io/sentry/protocol/SentryId.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import io.sentry.ObjectReader;
77
import io.sentry.ObjectWriter;
88
import io.sentry.SentryUUID;
9-
import io.sentry.util.LazyEvaluator;
109
import io.sentry.util.StringUtils;
1110
import io.sentry.util.UUIDStringUtils;
1211
import java.io.IOException;
@@ -19,19 +18,15 @@ public final class SentryId implements JsonSerializable {
1918
public static final SentryId EMPTY_ID =
2019
new SentryId(StringUtils.PROPER_NIL_UUID.replace("-", ""));
2120

22-
private final @NotNull LazyEvaluator<String> lazyStringValue;
21+
private volatile @Nullable String value;
22+
private final @Nullable UUID uuid;
2323

2424
public SentryId() {
2525
this((UUID) null);
2626
}
2727

2828
public SentryId(@Nullable UUID uuid) {
29-
if (uuid != null) {
30-
this.lazyStringValue =
31-
new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid)));
32-
} else {
33-
this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId);
34-
}
29+
this.uuid = uuid;
3530
}
3631

3732
public SentryId(final @NotNull String sentryIdString) {
@@ -42,29 +37,43 @@ public SentryId(final @NotNull String sentryIdString) {
4237
+ "or 36 characters long (completed UUID). Received: "
4338
+ sentryIdString);
4439
}
45-
if (normalized.length() == 36) {
46-
this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized));
47-
} else {
48-
this.lazyStringValue = new LazyEvaluator<>(() -> normalized);
40+
this.uuid = null;
41+
this.value = normalized.length() == 36 ? normalized.replace("-", "") : normalized;
42+
}
43+
44+
private @NotNull String getValue() {
45+
String result = value;
46+
if (result == null) {
47+
synchronized (this) {
48+
result = value;
49+
if (result == null) {
50+
result =
51+
uuid != null
52+
? normalize(UUIDStringUtils.toSentryIdString(uuid))
53+
: SentryUUID.generateSentryId();
54+
value = result;
55+
}
56+
}
4957
}
58+
return result;
5059
}
5160

5261
@Override
5362
public String toString() {
54-
return lazyStringValue.getValue();
63+
return getValue();
5564
}
5665

5766
@Override
5867
public boolean equals(final @Nullable Object o) {
5968
if (this == o) return true;
6069
if (o == null || getClass() != o.getClass()) return false;
6170
SentryId sentryId = (SentryId) o;
62-
return lazyStringValue.getValue().equals(sentryId.lazyStringValue.getValue());
71+
return getValue().equals(sentryId.getValue());
6372
}
6473

6574
@Override
6675
public int hashCode() {
67-
return lazyStringValue.getValue().hashCode();
76+
return getValue().hashCode();
6877
}
6978

7079
private @NotNull String normalize(@NotNull String uuidString) {

0 commit comments

Comments
 (0)