RUM-16394 Pt.2: Implement QuotaChecker for profiling quota HTTP check#3496
RUM-16394 Pt.2: Implement QuotaChecker for profiling quota HTTP check#3496ambushwork wants to merge 1 commit into
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Implements a profiling quota HTTP check component (QuotaChecker) and updates the quota result model to use a typed QuotaReason enum, with comprehensive unit tests to validate request building, response parsing, error handling, and deduplication behavior.
Changes:
- Add
QuotaCheckerto perform async quota checks against the quota endpoint and emitQuotaResult. - Introduce
QuotaReasonenum and migrateQuotaResult.reasonfromStringtoQuotaReason(including a newQUOTA_EXCEEDEDpredefined result). - Add/extend unit tests covering parsing, HTTP errors, request construction, deduplication/reset, last-result behavior, and custom-endpoint bypass.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/quota/QuotaChecker.kt | New async HTTP quota checker, parsing, and telemetry logging. |
| features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/quota/QuotaReason.kt | New enum encapsulating quota reasons and raw backend values. |
| features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/quota/QuotaResult.kt | Switch reason to QuotaReason and add QUOTA_EXCEEDED constant. |
| features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaCheckerTest.kt | New test suite validating QuotaChecker behavior end-to-end with mocked OkHttp. |
| features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/quota/QuotaResultTest.kt | Update tests for QuotaReason-based QuotaResult. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
146ea0f to
972ded7
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feature/continuous-profiling #3496 +/- ##
================================================================
+ Coverage 72.41% 72.43% +0.02%
================================================================
Files 980 982 +2
Lines 36268 36356 +88
Branches 6037 6049 +12
================================================================
+ Hits 26261 26333 +72
- Misses 8361 8363 +2
- Partials 1646 1660 +14
🚀 New features to boost your workflow:
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 972ded7447
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
972ded7 to
e5d4bc1
Compare
e5d4bc1 to
fdf7907
Compare
fdf7907 to
4bd33a8
Compare
4bd33a8 to
0152785
Compare
|
|
||
| @Suppress("UnsafeThirdPartyFunctionCall") // Wrapped in the try-catch | ||
| private fun buildRequest(sessionId: String, datadogContext: DatadogContext): Request { | ||
| val intakeHost = datadogContext.site.intakeEndpoint.removePrefix("https://") |
There was a problem hiding this comment.
because the quota endpoint is https://quota.$intakeHostName and intakeEndpoint is https://$intakeHostName, so we need to remove the prefix to extract the intakeHostName here.
| .getJSONObject(JSON_KEY_DATA) | ||
| .getJSONObject(JSON_KEY_ATTRIBUTES) | ||
| val admitted = attrs.optBoolean(JSON_KEY_ADMITTED, true) | ||
| val rawReason = attrs.optString(JSON_KEY_REASON).ifEmpty { null } |
There was a problem hiding this comment.
| val rawReason = attrs.optString(JSON_KEY_REASON).ifEmpty { null } | |
| val rawReason = attrs.optString(JSON_KEY_REASON) |
default fallback is empty string and rawReason is used only in the parseReason, both null and empty string yield to the same branch there.
| internal const val LOG_UNEXPECTED_ERROR = "Quota check unexpected error: %s" | ||
| internal const val LOG_QUOTA_DENIED = "Profiling quota denied: reason=%s" | ||
|
|
||
| internal const val QUOTA_URL_TEMPLATE = "https://quota.%s/api/v2/profiling/quota?session_id=%s" |
There was a problem hiding this comment.
to confirm that this is deployed in all DCs
| private set | ||
|
|
||
| fun checkAsync(sessionId: String, datadogContext: DatadogContext) { | ||
| if (hasCustomEndpoint) { |
There was a problem hiding this comment.
what is the use-case for that, who can set it?
There was a problem hiding this comment.
This is mentioned by @simaoseica-dd before, when user uses custom endpoint, we shouldn't check quota API
| var lastResult: QuotaResult? = null | ||
| private set | ||
|
|
||
| fun checkAsync(sessionId: String, datadogContext: DatadogContext) { |
There was a problem hiding this comment.
what will be the logic behind calling it? for example, we made a call, but got IO exception because there is no network. Obviously, we need to repeat the call, but a) when; b) with back-off?
There was a problem hiding this comment.
With current logic, we make a call only when the session is renewed, and we don't retry if there is a network error, because the network error is a fail open according to the browser profiling logic, which can be changed later ofc.
There was a problem hiding this comment.
I have a concern that browser logic regarding network errors can be be applied to mobile. The fair share of Browser SDK is on the desktop, there is always a connectivity there. Of course mobile browser can be used as well, but once again it should be a connectivity when page is loaded (and probably RUM session is started at this point).
Also Browser profiling requires a document policy sent by the server https://developer.mozilla.org/en-US/docs/Web/API/JS_Self-Profiling_API#security_requirements, this also implies having a connectivity.
| } | ||
| val previousId = lastSessionId.getAndSet(sessionId) | ||
| if (previousId == sessionId) return // same session: in-flight check (if any) is still valid | ||
| pendingFuture.getAndSet(null)?.cancel(true) |
There was a problem hiding this comment.
nb: if it is BackPressureExecutorService or LoggingScheduledThreadPoolExecutor, .cancel will trigger error log entry
There was a problem hiding this comment.
The plan is to use SingleThreadScheduledExecutor to avoid this.
0152785 to
55fd32d
Compare
What does this PR do?
Implement QuotaChecker for profiling quota HTTP check
Motivation
RUM-16394
Review checklist (to be filled by reviewers)