-
-
Notifications
You must be signed in to change notification settings - Fork 478
fix(android): Treat an unpopulated connection cache as stale (JAVA-717) #6029
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
base: no/java-572-timestamp-timing
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,10 +19,12 @@ | |
| import io.sentry.android.core.AppState; | ||
| import io.sentry.android.core.BuildInfoProvider; | ||
| import io.sentry.android.core.ContextUtils; | ||
| import io.sentry.transport.ICurrentDateProvider; | ||
| import io.sentry.time.Deadline; | ||
| import io.sentry.time.MonotonicTicker; | ||
| import io.sentry.util.AutoClosableReentrantLock; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import org.jetbrains.annotations.ApiStatus; | ||
| import org.jetbrains.annotations.NotNull; | ||
|
|
@@ -41,7 +43,7 @@ public final class AndroidConnectionStatusProvider | |
| private final @NotNull Context context; | ||
| private final @NotNull SentryOptions options; | ||
| private final @NotNull BuildInfoProvider buildInfoProvider; | ||
| private final @NotNull ICurrentDateProvider timeProvider; | ||
| private final @NotNull MonotonicTicker ticker; | ||
| private final @NotNull List<IConnectionStatusObserver> connectionStatusObservers; | ||
| private final @Nullable Handler handler; | ||
| private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); | ||
|
|
@@ -66,29 +68,30 @@ public final class AndroidConnectionStatusProvider | |
|
|
||
| private volatile @Nullable NetworkCapabilities cachedNetworkCapabilities; | ||
| private volatile @Nullable Network currentNetwork; | ||
| private volatile long lastCacheUpdateTime = 0; | ||
| private static final long CACHE_TTL_MS = 2 * 60 * 1000L; // 2 minutes | ||
| private volatile @NotNull Deadline cacheFreshUntil; | ||
| private static final long CACHE_TTL_MINUTES = 2; | ||
| private final @NotNull AtomicBoolean isConnected = new AtomicBoolean(false); | ||
|
|
||
| public AndroidConnectionStatusProvider( | ||
| @NotNull Context context, | ||
| @NotNull SentryOptions options, | ||
| @NotNull BuildInfoProvider buildInfoProvider, | ||
| @NotNull ICurrentDateProvider timeProvider) { | ||
| this(context, options, buildInfoProvider, timeProvider, null); | ||
| @NotNull MonotonicTicker ticker) { | ||
| this(context, options, buildInfoProvider, ticker, null); | ||
| } | ||
|
|
||
| @SuppressLint("InlinedApi") | ||
| public AndroidConnectionStatusProvider( | ||
| @NotNull Context context, | ||
| @NotNull SentryOptions options, | ||
| @NotNull BuildInfoProvider buildInfoProvider, | ||
| @NotNull ICurrentDateProvider timeProvider, | ||
| @NotNull MonotonicTicker ticker, | ||
| @Nullable Handler handler) { | ||
| this.context = ContextUtils.getApplicationContext(context); | ||
| this.options = options; | ||
| this.buildInfoProvider = buildInfoProvider; | ||
| this.timeProvider = timeProvider; | ||
| this.ticker = ticker; | ||
| this.cacheFreshUntil = Deadline.passed(ticker); | ||
| this.handler = handler; | ||
| this.connectionStatusObservers = new ArrayList<>(); | ||
|
|
||
|
|
@@ -231,7 +234,7 @@ private void clearCacheAndNotifyObservers() { | |
| try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { | ||
| cachedNetworkCapabilities = null; | ||
| currentNetwork = null; | ||
| lastCacheUpdateTime = timeProvider.getCurrentTimeMillis(); | ||
| cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); | ||
|
|
||
| options | ||
| .getLogger() | ||
|
|
@@ -362,13 +365,13 @@ private void updateCache(@Nullable NetworkCapabilities networkCapabilities) { | |
| SentryLevel.INFO, | ||
| "No permission (ACCESS_NETWORK_STATE) to check network status."); | ||
| cachedNetworkCapabilities = null; | ||
| lastCacheUpdateTime = timeProvider.getCurrentTimeMillis(); | ||
| cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); | ||
| return; | ||
| } | ||
|
|
||
| if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.M) { | ||
| cachedNetworkCapabilities = null; | ||
| lastCacheUpdateTime = timeProvider.getCurrentTimeMillis(); | ||
| cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -387,7 +390,7 @@ private void updateCache(@Nullable NetworkCapabilities networkCapabilities) { | |
| null; // Clear cached capabilities if connectivity manager is null | ||
| } | ||
| } | ||
| lastCacheUpdateTime = timeProvider.getCurrentTimeMillis(); | ||
| cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); | ||
|
|
||
| options | ||
| .getLogger() | ||
|
|
@@ -400,13 +403,13 @@ private void updateCache(@Nullable NetworkCapabilities networkCapabilities) { | |
| } catch (Throwable t) { | ||
| options.getLogger().log(SentryLevel.WARNING, "Failed to update connection status cache", t); | ||
| cachedNetworkCapabilities = null; | ||
| lastCacheUpdateTime = timeProvider.getCurrentTimeMillis(); | ||
| cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private boolean isCacheValid() { | ||
| return (timeProvider.getCurrentTimeMillis() - lastCacheUpdateTime) < CACHE_TTL_MS; | ||
| return !cacheFreshUntil.hasPassed(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l: We could add a hasNotPassed() method to Deadline, as well. I bet it'll get a lot of use.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its a good point that there are a lot of callers that invert the condition. I don't like methods with negative words in the name. When you invert the condition it becomes harder to reason about. |
||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -459,7 +462,7 @@ private void unregisterNetworkCallback(final boolean clearObservers) { | |
| // Clear cached state | ||
| cachedNetworkCapabilities = null; | ||
| currentNetwork = null; | ||
| lastCacheUpdateTime = 0; | ||
| cacheFreshUntil = Deadline.passed(ticker); | ||
| } | ||
| options.getLogger().log(SentryLevel.DEBUG, "Network callback unregistered"); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing this in action, I wonder if there isn't a better name than Deadline.after()? ("after" makes me think that perhaps we're offsetting the start of the deadline until the time we pass to after())
Thoughts about
until()? I prefer it b/c my mind defaults to thinking that whatever I'm returned is "valid"; a deadline that's passed isn't; anduntil()points to the valid time segment.(I also considered
at(), which reads nicely here, but (I think) mostly because my mind is sneaking in the idea of a clock, and not reading this as a pure duration.)Happy to defer to you...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had it as
inbut that's a reserved keyword in kotlin so it was an awkward API once we get to the kotlin code. I don't see a big enough difference betweenuntilandafter. My reasoning withafteris that the deadline expires after the specified time.We can change it later, but I'd rather start getting some PRs merged. Rebasing is getting tedious with all the stacked PRs.