Skip to content

Commit f51cb4c

Browse files
committed
RUM-8042 Replace Upload Quality with Batch Blocked telemetry
1 parent 15adef7 commit f51cb4c

File tree

11 files changed

+123
-162
lines changed

11 files changed

+123
-162
lines changed

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -919,8 +919,8 @@
919919
D22743E729DEB953001A7EF9 /* UIApplicationSwizzlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61410166251A661D00E3C2D9 /* UIApplicationSwizzlerTests.swift */; };
920920
D22743EB29DEC9E6001A7EF9 /* Casting+RUM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61411B0F24EC15AC0012EAB2 /* Casting+RUM.swift */; };
921921
D22743EC29DEC9E6001A7EF9 /* Casting+RUM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61411B0F24EC15AC0012EAB2 /* Casting+RUM.swift */; };
922-
D22789362D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */; };
923-
D22789372D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */; };
922+
D22789362D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */; };
923+
D22789372D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */; };
924924
D227A0A42C7622EA00C83324 /* BenchmarkProfiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */; };
925925
D227A0A52C7622EA00C83324 /* BenchmarkProfiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */; };
926926
D22C5BC82A98A0B20024CC1F /* Baggages.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22C5BC52A989D130024CC1F /* Baggages.swift */; };
@@ -2871,7 +2871,7 @@
28712871
D2216EC22A96632F00ADAEC8 /* FeatureBaggageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureBaggageTests.swift; sourceTree = "<group>"; };
28722872
D22442C42CA301DA002E71E4 /* UIColor+SessionReplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+SessionReplay.swift"; sourceTree = "<group>"; };
28732873
D224430C29E95D6600274EC7 /* CrashReportReceiverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrashReportReceiverTests.swift; sourceTree = "<group>"; };
2874-
D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadQualityMetric.swift; sourceTree = "<group>"; };
2874+
D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadCycleMetric.swift; sourceTree = "<group>"; };
28752875
D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BenchmarkProfiler.swift; sourceTree = "<group>"; };
28762876
D22C1F5B271484B400922024 /* LogEventMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEventMapper.swift; sourceTree = "<group>"; };
28772877
D22C5BC52A989D130024CC1F /* Baggages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Baggages.swift; sourceTree = "<group>"; };
@@ -5014,7 +5014,7 @@
50145014
children = (
50155015
6174D60B2BFDDEDF00EC7469 /* SDKMetricFields.swift */,
50165016
A7FA98CD2BA1A6930018D6B5 /* MethodCalledMetric.swift */,
5017-
D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */,
5017+
D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */,
50185018
);
50195019
path = SDKMetrics;
50205020
sourceTree = "<group>";
@@ -8528,7 +8528,7 @@
85288528
D2160CA229C0DE5700FAA9A5 /* NetworkInstrumentationFeature.swift in Sources */,
85298529
D2EBEE1F29BA160F00B15732 /* HTTPHeadersReader.swift in Sources */,
85308530
E2AA55E72C32C6D9002FEF28 /* ApplicationNotifications.swift in Sources */,
8531-
D22789372D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */,
8531+
D22789372D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */,
85328532
D263BCAF29DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */,
85338533
D23039E7298D5236001A1FA3 /* NetworkConnectionInfo.swift in Sources */,
85348534
D23039E9298D5236001A1FA3 /* TrackingConsent.swift in Sources */,
@@ -9553,7 +9553,7 @@
95539553
D2160CA329C0DE5700FAA9A5 /* NetworkInstrumentationFeature.swift in Sources */,
95549554
D2EBEE2D29BA161100B15732 /* HTTPHeadersReader.swift in Sources */,
95559555
E2AA55E82C32C6D9002FEF28 /* ApplicationNotifications.swift in Sources */,
9556-
D22789362D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */,
9556+
D22789362D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */,
95579557
D263BCB029DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */,
95589558
D2DA2359298D57AA00C6C7E6 /* NetworkConnectionInfo.swift in Sources */,
95599559
D2DA235A298D57AA00C6C7E6 /* TrackingConsent.swift in Sources */,

