Skip to content

Commit 02dfd7c

Browse files
authored
Merge 4707bd3 into dcafe87
2 parents dcafe87 + 4707bd3 commit 02dfd7c

36 files changed

+383
-145
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Breaking changes:
1717
- Previously performing a click on the same UI widget twice would keep the existing transaction running, the new behavior now better aligns with other SDKs
1818
- Android only: If global hub mode is enabled, Sentry.getSpan() returns the root span instead of the latest span ([#2855](https://github.com/getsentry/sentry-java/pull/2855))
1919
- Android only: Observe network state to upload any unsent envelopes ([#2910](https://github.com/getsentry/sentry-java/pull/2910))
20+
- Do not try to send and drop cached envelopes when rate-limiting is active ([#2937](https://github.com/getsentry/sentry-java/pull/2937))
2021

2122
### Fixes
2223

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions
4343
options.getEnvelopeReader(),
4444
options.getSerializer(),
4545
logger,
46-
options.getFlushTimeoutMillis());
46+
options.getFlushTimeoutMillis(),
47+
options.getMaxQueueSize());
4748

4849
observer =
4950
new EnvelopeFileObserver(path, outboxSender, logger, options.getFlushTimeoutMillis());

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package io.sentry.android.core;
22

3+
import io.sentry.DataCategory;
34
import io.sentry.IConnectionStatusProvider;
45
import io.sentry.IHub;
56
import io.sentry.Integration;
67
import io.sentry.SendCachedEnvelopeFireAndForgetIntegration;
78
import io.sentry.SentryLevel;
89
import io.sentry.SentryOptions;
10+
import io.sentry.transport.RateLimiter;
911
import io.sentry.util.LazyEvaluator;
1012
import io.sentry.util.Objects;
1113
import java.io.Closeable;
@@ -28,6 +30,7 @@ final class SendCachedEnvelopeIntegration
2830
private @Nullable IConnectionStatusProvider connectionStatusProvider;
2931
private @Nullable IHub hub;
3032
private @Nullable SentryAndroidOptions options;
33+
private @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget sender;
3134

3235
public SendCachedEnvelopeIntegration(
3336
final @NotNull SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory factory,
@@ -54,6 +57,8 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
5457
connectionStatusProvider = options.getConnectionStatusProvider();
5558
connectionStatusProvider.addConnectionStatusObserver(this);
5659

60+
sender = factory.create(hub, options);
61+
5762
sendCachedEnvelopes(hub, this.options);
5863
}
5964

@@ -71,6 +76,7 @@ public void onConnectionStatusChanged(IConnectionStatusProvider.ConnectionStatus
7176
}
7277
}
7378

79+
@SuppressWarnings({"NullAway"})
7480
private synchronized void sendCachedEnvelopes(
7581
final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) {
7682

@@ -81,8 +87,14 @@ private synchronized void sendCachedEnvelopes(
8187
return;
8288
}
8389

84-
final SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget sender =
85-
factory.create(hub, options);
90+
// in case there's rate limiting active, skip processing
91+
final @Nullable RateLimiter rateLimiter = hub.getRateLimiter();
92+
if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) {
93+
options
94+
.getLogger()
95+
.log(SentryLevel.INFO, "SendCachedEnvelopeIntegration, rate limiting active.");
96+
return;
97+
}
8698

8799
if (sender == null) {
88100
options.getLogger().log(SentryLevel.ERROR, "SendCachedEnvelopeIntegration factory is null.");

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import android.net.NetworkCapabilities;
99
import android.os.Build;
1010
import androidx.annotation.NonNull;
11-
import androidx.annotation.RequiresApi;
1211
import io.sentry.IConnectionStatusProvider;
1312
import io.sentry.ILogger;
1413
import io.sentry.SentryLevel;
@@ -63,9 +62,14 @@ public AndroidConnectionStatusProvider(
6362
return getConnectionType(context, logger, buildInfoProvider);
6463
}
6564

66-
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
65+
@SuppressLint("NewApi") // we have an if-check for that down below
6766
@Override
6867
public boolean addConnectionStatusObserver(@NotNull IConnectionStatusObserver observer) {
68+
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP) {
69+
logger.log(SentryLevel.DEBUG, "addConnectionStatusObserver requires Android 5+.");
70+
return false;
71+
}
72+
6973
final ConnectivityManager.NetworkCallback callback =
7074
new ConnectivityManager.NetworkCallback() {
7175
@Override

sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.sentry.protocol.Mechanism
2323
import io.sentry.protocol.SentryId
2424
import io.sentry.protocol.User
2525
import io.sentry.transport.ITransport
26+
import io.sentry.transport.RateLimiter
2627
import org.junit.runner.RunWith
2728
import org.mockito.kotlin.mock
2829
import org.mockito.kotlin.whenever
@@ -63,6 +64,10 @@ class InternalSentrySdkTest {
6364
override fun flush(timeoutMillis: Long) {
6465
// no-op
6566
}
67+
68+
override fun getRateLimiter(): RateLimiter? {
69+
return null
70+
}
6671
}
6772
}
6873
}

sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import io.sentry.ILogger
77
import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget
88
import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory
99
import io.sentry.SentryLevel.DEBUG
10+
import io.sentry.test.ImmediateExecutorService
11+
import io.sentry.transport.RateLimiter
1012
import io.sentry.util.LazyEvaluator
1113
import org.awaitility.kotlin.await
1214
import org.mockito.kotlin.any
@@ -35,8 +37,12 @@ class SendCachedEnvelopeIntegrationTest {
3537
hasStartupCrashMarker: Boolean = false,
3638
hasSender: Boolean = true,
3739
delaySend: Long = 0L,
38-
taskFails: Boolean = false
40+
taskFails: Boolean = false,
41+
useImmediateExecutor: Boolean = false
3942
): SendCachedEnvelopeIntegration {
43+
if (useImmediateExecutor) {
44+
options.executorService = ImmediateExecutorService()
45+
}
4046
options.cacheDirPath = cacheDirPath
4147
options.setLogger(logger)
4248
options.isDebug = true
@@ -144,7 +150,7 @@ class SendCachedEnvelopeIntegrationTest {
144150
)
145151

146152
sut.register(fixture.hub, fixture.options)
147-
verify(fixture.factory, never()).create(any(), any())
153+
verify(fixture.sender, never()).send()
148154
}
149155

150156
@Test
@@ -164,7 +170,7 @@ class SendCachedEnvelopeIntegrationTest {
164170

165171
@Test
166172
fun `whenever network connection status changes, retries sending for relevant statuses`() {
167-
val sut = fixture.getSut(hasStartupCrashMarker = false)
173+
val sut = fixture.getSut(hasStartupCrashMarker = false, useImmediateExecutor = true)
168174

169175
val connectionStatusProvider = mock<IConnectionStatusProvider>()
170176
fixture.options.connectionStatusProvider = connectionStatusProvider
@@ -174,22 +180,36 @@ class SendCachedEnvelopeIntegrationTest {
174180
sut.register(fixture.hub, fixture.options)
175181

176182
// when there's no connection no factory create call should be done
177-
verify(fixture.factory, never()).create(any(), any())
183+
verify(fixture.sender, never()).send()
178184

179185
// but for any other status processing should be triggered
180186
// CONNECTED
181187
whenever(connectionStatusProvider.connectionStatus).thenReturn(ConnectionStatus.CONNECTED)
182188
sut.onConnectionStatusChanged(ConnectionStatus.CONNECTED)
183-
verify(fixture.factory).create(any(), any())
189+
verify(fixture.sender).send()
184190

185191
// UNKNOWN
186192
whenever(connectionStatusProvider.connectionStatus).thenReturn(ConnectionStatus.UNKNOWN)
187193
sut.onConnectionStatusChanged(ConnectionStatus.UNKNOWN)
188-
verify(fixture.factory, times(2)).create(any(), any())
194+
verify(fixture.sender, times(2)).send()
189195

190196
// NO_PERMISSION
191197
whenever(connectionStatusProvider.connectionStatus).thenReturn(ConnectionStatus.NO_PERMISSION)
192198
sut.onConnectionStatusChanged(ConnectionStatus.NO_PERMISSION)
193-
verify(fixture.factory, times(3)).create(any(), any())
199+
verify(fixture.sender, times(3)).send()
200+
}
201+
202+
@Test
203+
fun `when rate limiter is active, does not send envelopes`() {
204+
val sut = fixture.getSut(hasStartupCrashMarker = false)
205+
val rateLimiter = mock<RateLimiter> {
206+
whenever(mock.isActiveForCategory(any())).thenReturn(true)
207+
}
208+
whenever(fixture.hub.rateLimiter).thenReturn(rateLimiter)
209+
210+
sut.register(fixture.hub, fixture.options)
211+
212+
// no factory call should be done if there's rate limiting active
213+
verify(fixture.sender, never()).send()
194214
}
195215
}

sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import io.sentry.TraceContext
2121
import io.sentry.UserFeedback
2222
import io.sentry.protocol.SentryId
2323
import io.sentry.protocol.SentryTransaction
24+
import io.sentry.transport.RateLimiter
2425
import org.junit.runner.RunWith
2526
import org.mockito.kotlin.mock
2627
import org.robolectric.annotation.Config
@@ -162,5 +163,9 @@ class SessionTrackingIntegrationTest {
162163
): SentryId {
163164
TODO("Not yet implemented")
164165
}
166+
167+
override fun getRateLimiter(): RateLimiter? {
168+
TODO("Not yet implemented")
169+
}
165170
}
166171
}

sentry-apache-http-client-5/api/sentry-apache-http-client-5.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public final class io/sentry/transport/apache/ApacheHttpClientTransport : io/sen
22
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/RequestDetails;Lorg/apache/hc/client5/http/impl/async/CloseableHttpAsyncClient;Lio/sentry/transport/RateLimiter;)V
33
public fun close ()V
44
public fun flush (J)V
5+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
56
public fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
67
}
78

sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ public void flush(long timeoutMillis) {
189189
}
190190
}
191191

192+
@Override
193+
public @NotNull RateLimiter getRateLimiter() {
194+
return rateLimiter;
195+
}
196+
192197
@Override
193198
public void close() throws IOException {
194199
options.getLogger().log(DEBUG, "Shutting down");

sentry/api/sentry.api

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public final class io/sentry/EnvelopeReader : io/sentry/IEnvelopeReader {
239239
}
240240

241241
public final class io/sentry/EnvelopeSender : io/sentry/IEnvelopeSender {
242-
public fun <init> (Lio/sentry/IHub;Lio/sentry/ISerializer;Lio/sentry/ILogger;JLio/sentry/cache/IEnvelopeCache;)V
242+
public fun <init> (Lio/sentry/IHub;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V
243243
public synthetic fun processDirectory (Ljava/io/File;)V
244244
public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V
245245
}
@@ -373,6 +373,7 @@ public final class io/sentry/Hub : io/sentry/IHub {
373373
public fun getBaggage ()Lio/sentry/BaggageHeader;
374374
public fun getLastEventId ()Lio/sentry/protocol/SentryId;
375375
public fun getOptions ()Lio/sentry/SentryOptions;
376+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
376377
public fun getSpan ()Lio/sentry/ISpan;
377378
public fun getTraceparent ()Lio/sentry/SentryTraceHeader;
378379
public fun getTransaction ()Lio/sentry/ITransaction;
@@ -422,6 +423,7 @@ public final class io/sentry/HubAdapter : io/sentry/IHub {
422423
public static fun getInstance ()Lio/sentry/HubAdapter;
423424
public fun getLastEventId ()Lio/sentry/protocol/SentryId;
424425
public fun getOptions ()Lio/sentry/SentryOptions;
426+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
425427
public fun getSpan ()Lio/sentry/ISpan;
426428
public fun getTraceparent ()Lio/sentry/SentryTraceHeader;
427429
public fun getTransaction ()Lio/sentry/ITransaction;
@@ -515,6 +517,7 @@ public abstract interface class io/sentry/IHub {
515517
public abstract fun getBaggage ()Lio/sentry/BaggageHeader;
516518
public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId;
517519
public abstract fun getOptions ()Lio/sentry/SentryOptions;
520+
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
518521
public abstract fun getSpan ()Lio/sentry/ISpan;
519522
public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader;
520523
public abstract fun getTransaction ()Lio/sentry/ITransaction;
@@ -608,6 +611,7 @@ public abstract interface class io/sentry/ISentryClient {
608611
public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V
609612
public abstract fun close ()V
610613
public abstract fun flush (J)V
614+
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
611615
public abstract fun isEnabled ()Z
612616
}
613617

@@ -910,6 +914,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub {
910914
public static fun getInstance ()Lio/sentry/NoOpHub;
911915
public fun getLastEventId ()Lio/sentry/protocol/SentryId;
912916
public fun getOptions ()Lio/sentry/SentryOptions;
917+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
913918
public fun getSpan ()Lio/sentry/ISpan;
914919
public fun getTraceparent ()Lio/sentry/SentryTraceHeader;
915920
public fun getTransaction ()Lio/sentry/ITransaction;
@@ -1070,7 +1075,7 @@ public final class io/sentry/OptionsContainer {
10701075
}
10711076

10721077
public final class io/sentry/OutboxSender : io/sentry/IEnvelopeSender {
1073-
public fun <init> (Lio/sentry/IHub;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;J)V
1078+
public fun <init> (Lio/sentry/IHub;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V
10741079
public synthetic fun processDirectory (Ljava/io/File;)V
10751080
public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V
10761081
}
@@ -1516,6 +1521,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
15161521
public fun captureUserFeedback (Lio/sentry/UserFeedback;)V
15171522
public fun close ()V
15181523
public fun flush (J)V
1524+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
15191525
public fun isEnabled ()Z
15201526
}
15211527

@@ -2472,7 +2478,6 @@ public final class io/sentry/TypeCheckHint {
24722478
public static final field OKHTTP_RESPONSE Ljava/lang/String;
24732479
public static final field OPEN_FEIGN_REQUEST Ljava/lang/String;
24742480
public static final field OPEN_FEIGN_RESPONSE Ljava/lang/String;
2475-
public static final field SENTRY_CACHED_ENVELOPE_FILE_PATH Ljava/lang/String;
24762481
public static final field SENTRY_DART_SDK_NAME Ljava/lang/String;
24772482
public static final field SENTRY_DOTNET_SDK_NAME Ljava/lang/String;
24782483
public static final field SENTRY_EVENT_DROP_REASON Ljava/lang/String;
@@ -2761,6 +2766,10 @@ public abstract interface class io/sentry/hints/DiskFlushNotification {
27612766
public abstract fun markFlushed ()V
27622767
}
27632768

2769+
public abstract interface class io/sentry/hints/Enqueable {
2770+
public abstract fun markEnqueued ()V
2771+
}
2772+
27642773
public final class io/sentry/hints/EventDropReason : java/lang/Enum {
27652774
public static final field MULTITHREADED_DEDUPLICATION Lio/sentry/hints/EventDropReason;
27662775
public static fun valueOf (Ljava/lang/String;)Lio/sentry/hints/EventDropReason;
@@ -4138,6 +4147,7 @@ public final class io/sentry/transport/AsyncHttpTransport : io/sentry/transport/
41384147
public fun <init> (Lio/sentry/transport/QueuedThreadPoolExecutor;Lio/sentry/SentryOptions;Lio/sentry/transport/RateLimiter;Lio/sentry/transport/ITransportGate;Lio/sentry/transport/HttpConnection;)V
41394148
public fun close ()V
41404149
public fun flush (J)V
4150+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
41414151
public fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
41424152
}
41434153

@@ -4152,6 +4162,7 @@ public abstract interface class io/sentry/transport/ICurrentDateProvider {
41524162

41534163
public abstract interface class io/sentry/transport/ITransport : java/io/Closeable {
41544164
public abstract fun flush (J)V
4165+
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
41554166
public fun send (Lio/sentry/SentryEnvelope;)V
41564167
public abstract fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
41574168
}
@@ -4173,6 +4184,7 @@ public final class io/sentry/transport/NoOpTransport : io/sentry/transport/ITran
41734184
public fun close ()V
41744185
public fun flush (J)V
41754186
public static fun getInstance ()Lio/sentry/transport/NoOpTransport;
4187+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
41764188
public fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
41774189
}
41784190

@@ -4185,7 +4197,7 @@ public final class io/sentry/transport/RateLimiter {
41854197
public fun <init> (Lio/sentry/SentryOptions;)V
41864198
public fun <init> (Lio/sentry/transport/ICurrentDateProvider;Lio/sentry/SentryOptions;)V
41874199
public fun filter (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/SentryEnvelope;
4188-
public fun getRateLimitedUntilFor (Ljava/lang/String;)Ljava/util/Date;
4200+
public fun isActiveForCategory (Lio/sentry/DataCategory;)Z
41894201
public fun updateRetryAfterLimits (Ljava/lang/String;Ljava/lang/String;I)V
41904202
}
41914203

@@ -4203,6 +4215,7 @@ public final class io/sentry/transport/StdoutTransport : io/sentry/transport/ITr
42034215
public fun <init> (Lio/sentry/ISerializer;)V
42044216
public fun close ()V
42054217
public fun flush (J)V
4218+
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
42064219
public fun send (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
42074220
}
42084221

0 commit comments

Comments
 (0)