-
Notifications
You must be signed in to change notification settings - Fork 135
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
RUM-8042 Replace Upload Quality with Batch Blocked telemetry #2230
base: develop
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 |
---|---|---|
|
@@ -73,20 +73,23 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
let context = contextProvider.read() | ||
let blockersForUpload = uploadConditions.blockersForUpload(with: context) | ||
let isSystemReady = blockersForUpload.isEmpty | ||
let files = isSystemReady ? fileReader.readFiles(limit: maxBatchesPerUpload) : nil | ||
if let files = files, !files.isEmpty { | ||
let files = fileReader.readFiles(limit: maxBatchesPerUpload) | ||
|
||
if !files.isEmpty && isSystemReady { | ||
Comment on lines
+76
to
+78
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. Can we try to asses if this change might have a negative performance impact ? 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 |
||
DD.logger.debug("⏳ (\(self.featureName)) Uploading batches...") | ||
self.backgroundTaskCoordinator?.beginBackgroundTask() | ||
self.uploadFile(from: files.reversed(), context: context) | ||
sendUploadCycleMetric() | ||
} else { | ||
let batchLabel = files?.isEmpty == false ? "YES" : (isSystemReady ? "NO" : "NOT CHECKED") | ||
let batchLabel = files.isEmpty ? "NO" : "YES" | ||
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. I imagine the answer is no, but does not sending "NOT CHECKED" anymore have any consequences on our metrics? 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. No it doesn't, it was just for console printing. The drawback of this change is that we will check for batches even if the upload is blocked, but the impact is minimal as we only list the files. |
||
DD.logger.debug("💡 (\(self.featureName)) No upload. Batch to upload: \(batchLabel), System conditions: \(blockersForUpload.description)") | ||
self.delay.increase() | ||
self.backgroundTaskCoordinator?.endBackgroundTask() | ||
self.scheduleNextCycle() | ||
sendUploadQualityMetric(blockers: blockersForUpload) | ||
sendBatchBlockedMetric(blockers: blockersForUpload, batchCount: files.count) | ||
} | ||
} | ||
|
||
self.readWork = readWorkItem | ||
|
||
// Start sending batches immediately after initialization: | ||
|
@@ -106,6 +109,7 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
return | ||
} | ||
|
||
let filesCount = files.count | ||
var files = files | ||
guard let file = files.popLast() else { | ||
self.scheduleNextCycle() | ||
|
@@ -121,12 +125,12 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
) | ||
|
||
previousUploadStatus = uploadStatus | ||
sendUploadQualityMetric(status: uploadStatus) | ||
|
||
if uploadStatus.needsRetry { | ||
DD.logger.debug(" → (\(self.featureName)) not delivered, will be retransmitted: \(uploadStatus.userDebugDescription)") | ||
self.delay.increase() | ||
self.scheduleNextCycle() | ||
sendBatchBlockedMetric(status: uploadStatus, batchCount: filesCount) | ||
return | ||
} | ||
|
||
|
@@ -147,8 +151,7 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
previousUploadStatus = nil | ||
|
||
if let error = uploadStatus.error { | ||
// Throw to report the request error accordingly | ||
throw error | ||
throw error // Throw to report the request error accordingly | ||
} | ||
} catch DataUploadError.httpError(statusCode: .unauthorized), DataUploadError.httpError(statusCode: .forbidden) { | ||
DD.logger.error("⚠️ Make sure that the provided token still exists and you're targeting the relevant Datadog site.") | ||
|
@@ -166,7 +169,6 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
self.fileReader.markBatchAsRead(batch, reason: .invalid) | ||
previousUploadStatus = nil | ||
self.telemetry.error("Failed to initiate '\(self.featureName)' data upload", error: error) | ||
sendUploadQualityMetric(failure: "invalid") | ||
} | ||
} | ||
|
||
|
@@ -235,55 +237,57 @@ internal class DataUploadWorker: DataUploadWorkerType { | |
} | ||
} | ||
|
||
private func sendUploadQualityMetric(blockers: [DataUploadConditions.Blocker]) { | ||
guard !blockers.isEmpty else { | ||
return sendUploadQualityMetric() | ||
} | ||
|
||
sendUploadQualityMetric( | ||
failure: "blocker", | ||
blockers: blockers.map { | ||
switch $0 { | ||
case .battery: return "low_battery" | ||
case .lowPowerModeOn: return "lpm" | ||
case .networkReachability: return "offline" | ||
} | ||
} | ||
private func sendUploadCycleMetric() { | ||
telemetry.metric( | ||
name: UploadCycleMetric.name, | ||
attributes: [UploadCycleMetric.track: featureName] | ||
) | ||
} | ||
|
||
private func sendUploadQualityMetric(status: DataUploadStatus) { | ||
guard let error = status.error else { | ||
return sendUploadQualityMetric() | ||
private func sendBatchBlockedMetric(blockers: [DataUploadConditions.Blocker], batchCount: Int) { | ||
guard !blockers.isEmpty else { | ||
return | ||
} | ||
|
||
sendUploadQualityMetric( | ||
failure: { | ||
switch error { | ||
case let .httpError(code): return "\(code)" | ||
case let .networkError(error): return "\(error.code)" | ||
} | ||
}() | ||
) | ||
} | ||
|
||
private func sendUploadQualityMetric() { | ||
telemetry.metric( | ||
name: UploadQualityMetric.name, | ||
name: BatchBlockedMetric.name, | ||
attributes: [ | ||
UploadQualityMetric.track: featureName | ||
] | ||
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue, | ||
BatchMetric.trackKey: featureName, | ||
BatchBlockedMetric.uploaderDelayKey: delay.current, | ||
BatchBlockedMetric.batchCount: batchCount, | ||
BatchBlockedMetric.blockers: blockers.map { | ||
switch $0 { | ||
case .battery: return "low_battery" | ||
case .lowPowerModeOn: return "lpm" | ||
case .networkReachability: return "offline" | ||
} | ||
} | ||
], | ||
sampleRate: BatchBlockedMetric.sampleRate | ||
) | ||
} | ||
|
||
private func sendUploadQualityMetric(failure: String, blockers: [String] = []) { | ||
private func sendBatchBlockedMetric(status: DataUploadStatus, batchCount: Int) { | ||
guard let error = status.error else { | ||
return | ||
} | ||
|
||
telemetry.metric( | ||
name: UploadQualityMetric.name, | ||
name: BatchBlockedMetric.name, | ||
attributes: [ | ||
UploadQualityMetric.track: featureName, | ||
UploadQualityMetric.failure: failure, | ||
UploadQualityMetric.blockers: blockers | ||
] | ||
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue, | ||
BatchMetric.trackKey: featureName, | ||
BatchBlockedMetric.uploaderDelayKey: delay.current, | ||
BatchBlockedMetric.batchCount: batchCount, | ||
BatchBlockedMetric.failure: { | ||
switch error { | ||
case let .httpError(code): return "intake-code-\(code.rawValue)" | ||
case let .networkError(error): return "network-code-\(error.code)" | ||
} | ||
}() | ||
], | ||
sampleRate: BatchBlockedMetric.sampleRate | ||
) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -130,3 +130,23 @@ internal enum BatchClosedMetric { | |
/// If the batch was closed by core or after new batch was forced by the feature. | ||
static let forcedNewKey = "forced_new" | ||
} | ||
|
||
/// Definition of "Batch Blocked" telemetry. | ||
internal enum BatchBlockedMetric { | ||
/// The name of this metric, included in telemetry log. | ||
/// Note: the "[Mobile Metric]" prefix is added when sending this telemetry in RUM. | ||
static let name = "Batch Blocked" | ||
/// Metric type value. | ||
static let typeValue = "batch blocked" | ||
/// The sample rate for this metric. | ||
/// It is applied in addition to the telemetry sample rate (20% by default). | ||
static let sampleRate: Float = 1.5 // 1.5% | ||
/// The key for uploader's current delay. | ||
static let uploaderDelayKey = "uploader_delay.current" | ||
/// The key for count of bacthes being blocked. | ||
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. nit: The key for the number of batches being blocked. |
||
static let batchCount = "batch_count" | ||
|
||
/// List of upload blockers | ||
static let blockers = "blockers" | ||
static let failure = "failure" | ||
} |
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.
If I read this correctly, we use to send
sendUploadQualityMetric
both in cases of success and failure. Now we sendsendUploadCycleMetric
before every upload, and track failures withsendBatchBlockedMetric
. So we don't explicitly send a telemetry in case of success. I imagine we can still graph the success rate, but is this intentional?