Skip to content

Commit 8ff8fd0

Browse files
adinauergetsentry-botromtsnmarkushilbloder
authored
Automatically downsample transaction when system is under load (backpressure) (#3072)
Co-authored-by: Sentry Github Bot <[email protected]> Co-authored-by: Roman Zavarnitsyn <[email protected]> Co-authored-by: Markus Hintersteiner <[email protected]> Co-authored-by: Lukas Bloder <[email protected]> Co-authored-by: Stefano <[email protected]> Co-authored-by: getsentry-bot <[email protected]> Co-authored-by: getsentry-bot <[email protected]> Co-authored-by: Richard Harrah <[email protected]>
1 parent 8d62770 commit 8ff8fd0

File tree

41 files changed

+555
-23
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+555
-23
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
### Features
66

77
- Support multiple debug-metadata.properties ([#3024](https://github.com/getsentry/sentry-java/pull/3024))
8+
- Automatically downsample transactions when the system is under load ([#3072](https://github.com/getsentry/sentry-java/pull/3072))
9+
- You can opt into this behaviour by setting `enable-backpressure-handling=true`.
10+
- We're happy to receive feedback, e.g. [in this GitHub issue](https://github.com/getsentry/sentry-java/issues/2829)
11+
- When the system is under load we start reducing the `tracesSampleRate` automatically.
12+
- Once the system goes back to healthy, we reset the `tracesSampleRate` to its original value.
813
- (Android) Experimental: Provide more detailed cold app start information ([#3057](https://github.com/getsentry/sentry-java/pull/3057))
914
- Attaches spans for Application, ContentProvider, and Activities to app-start timings
1015
- Uses Process.startUptimeMillis to calculate app-start timings

sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ sentry.enable-tracing=true
1313
sentry.ignored-checkins=ignored_monitor_slug_1,ignored_monitor_slug_2
1414
sentry.debug=true
1515
sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR
16+
sentry.enable-backpressure-handling=true
1617
in-app-includes="io.sentry.samples"
1718

1819
# Uncomment and set to true to enable aot compatibility

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/resources/application.properties

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ sentry.logging.minimum-event-level=info
99
sentry.logging.minimum-breadcrumb-level=debug
1010
sentry.reactive.thread-local-accessor-enabled=true
1111
sentry.enable-tracing=true
12+
sentry.enable-backpressure-handling=true

sentry-samples/sentry-samples-spring-boot-webflux/src/main/resources/application.properties

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ sentry.enable-tracing=true
1111
spring.graphql.graphiql.enabled=true
1212
spring.graphql.websocket.path=/graphql
1313
spring.graphql.schema.printer.enabled=true
14+
sentry.enable-backpressure-handling=true

sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ sentry.enable-tracing=true
1313
sentry.ignored-checkins=ignored_monitor_slug_1,ignored_monitor_slug_2
1414
sentry.debug=true
1515
sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR
16+
sentry.enable-backpressure-handling=true
1617
in-app-includes="io.sentry.samples"
1718

1819
# Database configuration

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ class SentryAutoConfigurationTest {
162162
"sentry.trace-propagation-targets=localhost,^(http|https)://api\\..*\$",
163163
"sentry.enabled=false",
164164
"sentry.send-modules=false",
165-
"sentry.ignored-checkins=slug1,slugB"
165+
"sentry.ignored-checkins=slug1,slugB",
166+
"sentry.enable-backpressure-handling=true"
166167
).run {
167168
val options = it.getBean(SentryProperties::class.java)
168169
assertThat(options.readTimeoutMillis).isEqualTo(10)
@@ -194,6 +195,7 @@ class SentryAutoConfigurationTest {
194195
assertThat(options.isEnabled).isEqualTo(false)
195196
assertThat(options.isSendModules).isEqualTo(false)
196197
assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB")
198+
assertThat(options.isEnableBackpressureHandling).isEqualTo(true)
197199
}
198200
}
199201

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ class SentrySpringIntegrationTest {
210210

211211
@SpringBootApplication
212212
open class App {
213-
private val transport = mock<ITransport>()
213+
private val transport = mock<ITransport>().also {
214+
whenever(it.isHealthy).thenReturn(true)
215+
}
214216

215217
@Bean
216218
open fun mockTransportFactory(): ITransportFactory {

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ class SentryAutoConfigurationTest {
162162
"sentry.trace-propagation-targets=localhost,^(http|https)://api\\..*\$",
163163
"sentry.enabled=false",
164164
"sentry.send-modules=false",
165-
"sentry.ignored-checkins=slug1,slugB"
165+
"sentry.ignored-checkins=slug1,slugB",
166+
"sentry.enable-backpressure-handling=true"
166167
).run {
167168
val options = it.getBean(SentryProperties::class.java)
168169
assertThat(options.readTimeoutMillis).isEqualTo(10)
@@ -194,6 +195,7 @@ class SentryAutoConfigurationTest {
194195
assertThat(options.isEnabled).isEqualTo(false)
195196
assertThat(options.isSendModules).isEqualTo(false)
196197
assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB")
198+
assertThat(options.isEnableBackpressureHandling).isEqualTo(true)
197199
}
198200
}
199201

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ class SentrySpringIntegrationTest {
210210

211211
@SpringBootApplication
212212
open class App {
213-
private val transport = mock<ITransport>()
213+
private val transport = mock<ITransport>().also {
214+
whenever(it.isHealthy).thenReturn(true)
215+
}
214216

215217
@Bean
216218
open fun mockTransportFactory(): ITransportFactory {

sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import io.sentry.ITransportFactory
66
import io.sentry.Integration
77
import io.sentry.Sentry
88
import io.sentry.SentryOptions
9+
import io.sentry.transport.ITransport
910
import org.assertj.core.api.Assertions.assertThat
11+
import org.mockito.kotlin.any
1012
import org.mockito.kotlin.mock
13+
import org.mockito.kotlin.whenever
1114
import org.springframework.boot.context.annotation.UserConfigurations
1215
import org.springframework.boot.test.context.runner.ApplicationContextRunner
1316
import org.springframework.context.annotation.Bean
@@ -185,7 +188,9 @@ class EnableSentryTest {
185188
class AppConfigWithCustomTransportFactory {
186189

187190
@Bean
188-
fun transport() = mock<ITransportFactory>()
191+
fun transport() = mock<ITransportFactory>().also {
192+
whenever(it.create(any(), any())).thenReturn(mock<ITransport>())
193+
}
189194
}
190195

191196
@EnableSentry(dsn = "http://key@localhost/proj")

sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ class SentryWebfluxIntegrationTest {
145145
@SpringBootApplication(exclude = [ReactiveSecurityAutoConfiguration::class, SecurityAutoConfiguration::class])
146146
open class App {
147147

148-
private val transport = mock<ITransport>()
148+
private val transport = mock<ITransport>().also {
149+
whenever(it.isHealthy).thenReturn(true)
150+
}
149151

150152
@Bean
151153
open fun mockTransportFactory(): ITransportFactory {

sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import io.sentry.ITransportFactory
66
import io.sentry.Integration
77
import io.sentry.Sentry
88
import io.sentry.SentryOptions
9+
import io.sentry.transport.ITransport
910
import org.assertj.core.api.Assertions.assertThat
11+
import org.mockito.kotlin.any
1012
import org.mockito.kotlin.mock
13+
import org.mockito.kotlin.whenever
1114
import org.springframework.boot.context.annotation.UserConfigurations
1215
import org.springframework.boot.test.context.runner.ApplicationContextRunner
1316
import org.springframework.context.annotation.Bean
@@ -185,7 +188,9 @@ class EnableSentryTest {
185188
class AppConfigWithCustomTransportFactory {
186189

187190
@Bean
188-
fun transport() = mock<ITransportFactory>()
191+
fun transport() = mock<ITransportFactory>().also {
192+
whenever(it.create(any(), any())).thenReturn(mock<ITransport>())
193+
}
189194
}
190195

191196
@EnableSentry(dsn = "http://key@localhost/proj")

sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ class SentryWebfluxIntegrationTest {
145145
@SpringBootApplication(exclude = [ReactiveSecurityAutoConfiguration::class, SecurityAutoConfiguration::class])
146146
open class App {
147147

148-
private val transport = mock<ITransport>()
148+
private val transport = mock<ITransport>().also {
149+
whenever(it.isHealthy).thenReturn(true)
150+
}
149151

150152
@Bean
151153
open fun mockTransportFactory(): ITransportFactory {

sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
package io.sentry.test
33

44
import io.sentry.ISentryExecutorService
5+
import io.sentry.backpressure.IBackpressureMonitor
56
import org.mockito.kotlin.mock
67
import java.util.concurrent.Callable
78
import java.util.concurrent.Future
89

910
class ImmediateExecutorService : ISentryExecutorService {
1011
override fun submit(runnable: Runnable): Future<*> {
11-
runnable.run()
12+
if (runnable !is IBackpressureMonitor) {
13+
runnable.run()
14+
}
1215
return mock()
1316
}
1417

1518
override fun <T> submit(callable: Callable<T>): Future<T> = mock()
1619
override fun schedule(runnable: Runnable, delayMillis: Long): Future<*> {
17-
runnable.run()
20+
if (runnable !is IBackpressureMonitor) {
21+
runnable.run()
22+
}
1823
return mock<Future<Runnable>>()
1924
}
2025
override fun close(timeoutMillis: Long) {}

sentry/api/sentry.api

+35
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,14 @@ public final class io/sentry/ExternalOptions {
336336
public fun getTracePropagationTargets ()Ljava/util/List;
337337
public fun getTracesSampleRate ()Ljava/lang/Double;
338338
public fun getTracingOrigins ()Ljava/util/List;
339+
public fun isEnableBackpressureHandling ()Ljava/lang/Boolean;
339340
public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean;
340341
public fun isEnabled ()Ljava/lang/Boolean;
341342
public fun isSendModules ()Ljava/lang/Boolean;
342343
public fun setDebug (Ljava/lang/Boolean;)V
343344
public fun setDist (Ljava/lang/String;)V
344345
public fun setDsn (Ljava/lang/String;)V
346+
public fun setEnableBackpressureHandling (Ljava/lang/Boolean;)V
345347
public fun setEnableDeduplication (Ljava/lang/Boolean;)V
346348
public fun setEnablePrettySerializationOutput (Ljava/lang/Boolean;)V
347349
public fun setEnableTracing (Ljava/lang/Boolean;)V
@@ -435,6 +437,7 @@ public final class io/sentry/Hub : io/sentry/IHub {
435437
public fun getTransaction ()Lio/sentry/ITransaction;
436438
public fun isCrashedLastRun ()Ljava/lang/Boolean;
437439
public fun isEnabled ()Z
440+
public fun isHealthy ()Z
438441
public fun popScope ()V
439442
public fun pushScope ()V
440443
public fun removeExtra (Ljava/lang/String;)V
@@ -485,6 +488,7 @@ public final class io/sentry/HubAdapter : io/sentry/IHub {
485488
public fun getTransaction ()Lio/sentry/ITransaction;
486489
public fun isCrashedLastRun ()Ljava/lang/Boolean;
487490
public fun isEnabled ()Z
491+
public fun isHealthy ()Z
488492
public fun popScope ()V
489493
public fun pushScope ()V
490494
public fun removeExtra (Ljava/lang/String;)V
@@ -578,6 +582,7 @@ public abstract interface class io/sentry/IHub {
578582
public abstract fun getTransaction ()Lio/sentry/ITransaction;
579583
public abstract fun isCrashedLastRun ()Ljava/lang/Boolean;
580584
public abstract fun isEnabled ()Z
585+
public abstract fun isHealthy ()Z
581586
public abstract fun popScope ()V
582587
public abstract fun pushScope ()V
583588
public abstract fun removeExtra (Ljava/lang/String;)V
@@ -718,6 +723,7 @@ public abstract interface class io/sentry/ISentryClient {
718723
public abstract fun flush (J)V
719724
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
720725
public abstract fun isEnabled ()Z
726+
public fun isHealthy ()Z
721727
}
722728

723729
public abstract interface class io/sentry/ISentryExecutorService {
@@ -1119,6 +1125,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub {
11191125
public fun getTransaction ()Lio/sentry/ITransaction;
11201126
public fun isCrashedLastRun ()Ljava/lang/Boolean;
11211127
public fun isEnabled ()Z
1128+
public fun isHealthy ()Z
11221129
public fun popScope ()V
11231130
public fun pushScope ()V
11241131
public fun removeExtra (Ljava/lang/String;)V
@@ -1665,6 +1672,7 @@ public final class io/sentry/Sentry {
16651672
public static fun init (Ljava/lang/String;)V
16661673
public static fun isCrashedLastRun ()Ljava/lang/Boolean;
16671674
public static fun isEnabled ()Z
1675+
public static fun isHealthy ()Z
16681676
public static fun popScope ()V
16691677
public static fun pushScope ()V
16701678
public static fun removeExtra (Ljava/lang/String;)V
@@ -1781,6 +1789,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
17811789
public fun flush (J)V
17821790
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
17831791
public fun isEnabled ()Z
1792+
public fun isHealthy ()Z
17841793
}
17851794

17861795
public final class io/sentry/SentryCrashLastRunState {
@@ -2078,6 +2087,7 @@ public class io/sentry/SentryOptions {
20782087
public fun addOptionsObserver (Lio/sentry/IOptionsObserver;)V
20792088
public fun addScopeObserver (Lio/sentry/IScopeObserver;)V
20802089
public fun addTracingOrigin (Ljava/lang/String;)V
2090+
public fun getBackpressureMonitor ()Lio/sentry/backpressure/IBackpressureMonitor;
20812091
public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;
20822092
public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback;
20832093
public fun getBeforeSendTransaction ()Lio/sentry/SentryOptions$BeforeSendTransactionCallback;
@@ -2156,6 +2166,7 @@ public class io/sentry/SentryOptions {
21562166
public fun isAttachThreads ()Z
21572167
public fun isDebug ()Z
21582168
public fun isEnableAutoSessionTracking ()Z
2169+
public fun isEnableBackpressureHandling ()Z
21592170
public fun isEnableDeduplication ()Z
21602171
public fun isEnableExternalConfiguration ()Z
21612172
public fun isEnablePrettySerializationOutput ()Z
@@ -2177,6 +2188,7 @@ public class io/sentry/SentryOptions {
21772188
public fun setAttachServerName (Z)V
21782189
public fun setAttachStacktrace (Z)V
21792190
public fun setAttachThreads (Z)V
2191+
public fun setBackpressureMonitor (Lio/sentry/backpressure/IBackpressureMonitor;)V
21802192
public fun setBeforeBreadcrumb (Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;)V
21812193
public fun setBeforeSend (Lio/sentry/SentryOptions$BeforeSendCallback;)V
21822194
public fun setBeforeSendTransaction (Lio/sentry/SentryOptions$BeforeSendTransactionCallback;)V
@@ -2191,6 +2203,7 @@ public class io/sentry/SentryOptions {
21912203
public fun setDistinctId (Ljava/lang/String;)V
21922204
public fun setDsn (Ljava/lang/String;)V
21932205
public fun setEnableAutoSessionTracking (Z)V
2206+
public fun setEnableBackpressureHandling (Z)V
21942207
public fun setEnableDeduplication (Z)V
21952208
public fun setEnableExternalConfiguration (Z)V
21962209
public fun setEnablePrettySerializationOutput (Z)V
@@ -2824,6 +2837,24 @@ public final class io/sentry/UserFeedback$JsonKeys {
28242837
public fun <init> ()V
28252838
}
28262839

2840+
public final class io/sentry/backpressure/BackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor, java/lang/Runnable {
2841+
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/IHub;)V
2842+
public fun getDownsampleFactor ()I
2843+
public fun run ()V
2844+
public fun start ()V
2845+
}
2846+
2847+
public abstract interface class io/sentry/backpressure/IBackpressureMonitor {
2848+
public abstract fun getDownsampleFactor ()I
2849+
public abstract fun start ()V
2850+
}
2851+
2852+
public final class io/sentry/backpressure/NoOpBackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor {
2853+
public fun getDownsampleFactor ()I
2854+
public static fun getInstance ()Lio/sentry/backpressure/NoOpBackpressureMonitor;
2855+
public fun start ()V
2856+
}
2857+
28272858
public class io/sentry/cache/EnvelopeCache : io/sentry/cache/IEnvelopeCache {
28282859
public static final field CRASH_MARKER_FILE Ljava/lang/String;
28292860
public static final field NATIVE_CRASH_MARKER_FILE Ljava/lang/String;
@@ -2925,6 +2956,7 @@ public final class io/sentry/clientreport/ClientReportRecorder : io/sentry/clien
29252956
}
29262957

29272958
public final class io/sentry/clientreport/DiscardReason : java/lang/Enum {
2959+
public static final field BACKPRESSURE Lio/sentry/clientreport/DiscardReason;
29282960
public static final field BEFORE_SEND Lio/sentry/clientreport/DiscardReason;
29292961
public static final field CACHE_OVERFLOW Lio/sentry/clientreport/DiscardReason;
29302962
public static final field EVENT_PROCESSOR Lio/sentry/clientreport/DiscardReason;
@@ -4432,6 +4464,7 @@ public final class io/sentry/transport/AsyncHttpTransport : io/sentry/transport/
44324464
public fun close ()V
44334465
public fun flush (J)V
44344466
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
4467+
public fun isHealthy ()Z
44354468
public fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
44364469
}
44374470

@@ -4447,6 +4480,7 @@ public abstract interface class io/sentry/transport/ICurrentDateProvider {
44474480
public abstract interface class io/sentry/transport/ITransport : java/io/Closeable {
44484481
public abstract fun flush (J)V
44494482
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
4483+
public fun isHealthy ()Z
44504484
public fun send (Lio/sentry/SentryEnvelope;)V
44514485
public abstract fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
44524486
}
@@ -4481,6 +4515,7 @@ public final class io/sentry/transport/RateLimiter {
44814515
public fun <init> (Lio/sentry/transport/ICurrentDateProvider;Lio/sentry/SentryOptions;)V
44824516
public fun filter (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/SentryEnvelope;
44834517
public fun isActiveForCategory (Lio/sentry/DataCategory;)Z
4518+
public fun isAnyRateLimitActive ()Z
44844519
public fun updateRetryAfterLimits (Ljava/lang/String;Ljava/lang/String;I)V
44854520
}
44864521

sentry/src/main/java/io/sentry/ExternalOptions.java

+14
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public final class ExternalOptions {
4949
private @Nullable List<String> ignoredCheckIns;
5050

5151
private @Nullable Boolean sendModules;
52+
private @Nullable Boolean enableBackpressureHandling;
5253

5354
@SuppressWarnings("unchecked")
5455
public static @NotNull ExternalOptions from(
@@ -131,6 +132,9 @@ public final class ExternalOptions {
131132

132133
options.setIgnoredCheckIns(propertiesProvider.getList("ignored-checkins"));
133134

135+
options.setEnableBackpressureHandling(
136+
propertiesProvider.getBooleanProperty("enable-backpressure-handling"));
137+
134138
for (final String ignoredExceptionType :
135139
propertiesProvider.getList("ignored-exceptions-for-type")) {
136140
try {
@@ -398,4 +402,14 @@ public void setIgnoredCheckIns(final @Nullable List<String> ignoredCheckIns) {
398402
public @Nullable List<String> getIgnoredCheckIns() {
399403
return ignoredCheckIns;
400404
}
405+
406+
@ApiStatus.Experimental
407+
public void setEnableBackpressureHandling(final @Nullable Boolean enableBackpressureHandling) {
408+
this.enableBackpressureHandling = enableBackpressureHandling;
409+
}
410+
411+
@ApiStatus.Experimental
412+
public @Nullable Boolean isEnableBackpressureHandling() {
413+
return enableBackpressureHandling;
414+
}
401415
}

0 commit comments

Comments
 (0)