DatadogCore/Sources/Core/Upload/DataUploadWorker.swift

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ internal class DataUploadWorker: DataUploadWorkerType {
7878
DD.logger.debug("⏳ (\(self.featureName)) Uploading batches...")
7979
self.backgroundTaskCoordinator?.beginBackgroundTask()
8080
self.uploadFile(from: files.reversed(), context: context)
81+
sendUploadCycleMetric()
8182
} else {
8283
let batchLabel = files?.isEmpty == false ? "YES" : (isSystemReady ? "NO" : "NOT CHECKED")
8384
DD.logger.debug("💡 (\(self.featureName)) No upload. Batch to upload: \(batchLabel), System conditions: \(blockersForUpload.description)")
8485
self.delay.increase()
8586
self.backgroundTaskCoordinator?.endBackgroundTask()
8687
self.scheduleNextCycle()
87-
sendUploadQualityMetric(blockers: blockersForUpload)
88+
sendBatchBlockedMetric(blockers: blockersForUpload)
8889
}
8990
}
9091
self.readWork = readWorkItem
@@ -121,7 +122,6 @@ internal class DataUploadWorker: DataUploadWorkerType {
121122
)
122123

123124
previousUploadStatus = uploadStatus
124-
sendUploadQualityMetric(status: uploadStatus)
125125

126126
if uploadStatus.needsRetry {
127127
DD.logger.debug(" → (\(self.featureName)) not delivered, will be retransmitted: \(uploadStatus.userDebugDescription)")
@@ -143,8 +143,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
143143
previousUploadStatus = nil
144144

145145
if let error = uploadStatus.error {
146-
// Throw to report the request error accordingly
147-
throw error
146+
sendBatchBlockedMetric(error: error)
147+
throw error // Throw to report the request error accordingly
148148
}
149149
} catch DataUploadError.httpError(statusCode: .unauthorized), DataUploadError.httpError(statusCode: .forbidden) {
150150
DD.logger.error("⚠️ Make sure that the provided token still exists and you're targeting the relevant Datadog site.")
@@ -162,7 +162,6 @@ internal class DataUploadWorker: DataUploadWorkerType {
162162
self.fileReader.markBatchAsRead(batch, reason: .invalid)
163163
previousUploadStatus = nil
164164
self.telemetry.error("Failed to initiate '\(self.featureName)' data upload", error: error)
165-
sendUploadQualityMetric(failure: "invalid")
166165
}
167166
}
168167

@@ -231,55 +230,51 @@ internal class DataUploadWorker: DataUploadWorkerType {
231230
}
232231
}
233232

