Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Performance

- Remove executor prewarm during SDK init ([#5681](https://github.com/getsentry/sentry-java/pull/5681))
- The single-threaded `SentryExecutorService` queued the prewarm work ahead of the first useful task, so it could only delay init work, never speed it up; the thread and class loading it warmed are paid identically by the first real task submitted right after.

## 8.47.0

### Behavioral Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ class AndroidProfilerTest {
override fun close(timeoutMillis: Long) {}

override fun isClosed() = false

override fun prewarm() = Unit
}

val options =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ class AndroidTransactionProfilerTest {
override fun close(timeoutMillis: Long) {}

override fun isClosed() = false

override fun prewarm() = Unit
}

val options =
Expand Down
6 changes: 0 additions & 6 deletions sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ class ImmediateExecutorService : ISentryExecutorService {
override fun close(timeoutMillis: Long) {}

override fun isClosed(): Boolean = false

override fun prewarm() = Unit
}

class DeferredExecutorService : ISentryExecutorService {
Expand Down Expand Up @@ -74,8 +72,6 @@ class DeferredExecutorService : ISentryExecutorService {

override fun isClosed(): Boolean = false

override fun prewarm() = Unit

fun hasScheduledRunnables(): Boolean = scheduledRunnables.isNotEmpty()
}

Expand All @@ -90,8 +86,6 @@ class NonOverridableNoOpSentryExecutorService : ISentryExecutorService {
override fun close(timeoutMillis: Long) {}

override fun isClosed(): Boolean = false

override fun prewarm() = Unit
}

fun createSentryClientMock(enabled: Boolean = true) =
Expand Down
3 changes: 0 additions & 3 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,6 @@ public abstract interface class io/sentry/ISentryClient {
public abstract interface class io/sentry/ISentryExecutorService {
public abstract fun close (J)V
public abstract fun isClosed ()Z
public abstract fun prewarm ()V
public abstract fun schedule (Ljava/lang/Runnable;J)Ljava/util/concurrent/Future;
public abstract fun submit (Ljava/lang/Runnable;)Ljava/util/concurrent/Future;
public abstract fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
Expand Down Expand Up @@ -1879,7 +1878,6 @@ public final class io/sentry/NoOpSentryExecutorService : io/sentry/ISentryExecut
public fun close (J)V
public static fun getInstance ()Lio/sentry/ISentryExecutorService;
public fun isClosed ()Z
public fun prewarm ()V
public fun schedule (Ljava/lang/Runnable;J)Ljava/util/concurrent/Future;
public fun submit (Ljava/lang/Runnable;)Ljava/util/concurrent/Future;
public fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
Expand Down Expand Up @@ -3181,7 +3179,6 @@ public final class io/sentry/SentryExecutorService : io/sentry/ISentryExecutorSe
public fun <init> (Lio/sentry/SentryOptions;)V
public fun close (J)V
public fun isClosed ()Z
public fun prewarm ()V
public fun schedule (Ljava/lang/Runnable;J)Ljava/util/concurrent/Future;
public fun submit (Ljava/lang/Runnable;)Ljava/util/concurrent/Future;
public fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
Expand Down
6 changes: 0 additions & 6 deletions sentry/src/main/java/io/sentry/ISentryExecutorService.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,4 @@ Future<?> schedule(final @NotNull Runnable runnable, final long delayMillis)
* @return If the executorService was previously closed
*/
boolean isClosed();

/**
* Pre-warms the executor service by increasing the initial queue capacity. SHOULD be called
* directly after instantiating this executor service.
*/
void prewarm();
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,4 @@ public void close(long timeoutMillis) {}
public boolean isClosed() {
return false;
}

@Override
public void prewarm() {}
}
1 change: 0 additions & 1 deletion sentry/src/main/java/io/sentry/Sentry.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ private static void init(final @NotNull SentryOptions options, final boolean glo
// to set a new one
if (options.getExecutorService().isClosed()) {
options.setExecutorService(new SentryExecutorService(options));
options.getExecutorService().prewarm();
}

// load lazy fields of the options in a separate thread
Expand Down
39 changes: 0 additions & 39 deletions sentry/src/main/java/io/sentry/SentryExecutorService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@
@ApiStatus.Internal
public final class SentryExecutorService implements ISentryExecutorService {

/**
* ScheduledThreadPoolExecutor grows work queue by 50% each time. With the initial capacity of 16
* it will have to resize 4 times to reach 40, which is a decent middle-ground for prewarming.
* This will prevent from growing in unexpected areas of the SDK.
*/
private static final int INITIAL_QUEUE_SIZE = 40;

/**
* By default, the work queue is unbounded so it can grow as much as the memory allows. We want to
* limit it by 271 which would be x8 times growth from the default initial capacity.
Expand All @@ -32,9 +25,6 @@ public final class SentryExecutorService implements ISentryExecutorService {
private final @NotNull ScheduledThreadPoolExecutor executorService;
private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock();

@SuppressWarnings("UnnecessaryLambda")
private final @NotNull Runnable dummyRunnable = () -> {};

private final @Nullable SentryOptions options;

@TestOnly
Expand Down Expand Up @@ -120,35 +110,6 @@ public boolean isClosed() {
}
}

@SuppressWarnings({"FutureReturnValueIgnored"})
@Override
public void prewarm() {
try {
executorService.submit(
() -> {
try {
// schedule a bunch of dummy runnables in the future that will never execute to
// trigger
// queue growth and then purge the queue
for (int i = 0; i < INITIAL_QUEUE_SIZE; i++) {
final Future<?> future =
executorService.schedule(dummyRunnable, 365L, TimeUnit.DAYS);
future.cancel(true);
}
executorService.purge();
} catch (RejectedExecutionException ignored) {
// ignore
}
});
} catch (RejectedExecutionException e) {
if (options != null) {
options
.getLogger()
.log(SentryLevel.WARNING, "Prewarm task rejected from " + executorService, e);
}
}
}

private static final class SentryExecutorServiceThreadFactory implements ThreadFactory {
private int cnt;

Expand Down
1 change: 0 additions & 1 deletion sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,6 @@ public void activate() {
// SentryExecutorService should be initialized before any
// SendCachedEventFireAndForgetIntegration
executorService = new SentryExecutorService(this);
executorService.prewarm();
}

// SpotlightIntegration is loaded via reflection to allow the sentry-spotlight module
Expand Down
16 changes: 0 additions & 16 deletions sentry/src/test/java/io/sentry/SentryExecutorServiceTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.sentry

import io.sentry.test.getProperty
import java.util.concurrent.BlockingQueue
import java.util.concurrent.Callable
import java.util.concurrent.CancellationException
Expand Down Expand Up @@ -191,21 +190,6 @@ class SentryExecutorServiceTest {
verify(executor).schedule(any<Runnable>(), any(), any())
}

@Test
fun `SentryExecutorService prewarm schedules dummy tasks and clears queue`() {
val executor = ScheduledThreadPoolExecutor(1)

val sentryExecutor = SentryExecutorService(executor, null)
sentryExecutor.prewarm()

Thread.sleep(1000)

// the internal queue/array should be resized 4 times to 54
assertEquals(54, (executor.queue.getProperty("queue") as Array<*>).size)
// the queue should be empty
assertEquals(0, executor.queue.size)
}

@Test
fun `SentryExecutorService schedules any number of job`() {
val executor = ScheduledThreadPoolExecutor(1)
Expand Down
1 change: 0 additions & 1 deletion sentry/src/test/java/io/sentry/SentryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,6 @@ class SentryTest {
it.cacheDirPath = getTempPath()
it.setLogger(logger)
it.executorService = SentryExecutorService()
it.executorService.prewarm()
it.executorService.close(0)
it.isDebug = true
}
Expand Down
Loading