-
Notifications
You must be signed in to change notification settings - Fork 7
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
Use remote limit as ceiling for network call limits and apply consistently #78
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…ently
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,11 +40,17 @@ | |
|
||
private val networkCallCache = CacheableValue<List<NetworkCallV2>> { callsStorageLastUpdate.get() } | ||
|
||
private val domainSettings = ConcurrentHashMap<String, DomainSettings>() | ||
private val domainSetting = ConcurrentHashMap<String, DomainSettings>() | ||
|
||
private val callsPerDomain = hashMapOf<String, DomainCount>() | ||
|
||
private val ipAddressCount = AtomicInteger(0) | ||
private val ipAddressNetworkCallCount = AtomicInteger(0) | ||
|
||
private val untrackedNetworkCallCount = AtomicInteger(0) | ||
|
||
private var defaultPerDomainCallLimit = configService.networkBehavior.getNetworkCaptureLimit() | ||
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. We want to maintain the same limits for each session, so we get it at init time, and update when we reset the service when the session ends. Previously, this is pulled every time at run time, and we can get into inconsistent states in the middle of a session if new remote limits are fetched. |
||
|
||
private var domainSuffixCallLimits = configService.networkBehavior.getNetworkCallLimitsPerDomain() | ||
|
||
override fun getNetworkCallsForSession(): NetworkSessionV2 { | ||
val calls = networkCallCache.value { | ||
|
@@ -68,8 +74,6 @@ | |
logger.logError(msg, IllegalStateException(msg), true) | ||
} | ||
|
||
// clear calls per domain and session network calls lists before be used by the next session | ||
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. Lol is one line is probably the most important change in the PR. We were resetting this every time a session payload is built, which effectively nerfs the limit. 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. Periodic session caching strikes again 💥 |
||
callsPerDomain.clear() | ||
return NetworkSessionV2(calls, overLimit) | ||
} | ||
|
||
|
@@ -114,7 +118,6 @@ | |
} | ||
|
||
processNetworkCall(callId, networkCall) | ||
storeSettings(url) | ||
} | ||
|
||
override fun logNetworkError( | ||
|
@@ -156,7 +159,6 @@ | |
) | ||
} | ||
processNetworkCall(callId, networkCall) | ||
storeSettings(url) | ||
} | ||
|
||
/** | ||
|
@@ -166,88 +168,66 @@ | |
* @param networkCall that is going to be captured | ||
*/ | ||
private fun processNetworkCall(callId: String, networkCall: NetworkCallV2) { | ||
// Get the domain, if it can be successfully parsed | ||
// Get the domain, if it can be successfully parsed. If not, don't log this call. | ||
val domain = networkCall.url?.let { | ||
getDomain(it) | ||
} | ||
|
||
if (domain == null) { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "Domain is not present") | ||
return | ||
} | ||
|
||
logger.logDeveloper("EmbraceNetworkLoggingService", "Domain: $domain") | ||
} ?: return | ||
|
||
if (isIpAddress(domain)) { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "Domain is an ip address") | ||
val captureLimit = configService.networkBehavior.getNetworkCaptureLimit() | ||
|
||
if (ipAddressCount.getAndIncrement() < captureLimit) { | ||
// only capture if the ipAddressCount has not exceeded defaultLimit | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "capturing network call") | ||
if (ipAddressNetworkCallCount.getAndIncrement() < defaultPerDomainCallLimit) { | ||
storeNetworkCall(callId, networkCall) | ||
} else { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "capture limit exceeded") | ||
} | ||
return | ||
} else if (!domainSetting.containsKey(domain)) { | ||
createLimitForDomain(domain) | ||
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. Added the fetching of the domain limits here to run before we record a network request. It was doing it after and I have no idea why. |
||
} | ||
|
||
val settings = domainSettings[domain] | ||
val settings = domainSetting[domain] | ||
if (settings == null) { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "no domain settings") | ||
storeNetworkCall(callId, networkCall) | ||
// Not sure how this is possible, but in case it is, limit logged logs where we can't figure out the settings to apply | ||
if (untrackedNetworkCallCount.getAndIncrement() < defaultPerDomainCallLimit) { | ||
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. This should never happen so I don't know how to test it, but I've added code to catch and limit this in case it happens - it wasn't limiting that before. Another option is just to ditch it like if we can't find a domain (which probably prevents this block from every running tbh), but this seems safer and who knows what funny refactoring in the future will bring. Safety first I guess. |
||
storeNetworkCall(callId, networkCall) | ||
Check warning on line 189 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt Codecov / codecov/patchembrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt#L189
|
||
} | ||
} else { | ||
val suffix = settings.suffix | ||
val limit = settings.limit | ||
var count = callsPerDomain[suffix] | ||
var countPerSuffix = callsPerDomain[suffix] | ||
|
||
if (count == null) { | ||
count = DomainCount(1, limit) | ||
if (countPerSuffix == null) { | ||
countPerSuffix = DomainCount(0, limit) | ||
} | ||
|
||
// Exclude if the network call exceeds the limit | ||
if (count.requestCount < limit) { | ||
if (countPerSuffix.requestCount < limit) { | ||
storeNetworkCall(callId, networkCall) | ||
} else { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "capture limit exceeded") | ||
} | ||
|
||
// Track the number of calls for each domain (or configured suffix) | ||
suffix?.let { | ||
callsPerDomain[it] = DomainCount(count.requestCount + 1, limit) | ||
callsPerDomain[it] = DomainCount(countPerSuffix.requestCount + 1, limit) | ||
logger.logDeveloper( | ||
"EmbraceNetworkLoggingService", | ||
"Call per domain $domain ${count.requestCount + 1}" | ||
"Call per domain $domain ${countPerSuffix.requestCount + 1}" | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun storeSettings(url: String) { | ||
private fun createLimitForDomain(domain: String) { | ||
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. The old implementation is really wasteful in that it rebuild the domain limits map at every call, often unnecessarily. So we build and cache it for reuse at init time and redo it when the session ends instead. |
||
try { | ||
val mergedLimits = configService.networkBehavior.getNetworkCallLimitsPerDomain() | ||
|
||
val domain = getDomain(url) | ||
if (domain == null) { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "Domain not present") | ||
return | ||
} | ||
if (domainSettings.containsKey(domain)) { | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "No settings for $domain") | ||
return | ||
} | ||
|
||
for ((key, value) in mergedLimits) { | ||
for ((key, value) in domainSuffixCallLimits) { | ||
if (domain.endsWith(key)) { | ||
domainSettings[domain] = DomainSettings(value, key) | ||
return | ||
domainSetting[domain] = DomainSettings(value, key) | ||
} | ||
} | ||
|
||
val defaultLimit = configService.networkBehavior.getNetworkCaptureLimit() | ||
domainSettings[domain] = DomainSettings(defaultLimit, domain) | ||
if (!domainSetting.containsKey(domain)) { | ||
domainSetting[domain] = DomainSettings(defaultPerDomainCallLimit, domain) | ||
} | ||
} catch (ex: Exception) { | ||
logger.logDebug("Failed to determine limits for URL: $url", ex) | ||
logger.logDebug("Failed to determine limits for domain: $domain", ex) | ||
Check warning on line 230 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt Codecov / codecov/patchembrace-android-sdk/src/main/java/io/embrace/android/embracesdk/network/logging/EmbraceNetworkLoggingService.kt#L230
|
||
} | ||
} | ||
|
||
|
@@ -266,11 +246,13 @@ | |
} | ||
|
||
override fun cleanCollections() { | ||
domainSettings.clear() | ||
domainSetting.clear() | ||
callsPerDomain.clear() | ||
ipAddressNetworkCallCount.set(0) | ||
untrackedNetworkCallCount.set(0) | ||
clearNetworkCalls() | ||
// reset counters | ||
ipAddressCount.set(0) | ||
logger.logDeveloper("EmbraceNetworkLoggingService", "Collections cleaned") | ||
// re-fetch limits in case they changed since they last time they were fetched | ||
defaultPerDomainCallLimit = configService.networkBehavior.getNetworkCaptureLimit() | ||
domainSuffixCallLimits = configService.networkBehavior.getNetworkCallLimitsPerDomain() | ||
} | ||
} |
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.
The algo is:
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.
Consider leaving as a comment in the code?
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.
Good idea