Skip to content

Commit 741807a

Browse files
lbloderadinauerstefanosianogetsentry-bot
authored
Faster ID generation (#3818)
* lazy uuid generation for SentryId and SpanId * add changelog * add tests for lazy init, rework SentryId to cache string result * fix changelog * Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano <[email protected]> * Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano <[email protected]> * Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano <[email protected]> * Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano <[email protected]> * Update sentry/src/main/java/io/sentry/protocol/SentryId.java Co-authored-by: Stefano <[email protected]> * Format code * Add object comparison to SpanFrameMetricsCollector only check for time difference of .equals if objects are not the same * add proposed SentryUUID class * [WIP] use SentryUUID instead of UUID.random().toString * Format code * split SentryUUID internals into separate classes for easier attribution * add changelog * fix tests * fix SpanId.EMPTY_ID * test normalized is never called in no-arg constructor, only called once when invoking with string * fix test * Format code * use new Random Generator * fix Sentry Empty ID, replace dashes if 36 char uuid String is passed to SentryId constructor * fix Tests for SpanId, add tests for SentryUUID and UUIDStringUtils --------- Co-authored-by: Alexander Dinauer <[email protected]> Co-authored-by: Stefano <[email protected]> Co-authored-by: Sentry Github Bot <[email protected]>
1 parent e5e4336 commit 741807a

File tree

17 files changed

+377
-59
lines changed

17 files changed

+377
-59
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
- `globalHubMode` used to only be a param on `Sentry.init`. To make it easier to be used in e.g. Desktop environments, we now additionally added it as an option on SentryOptions that can also be set via `sentry.properties`.
1414
- If both the param on `Sentry.init` and the option are set, the option will win. By default the option is set to `null` meaning whatever is passed to `Sentry.init` takes effect.
1515
- Lazy uuid generation for SentryId and SpanId ([#3770](https://github.com/getsentry/sentry-java/pull/3770))
16+
- Faster generation of Sentry and Span IDs ([#3818](https://github.com/getsentry/sentry-java/pull/3818))
17+
- Uses faster implementation to convert UUID to SentryID String
18+
- Uses faster Random implementation to generate UUIDs
1619
- Use a separate `Random` instance per thread to improve SDK performance ([#3835](https://github.com/getsentry/sentry-java/pull/3835))
1720
- Android 15: Add support for 16KB page sizes ([#3851](https://github.com/getsentry/sentry-java/pull/3851))
1821
- See https://developer.android.com/guide/practices/page-sizes for more details

sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.sentry.MemoryCollectionData;
1313
import io.sentry.PerformanceCollectionData;
1414
import io.sentry.SentryLevel;
15+
import io.sentry.SentryUUID;
1516
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
1617
import io.sentry.profilemeasurements.ProfileMeasurement;
1718
import io.sentry.profilemeasurements.ProfileMeasurementValue;
@@ -23,7 +24,6 @@
2324
import java.util.HashMap;
2425
import java.util.List;
2526
import java.util.Map;
26-
import java.util.UUID;
2727
import java.util.concurrent.Future;
2828
import java.util.concurrent.RejectedExecutionException;
2929
import java.util.concurrent.TimeUnit;
@@ -129,7 +129,7 @@ public AndroidProfiler(
129129
}
130130

131131
// We create a file with a uuid name, so no need to check if it already exists
132-
traceFile = new File(traceFilesDir, UUID.randomUUID() + ".trace");
132+
traceFile = new File(traceFilesDir, SentryUUID.generateSentryId() + ".trace");
133133

134134
measurementsMap.clear();
135135
screenFrameRateMeasurements.clear();

sentry-android-core/src/main/java/io/sentry/android/core/Installation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
import android.content.Context;
44
import io.sentry.ISentryLifecycleToken;
5+
import io.sentry.SentryUUID;
56
import io.sentry.util.AutoClosableReentrantLock;
67
import java.io.File;
78
import java.io.FileOutputStream;
89
import java.io.IOException;
910
import java.io.OutputStream;
1011
import java.io.RandomAccessFile;
1112
import java.nio.charset.Charset;
12-
import java.util.UUID;
1313
import org.jetbrains.annotations.NotNull;
1414
import org.jetbrains.annotations.Nullable;
1515
import org.jetbrains.annotations.TestOnly;
@@ -65,7 +65,7 @@ public static String id(final @NotNull Context context) throws RuntimeException
6565
static @NotNull String writeInstallationFile(final @NotNull File installation)
6666
throws IOException {
6767
try (final OutputStream out = new FileOutputStream(installation)) {
68-
final String id = UUID.randomUUID().toString();
68+
final String id = SentryUUID.generateSentryId();
6969
out.write(id.getBytes(UTF_8));
7070
out.flush();
7171
return id;

sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
import io.sentry.ILogger;
1717
import io.sentry.SentryLevel;
1818
import io.sentry.SentryOptions;
19+
import io.sentry.SentryUUID;
1920
import io.sentry.android.core.BuildInfoProvider;
2021
import io.sentry.android.core.ContextUtils;
2122
import io.sentry.util.Objects;
2223
import java.lang.ref.WeakReference;
2324
import java.lang.reflect.Field;
2425
import java.util.Map;
2526
import java.util.Set;
26-
import java.util.UUID;
2727
import java.util.concurrent.ConcurrentHashMap;
2828
import java.util.concurrent.CopyOnWriteArraySet;
2929
import java.util.concurrent.TimeUnit;
@@ -262,7 +262,7 @@ public void onActivityDestroyed(@NotNull Activity activity) {}
262262
if (!isAvailable) {
263263
return null;
264264
}
265-
final String uid = UUID.randomUUID().toString();
265+
final String uid = SentryUUID.generateSentryId();
266266
listenerMap.put(uid, listener);
267267
trackCurrentWindow();
268268
return uid;

sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ android {
1717

1818
defaultConfig {
1919
applicationId = "io.sentry.uitest.android.critical"
20-
minSdk = Config.Android.minSdkVersionCompose
20+
minSdk = Config.Android.minSdkVersion
2121
targetSdk = Config.Android.targetSdkVersion
2222
versionCode = 1
2323
versionName = "1.0"

sentry/api/sentry.api

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3265,6 +3265,11 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction {
32653265
public fun updateEndDate (Lio/sentry/SentryDate;)Z
32663266
}
32673267

3268+
public final class io/sentry/SentryUUID {
3269+
public static fun generateSentryId ()Ljava/lang/String;
3270+
public static fun generateSpanId ()Ljava/lang/String;
3271+
}
3272+
32683273
public final class io/sentry/SentryWrapper {
32693274
public fun <init> ()V
32703275
public static fun wrapCallable (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable;
@@ -6198,6 +6203,20 @@ public final class io/sentry/util/TracingUtils$TracingHeaders {
61986203
public fun getSentryTraceHeader ()Lio/sentry/SentryTraceHeader;
61996204
}
62006205

6206+
public final class io/sentry/util/UUIDGenerator {
6207+
public fun <init> ()V
6208+
public static fun randomHalfLengthUUID ()J
6209+
public static fun randomUUID ()Ljava/util/UUID;
6210+
}
6211+
6212+
public final class io/sentry/util/UUIDStringUtils {
6213+
public fun <init> ()V
6214+
public static fun toSentryIdString (JJ)Ljava/lang/String;
6215+
public static fun toSentryIdString (Ljava/util/UUID;)Ljava/lang/String;
6216+
public static fun toSentrySpanIdString (J)Ljava/lang/String;
6217+
public static fun toSentrySpanIdString (Ljava/util/UUID;)Ljava/lang/String;
6218+
}
6219+
62016220
public final class io/sentry/util/UrlUtils {
62026221
public static final field SENSITIVE_DATA_SUBSTITUTE Ljava/lang/String;
62036222
public fun <init> ()V

sentry/src/main/java/io/sentry/ProfilingTraceData.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.util.List;
1111
import java.util.Locale;
1212
import java.util.Map;
13-
import java.util.UUID;
1413
import java.util.concurrent.Callable;
1514
import java.util.concurrent.ConcurrentHashMap;
1615
import org.jetbrains.annotations.ApiStatus;
@@ -154,7 +153,7 @@ public ProfilingTraceData(
154153
// Stacktrace context
155154
this.transactionId = transactionId;
156155
this.traceId = traceId;
157-
this.profileId = UUID.randomUUID().toString();
156+
this.profileId = SentryUUID.generateSentryId();
158157
this.environment = environment != null ? environment : DEFAULT_ENVIRONMENT;
159158
this.truncationReason = truncationReason;
160159
if (!isTruncationReasonValid()) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.sentry;
2+
3+
import io.sentry.util.UUIDGenerator;
4+
import io.sentry.util.UUIDStringUtils;
5+
6+
/**
7+
* SentryUUID is a utility class for generating Sentry-specific ID Strings. It provides methods for
8+
* generating Sentry IDs and Sentry Span IDs.
9+
*/
10+
public final class SentryUUID {
11+
12+
private SentryUUID() {
13+
// A private constructor prevents callers from accidentally instantiating FastUUID objects
14+
}
15+
16+
public static String generateSentryId() {
17+
return UUIDStringUtils.toSentryIdString(UUIDGenerator.randomUUID());
18+
}
19+
20+
public static String generateSpanId() {
21+
return UUIDStringUtils.toSentrySpanIdString(UUIDGenerator.randomHalfLengthUUID());
22+
}
23+
}

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import static io.sentry.util.StringUtils.PROPER_NIL_UUID;
44

55
import io.sentry.util.LazyEvaluator;
6-
import io.sentry.util.StringUtils;
76
import java.io.IOException;
87
import java.util.Objects;
9-
import java.util.UUID;
108
import org.jetbrains.annotations.NotNull;
119

1210
public final class SpanId implements JsonSerializable {
13-
public static final SpanId EMPTY_ID = new SpanId(PROPER_NIL_UUID);
11+
public static final SpanId EMPTY_ID =
12+
new SpanId(PROPER_NIL_UUID.replace("-", "").substring(0, 16));
1413

1514
private final @NotNull LazyEvaluator<String> lazyValue;
1615

@@ -20,12 +19,7 @@ public SpanId(final @NotNull String value) {
2019
}
2120

2221
public SpanId() {
23-
this.lazyValue =
24-
new LazyEvaluator<>(
25-
() ->
26-
StringUtils.normalizeUUID(UUID.randomUUID().toString())
27-
.replace("-", "")
28-
.substring(0, 16));
22+
this.lazyValue = new LazyEvaluator<>(SentryUUID::generateSpanId);
2923
}
3024

3125
@Override

sentry/src/main/java/io/sentry/cache/EnvelopeCache.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.sentry.SentryItemType;
1717
import io.sentry.SentryLevel;
1818
import io.sentry.SentryOptions;
19+
import io.sentry.SentryUUID;
1920
import io.sentry.Session;
2021
import io.sentry.UncaughtExceptionHandlerIntegration;
2122
import io.sentry.hints.AbnormalExit;
@@ -45,7 +46,6 @@
4546
import java.util.Iterator;
4647
import java.util.List;
4748
import java.util.Map;
48-
import java.util.UUID;
4949
import java.util.WeakHashMap;
5050
import java.util.concurrent.CountDownLatch;
5151
import java.util.concurrent.TimeUnit;
@@ -368,7 +368,7 @@ public void discard(final @NotNull SentryEnvelope envelope) {
368368
if (fileNameMap.containsKey(envelope)) {
369369
fileName = fileNameMap.get(envelope);
370370
} else {
371-
fileName = UUID.randomUUID() + SUFFIX_ENVELOPE_FILE;
371+
fileName = SentryUUID.generateSentryId() + SUFFIX_ENVELOPE_FILE;
372372
fileNameMap.put(envelope, fileName);
373373
}
374374

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
import io.sentry.JsonSerializable;
66
import io.sentry.ObjectReader;
77
import io.sentry.ObjectWriter;
8+
import io.sentry.SentryUUID;
89
import io.sentry.util.LazyEvaluator;
910
import io.sentry.util.StringUtils;
11+
import io.sentry.util.UUIDStringUtils;
1012
import java.io.IOException;
1113
import java.util.UUID;
1214
import org.jetbrains.annotations.NotNull;
1315
import org.jetbrains.annotations.Nullable;
1416

1517
public final class SentryId implements JsonSerializable {
1618

17-
public static final SentryId EMPTY_ID = new SentryId(new UUID(0, 0));
19+
public static final SentryId EMPTY_ID =
20+
new SentryId(StringUtils.PROPER_NIL_UUID.replace("-", ""));
1821

1922
private final @NotNull LazyEvaluator<String> lazyStringValue;
2023

@@ -24,9 +27,10 @@ public SentryId() {
2427

2528
public SentryId(@Nullable UUID uuid) {
2629
if (uuid != null) {
27-
this.lazyStringValue = new LazyEvaluator<>(() -> normalize(uuid.toString()));
30+
this.lazyStringValue =
31+
new LazyEvaluator<>(() -> normalize(UUIDStringUtils.toSentryIdString(uuid)));
2832
} else {
29-
this.lazyStringValue = new LazyEvaluator<>(() -> normalize(UUID.randomUUID().toString()));
33+
this.lazyStringValue = new LazyEvaluator<>(SentryUUID::generateSentryId);
3034
}
3135
}
3236

@@ -38,7 +42,11 @@ public SentryId(final @NotNull String sentryIdString) {
3842
+ "or 36 characters long (completed UUID). Received: "
3943
+ sentryIdString);
4044
}
41-
this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized));
45+
if (normalized.length() == 36) {
46+
this.lazyStringValue = new LazyEvaluator<>(() -> normalize(normalized));
47+
} else {
48+
this.lazyStringValue = new LazyEvaluator<>(() -> normalized);
49+
}
4250
}
4351

4452
@Override
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package io.sentry.util;
27+
28+
import java.util.UUID;
29+
30+
/**
31+
* Utility class for generating UUIDs and half-length (1 long) UUIDs. Adapted from `java.util.UUID`
32+
* to use a faster random number generator.
33+
*/
34+
public final class UUIDGenerator {
35+
36+
@SuppressWarnings("NarrowingCompoundAssignment")
37+
public static long randomHalfLengthUUID() {
38+
Random random = SentryRandom.current();
39+
byte[] randomBytes = new byte[8];
40+
random.nextBytes(randomBytes);
41+
randomBytes[6] &= 0x0f; /* clear version */
42+
randomBytes[6] |= 0x40; /* set to version 4 */
43+
44+
long msb = 0;
45+
46+
for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff);
47+
48+
return msb;
49+
}
50+
51+
@SuppressWarnings("NarrowingCompoundAssignment")
52+
public static UUID randomUUID() {
53+
Random random = SentryRandom.current();
54+
byte[] randomBytes = new byte[16];
55+
random.nextBytes(randomBytes);
56+
randomBytes[6] &= 0x0f; /* clear version */
57+
randomBytes[6] |= 0x40; /* set to version 4 */
58+
randomBytes[8] &= 0x3f; /* clear variant */
59+
randomBytes[8] |= (byte) 0x80; /* set to IETF variant */
60+
61+
long msb = 0;
62+
long lsb = 0;
63+
64+
for (int i = 0; i < 8; i++) msb = (msb << 8) | (randomBytes[i] & 0xff);
65+
66+
for (int i = 8; i < 16; i++) lsb = (lsb << 8) | (randomBytes[i] & 0xff);
67+
68+
return new UUID(msb, lsb);
69+
}
70+
}

0 commit comments

Comments
 (0)