234-
private func sendUploadQualityMetric(blockers: [DataUploadConditions.Blocker]) {
235-
guard !blockers.isEmpty else {
236-
return sendUploadQualityMetric()
237-
}
238-
239-
sendUploadQualityMetric(
240-
failure: "blocker",
241-
blockers: blockers.map {
242-
switch $0 {
243-
case .battery: return "low_battery"
244-
case .lowPowerModeOn: return "lpm"
245-
case .networkReachability: return "offline"
246-
}
247-
}
233+
private func sendUploadCycleMetric() {
234+
telemetry.metric(
235+
name: UploadCycleMetric.name,
236+
attributes: [UploadCycleMetric.track: featureName]
248237
)
249238
}
250239

251-
private func sendUploadQualityMetric(status: DataUploadStatus) {
252-
guard let error = status.error else {
253-
return sendUploadQualityMetric()
240+
private func sendBatchBlockedMetric(blockers: [DataUploadConditions.Blocker]) {
241+
guard !blockers.isEmpty else {
242+
return
254243
}
255244

256-
sendUploadQualityMetric(
257-
failure: {
258-
switch error {
259-
case let .httpError(code): return "\(code)"
260-
case let .networkError(error): return "\(error.code)"
261-
}
262-
}()
263-
)
264-
}
265-
266-
private func sendUploadQualityMetric() {
267245
telemetry.metric(
268-
name: UploadQualityMetric.name,
246+
name: BatchBlockedMetric.name,
269247
attributes: [
270-
UploadQualityMetric.track: featureName
271-
]
248+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
249+
BatchMetric.trackKey: featureName,
250+
BatchBlockedMetric.uploaderDelayKey: delay.current,
251+
BatchBlockedMetric.blockers: blockers.map {
252+
switch $0 {
253+
case .battery: return "low_battery"
254+
case .lowPowerModeOn: return "lpm"
255+
case .networkReachability: return "offline"
256+
}
257+
}
258+
],
259+
sampleRate: BatchBlockedMetric.sampleRate
272260
)
273261
}
274262

275-
private func sendUploadQualityMetric(failure: String, blockers: [String] = []) {
263+
private func sendBatchBlockedMetric(error: DataUploadError) {
276264
telemetry.metric(
277-
name: UploadQualityMetric.name,
265+
name: BatchBlockedMetric.name,
278266
attributes: [
279-
UploadQualityMetric.track: featureName,
280-
UploadQualityMetric.failure: failure,
281-
UploadQualityMetric.blockers: blockers
282-
]
267+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
268+
BatchMetric.trackKey: featureName,
269+
BatchBlockedMetric.uploaderDelayKey: delay.current,
270+
BatchBlockedMetric.failure: {
271+
switch error {
272+
case let .httpError(code): return "intake-code-\(code.rawValue)"
273+
case let .networkError(error): return "network-code-\(error.code)"
274+
}
275+
}()
276+
],
277+
sampleRate: BatchBlockedMetric.sampleRate
283278
)
284279
}
285280
}

DatadogCore/Sources/SDKMetrics/BatchMetrics.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,21 @@ internal enum BatchClosedMetric {
130130
/// If the batch was closed by core or after new batch was forced by the feature.
131131
static let forcedNewKey = "forced_new"
132132
}
133+
134+
/// Definition of "Batch Blocked" telemetry.
135+
internal enum BatchBlockedMetric {
136+
/// The name of this metric, included in telemetry log.
137+
/// Note: the "[Mobile Metric]" prefix is added when sending this telemetry in RUM.
138+
static let name = "Batch Blocked"
139+
/// Metric type value.
140+
static let typeValue = "batch blocked"
141+
/// The sample rate for this metric.
142+
/// It is applied in addition to the telemetry sample rate (20% by default).
143+
static let sampleRate: Float = 1.5 // 1.5%
144+
/// The key for uploader's current delay.
145+
static let uploaderDelayKey = "uploader_delay"
146+
147+
/// List of upload blockers
148+
static let blockers = "blockers"
149+
static let failure = "failure"
150+
}

DatadogCore/Tests/Datadog/Core/Upload/DataUploadWorkerTests.swift

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,8 @@ class DataUploadWorkerTests: XCTestCase {
8989
XCTAssertEqual(try orchestrator.directory.files().count, 0)
9090

9191
XCTAssertEqual(telemetry.messages.count, 3)
92-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
92+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
9393
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
94-
XCTAssertNil(metric.attributes["failure"])
95-
XCTAssertNil(metric.attributes["blockers"])
9694
}
9795

9896
func testItUploadsDataSequentiallyWithoutDelay_whenMaxBatchesPerUploadIsSet() throws {
@@ -141,11 +139,9 @@ class DataUploadWorkerTests: XCTestCase {
141139
worker.cancelSynchronously()
142140
XCTAssertEqual(try orchestrator.directory.files().count, 1)
143141

144-
XCTAssertEqual(telemetry.messages.count, 2)
145-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
142+
XCTAssertEqual(telemetry.messages.count, 1)
143+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
146144
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
147-
XCTAssertNil(metric.attributes["failure"])
148-
XCTAssertNil(metric.attributes["blockers"])
149145
}
150146

151147
func testGivenDataToUpload_whenUploadFinishesAndDoesNotNeedToBeRetried_thenDataIsDeleted() {
@@ -526,7 +522,7 @@ class DataUploadWorkerTests: XCTestCase {
526522
)
527523
}
528524

529-
func testWhenUploadIsBlocked_itDoesSendUploadQualityTelemetry() throws {
525+
func testWhenUploadIsBlocked_itDoesSendBatchBlockedTelemetry() throws {
530526
// Given
531527
let telemetry = TelemetryMock()
532528

@@ -566,9 +562,9 @@ class DataUploadWorkerTests: XCTestCase {
566562

567563
// Then
568564
XCTAssertEqual(telemetry.messages.count, 1)
569-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
570-
XCTAssertEqual(metric.attributes["failure"] as? String, "blocker")
565+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "Batch Blocked"), "A Batch blocked metric should be send to `telemetry`.")
571566
XCTAssertEqual(metric.attributes["blockers"] as? [String], ["offline", "low_battery"])
567+
XCTAssertNil(metric.attributes["failure"])
572568
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
573569
}
574570

@@ -613,9 +609,12 @@ class DataUploadWorkerTests: XCTestCase {
613609
worker.cancelSynchronously()
614610

615611
// Then
616-
XCTAssertEqual(telemetry.messages.count, 1)
617-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
618-
XCTAssertEqual(metric.attributes["failure"] as? String, "\(randomStatusCode)")
612+
XCTAssertEqual(telemetry.messages.count, 2)
613+
XCTAssertNotNil(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
614+
615+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "Batch Blocked"), "A Batch Blocked metric should be send to `telemetry`.")
616+
XCTAssertEqual(metric.attributes["failure"] as? String, "intake-code-\(randomStatusCode.rawValue)")
617+
XCTAssertNil(metric.attributes["blockers"])
619618
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
620619
}
621620

@@ -660,13 +659,16 @@ class DataUploadWorkerTests: XCTestCase {
660659
worker.cancelSynchronously()
661660

662661
// Then
663-
XCTAssertEqual(telemetry.messages.count, 2)
662+
XCTAssertEqual(telemetry.messages.count, 3)
663+
664+
XCTAssertNotNil(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
664665

665666
let error = try XCTUnwrap(telemetry.messages.firstError(), "An error should be send to `telemetry`.")
666667
XCTAssertEqual(error.message,"Data upload finished with status code: \(randomStatusCode.rawValue)")
667668

668-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
669-
XCTAssertEqual(metric.attributes["failure"] as? String, "\(randomStatusCode)")
669+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "Batch Blocked"), "A Batch Blocked metric should be send to `telemetry`.")
670+
XCTAssertEqual(metric.attributes["failure"] as? String, "intake-code-\(randomStatusCode.rawValue)")
671+
XCTAssertNil(metric.attributes["blockers"])
670672
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
671673
}
672674

@@ -705,13 +707,16 @@ class DataUploadWorkerTests: XCTestCase {
705707
worker.cancelSynchronously()
706708

707709
// Then
708-
XCTAssertEqual(telemetry.messages.count, 2)
710+
XCTAssertEqual(telemetry.messages.count, 3)
711+
712+
XCTAssertNotNil(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
709713

710714
let error = try XCTUnwrap(telemetry.messages.firstError(), "An error should be send to `telemetry`.")
711715
XCTAssertEqual(error.message, #"Data upload finished with error - Error Domain=abc Code=0 "(null)""#)
712716

713-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
714-
XCTAssertEqual(metric.attributes["failure"] as? String, "\(nserror.code)")
717+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "Batch Blocked"), "A Batch Blocked metric should be send to `telemetry`.")
718+
XCTAssertEqual(metric.attributes["failure"] as? String, "network-code-\(nserror.code)")
719+
XCTAssertNil(metric.attributes["blockers"])
715720
XCTAssertEqual(metric.attributes["track"] as? String, featureName)
716721
}
717722

@@ -751,8 +756,7 @@ class DataUploadWorkerTests: XCTestCase {
751756
let error = try XCTUnwrap(telemetry.messages.firstError(), "An error should be send to `telemetry`.")
752757
XCTAssertEqual(error.message, #"Failed to initiate 'some-feature' data upload - Failed to prepare upload"#)
753758

754-
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_quality"), "An upload quality metric should be send to `telemetry`.")
755-
XCTAssertEqual(metric.attributes["failure"] as? String, "invalid")
759+
let metric = try XCTUnwrap(telemetry.messages.firstMetric(named: "upload_cycle"), "An upload cycle metric should be send to `telemetry`.")
756760
XCTAssertEqual(metric.attributes["track"] as? String, "some-feature")
757761
}
758762

DatadogInternal/Sources/SDKMetrics/UploadQualityMetric.swift renamed to DatadogInternal/Sources/SDKMetrics/UploadCycleMetric.swift

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66

77
import Foundation
88

9-
/// Fields of the Upload Quality Metric.
9+
/// Fields of the Upload Cycle Metric.
1010
///
1111
/// This metric is not sent to Telemetry as-is, values are sent on the message-bus
1212
/// and aggregated internally by RUM's message receiver. The aggregate is sent as an
1313
/// attribute of the "RUM Session Ended" metric.
14-
public enum UploadQualityMetric {
14+
public enum UploadCycleMetric {
1515
/// Metric's name
16-
public static let name = "upload_quality"
16+
public static let name = "upload_cycle"
1717
/// The Metrics' upload track, or feature name.
1818
public static let track = "track"
19-
/// The upload's failure description.
20-
public static let failure = "failure"
21-
/// The upload's blockers list.
22-
public static let blockers = "blockers"
2319
}

0 commit comments

Comments
 (0)