Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QA] Lazily load SentryOptions members #3749

Merged
merged 13 commits into from
Oct 8, 2024
6 changes: 5 additions & 1 deletion sentry/src/main/java/io/sentry/ExperimentalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
* <p>Beware that experimental options can change at any time.
*/
public final class ExperimentalOptions {
private @NotNull SentryReplayOptions sessionReplay = new SentryReplayOptions();
private @NotNull SentryReplayOptions sessionReplay;

public ExperimentalOptions(final boolean empty) {
this.sessionReplay = new SentryReplayOptions(empty);
}

@NotNull
public SentryReplayOptions getSessionReplay() {
Expand Down
45 changes: 36 additions & 9 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import io.sentry.transport.ITransportGate;
import io.sentry.transport.NoOpEnvelopeCache;
import io.sentry.transport.NoOpTransportGate;
import io.sentry.util.Objects;
import io.sentry.util.Platform;
import io.sentry.util.SampleRateUtils;
import io.sentry.util.StringUtils;
import io.sentry.util.thread.IMainThreadChecker;
import io.sentry.util.thread.NoOpMainThreadChecker;
import java.io.File;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -119,10 +119,14 @@ public class SentryOptions {
private @NotNull SentryLevel diagnosticLevel = DEFAULT_DIAGNOSTIC_LEVEL;

/** Envelope reader interface */
private @NotNull IEnvelopeReader envelopeReader = new EnvelopeReader(new JsonSerializer(this));
private volatile @Nullable IEnvelopeReader envelopeReader;

private final @NotNull Object envelopeReaderLock = new Object();

/** Serializer interface to serialize/deserialize json events */
private @NotNull ISerializer serializer = new JsonSerializer(this);
private volatile @Nullable ISerializer serializer;

private final @NotNull Object serializerLock = new Object();

/** Max depth when serializing object graphs with reflection. * */
private int maxDepth = 100;
Expand Down Expand Up @@ -415,8 +419,9 @@ public class SentryOptions {
private boolean traceOptionsRequests = true;

/** Date provider to retrieve the current date from. */
@ApiStatus.Internal
private @NotNull SentryDateProvider dateProvider = new SentryAutoDateProvider();
@ApiStatus.Internal private volatile @Nullable SentryDateProvider dateProvider;
romtsn marked this conversation as resolved.
Show resolved Hide resolved

private final @NotNull Object dateProviderLock = new Object();

private final @NotNull List<IPerformanceCollector> performanceCollectors = new ArrayList<>();

Expand Down Expand Up @@ -479,7 +484,7 @@ public class SentryOptions {

@ApiStatus.Experimental private @Nullable Cron cron = null;

private final @NotNull ExperimentalOptions experimental = new ExperimentalOptions();
private final @NotNull ExperimentalOptions experimental;

private @NotNull ReplayController replayController = NoOpReplayController.getInstance();

Expand Down Expand Up @@ -605,7 +610,14 @@ public void setDiagnosticLevel(@Nullable final SentryLevel diagnosticLevel) {
* @return the serializer
*/
public @NotNull ISerializer getSerializer() {
return serializer;
if (serializer == null) {
synchronized (serializerLock) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should replace synchronized with using a ReentrantLock when merging into v8.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep noted!

if (serializer == null) {
serializer = new JsonSerializer(this);
}
}
}
return Objects.requireNonNull(serializer, "Serializer was null");
}

/**
Expand Down Expand Up @@ -636,7 +648,14 @@ public void setMaxDepth(int maxDepth) {
}

public @NotNull IEnvelopeReader getEnvelopeReader() {
return envelopeReader;
if (envelopeReader == null) {
synchronized (envelopeReaderLock) {
if (envelopeReader == null) {
envelopeReader = new EnvelopeReader(getSerializer());
}
}
}
return Objects.requireNonNull(envelopeReader, "EnvelopeReader was null");
}

public void setEnvelopeReader(final @Nullable IEnvelopeReader envelopeReader) {
Expand Down Expand Up @@ -2212,7 +2231,14 @@ public void setIgnoredCheckIns(final @Nullable List<String> ignoredCheckIns) {
/** Returns the current {@link SentryDateProvider} that is used to retrieve the current date. */
@ApiStatus.Internal
public @NotNull SentryDateProvider getDateProvider() {
return dateProvider;
if (dateProvider == null) {
synchronized (dateProviderLock) {
if (dateProvider == null) {
dateProvider = new SentryAutoDateProvider();
}
}
}
return Objects.requireNonNull(dateProvider, "DateProvider is not set");
}

/**
Expand Down Expand Up @@ -2540,6 +2566,7 @@ public SentryOptions() {
* @param empty if options should be empty.
*/
private SentryOptions(final boolean empty) {
experimental = new ExperimentalOptions(empty);
if (!empty) {
// SentryExecutorService should be initialized before any
// SendCachedEventFireAndForgetIntegration
Expand Down
10 changes: 6 additions & 4 deletions sentry/src/main/java/io/sentry/SentryReplayOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,16 @@ public enum SentryReplayQuality {
/** The maximum duration of a full session replay, defaults to 1h. */
private long sessionDuration = 60 * 60 * 1000L;

public SentryReplayOptions() {
setRedactAllText(true);
setRedactAllImages(true);
public SentryReplayOptions(final boolean empty) {
if (!empty) {
setRedactAllText(true);
setRedactAllImages(true);
}
}

public SentryReplayOptions(
final @Nullable Double sessionSampleRate, final @Nullable Double onErrorSampleRate) {
this();
this(false);
this.sessionSampleRate = sessionSampleRate;
this.onErrorSampleRate = onErrorSampleRate;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.sentry.clientreport;

import io.sentry.DataCategory;
import io.sentry.util.LazyEvaluator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.jetbrains.annotations.ApiStatus;
Expand All @@ -14,25 +16,28 @@
@ApiStatus.Internal
final class AtomicClientReportStorage implements IClientReportStorage {

private final @NotNull Map<ClientReportKey, AtomicLong> lostEventCounts;
private final @NotNull LazyEvaluator<Map<ClientReportKey, AtomicLong>> lostEventCounts =
new LazyEvaluator<>(
romtsn marked this conversation as resolved.
Show resolved Hide resolved
romtsn marked this conversation as resolved.
Show resolved Hide resolved
() -> {
final Map<ClientReportKey, AtomicLong> modifyableEventCountsForInit =
new ConcurrentHashMap<>();

public AtomicClientReportStorage() {
final Map<ClientReportKey, AtomicLong> modifyableEventCountsForInit = new ConcurrentHashMap<>();
for (final DiscardReason discardReason : DiscardReason.values()) {
romtsn marked this conversation as resolved.
Show resolved Hide resolved
for (final DataCategory category : DataCategory.values()) {
modifyableEventCountsForInit.put(
new ClientReportKey(discardReason.getReason(), category.getCategory()),
new AtomicLong(0));
}
}

for (final DiscardReason discardReason : DiscardReason.values()) {
for (final DataCategory category : DataCategory.values()) {
modifyableEventCountsForInit.put(
new ClientReportKey(discardReason.getReason(), category.getCategory()),
new AtomicLong(0));
}
}
return Collections.unmodifiableMap(modifyableEventCountsForInit);
});

lostEventCounts = Collections.unmodifiableMap(modifyableEventCountsForInit);
}
public AtomicClientReportStorage() {}

@Override
public void addCount(ClientReportKey key, Long count) {
final @Nullable AtomicLong quantity = lostEventCounts.get(key);
final @Nullable AtomicLong quantity = lostEventCounts.getValue().get(key);

if (quantity != null) {
quantity.addAndGet(count);
Expand All @@ -43,7 +48,8 @@ public void addCount(ClientReportKey key, Long count) {
public List<DiscardedEvent> resetCountsAndGet() {
final List<DiscardedEvent> discardedEvents = new ArrayList<>();

for (final Map.Entry<ClientReportKey, AtomicLong> entry : lostEventCounts.entrySet()) {
Set<Map.Entry<ClientReportKey, AtomicLong>> entrySet = lostEventCounts.getValue().entrySet();
for (final Map.Entry<ClientReportKey, AtomicLong> entry : entrySet) {
final Long quantity = entry.getValue().getAndSet(0);
if (quantity > 0) {
discardedEvents.add(
Expand Down