Skip to content

Commit 2a444dc

Browse files
committed
RUM-8042 Replace Upload Quality with Batch Blocked telemetry
1 parent 09490e5 commit 2a444dc

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
@@ -940,8 +940,8 @@
940940
D22743E729DEB953001A7EF9 /* UIApplicationSwizzlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61410166251A661D00E3C2D9 /* UIApplicationSwizzlerTests.swift */; };
941941
D22743EB29DEC9E6001A7EF9 /* Casting+RUM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61411B0F24EC15AC0012EAB2 /* Casting+RUM.swift */; };
942942
D22743EC29DEC9E6001A7EF9 /* Casting+RUM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61411B0F24EC15AC0012EAB2 /* Casting+RUM.swift */; };
943-
D22789362D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */; };
944-
D22789372D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */; };
943+
D22789362D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */; };
944+
D22789372D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */; };
945945
D227A0A42C7622EA00C83324 /* BenchmarkProfiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */; };
946946
D227A0A52C7622EA00C83324 /* BenchmarkProfiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */; };
947947
D22C5BC82A98A0B20024CC1F /* Baggages.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22C5BC52A989D130024CC1F /* Baggages.swift */; };
@@ -2903,7 +2903,7 @@
29032903
D2216EC22A96632F00ADAEC8 /* FeatureBaggageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureBaggageTests.swift; sourceTree = "<group>"; };
29042904
D22442C42CA301DA002E71E4 /* UIColor+SessionReplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+SessionReplay.swift"; sourceTree = "<group>"; };
29052905
D224430C29E95D6600274EC7 /* CrashReportReceiverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrashReportReceiverTests.swift; sourceTree = "<group>"; };
2906-
D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadQualityMetric.swift; sourceTree = "<group>"; };
2906+
D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadCycleMetric.swift; sourceTree = "<group>"; };
29072907
D227A0A32C7622EA00C83324 /* BenchmarkProfiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BenchmarkProfiler.swift; sourceTree = "<group>"; };
29082908
D22C1F5B271484B400922024 /* LogEventMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEventMapper.swift; sourceTree = "<group>"; };
29092909
D22C5BC52A989D130024CC1F /* Baggages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Baggages.swift; sourceTree = "<group>"; };
@@ -5048,7 +5048,7 @@
50485048
children = (
50495049
6174D60B2BFDDEDF00EC7469 /* SDKMetricFields.swift */,
50505050
A7FA98CD2BA1A6930018D6B5 /* MethodCalledMetric.swift */,
5051-
D22789352D64A0D3007E9DB0 /* UploadQualityMetric.swift */,
5051+
D22789352D64A0D3007E9DB0 /* UploadCycleMetric.swift */,
50525052
);
50535053
path = SDKMetrics;
50545054
sourceTree = "<group>";
@@ -8573,7 +8573,7 @@
85738573
D2160CA229C0DE5700FAA9A5 /* NetworkInstrumentationFeature.swift in Sources */,
85748574
D2EBEE1F29BA160F00B15732 /* HTTPHeadersReader.swift in Sources */,
85758575
E2AA55E72C32C6D9002FEF28 /* ApplicationNotifications.swift in Sources */,
8576-
D22789372D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */,
8576+
D22789372D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */,
85778577
D263BCAF29DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */,
85788578
D23039E7298D5236001A1FA3 /* NetworkConnectionInfo.swift in Sources */,
85798579
D23039E9298D5236001A1FA3 /* TrackingConsent.swift in Sources */,
@@ -9616,7 +9616,7 @@
96169616
D2160CA329C0DE5700FAA9A5 /* NetworkInstrumentationFeature.swift in Sources */,
96179617
D2EBEE2D29BA161100B15732 /* HTTPHeadersReader.swift in Sources */,
96189618
E2AA55E82C32C6D9002FEF28 /* ApplicationNotifications.swift in Sources */,
9619-
D22789362D64A0D7007E9DB0 /* UploadQualityMetric.swift in Sources */,
9619+
D22789362D64A0D7007E9DB0 /* UploadCycleMetric.swift in Sources */,
96209620
D263BCB029DAFFEB00FA0E21 /* PerformancePresetOverride.swift in Sources */,
96219621
D2DA2359298D57AA00C6C7E6 /* NetworkConnectionInfo.swift in Sources */,
96229622
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)")
@@ -147,8 +147,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
147147
previousUploadStatus = nil
148148

149149
if let error = uploadStatus.error {
150-
// Throw to report the request error accordingly
151-
throw error
150+
sendBatchBlockedMetric(error: error)
151+
throw error // Throw to report the request error accordingly
152152
}
153153
} catch DataUploadError.httpError(statusCode: .unauthorized), DataUploadError.httpError(statusCode: .forbidden) {
154154
DD.logger.error("⚠️ Make sure that the provided token still exists and you're targeting the relevant Datadog site.")
@@ -166,7 +166,6 @@ internal class DataUploadWorker: DataUploadWorkerType {
166166
self.fileReader.markBatchAsRead(batch, reason: .invalid)
167167
previousUploadStatus = nil
168168
self.telemetry.error("Failed to initiate '\(self.featureName)' data upload", error: error)
169-
sendUploadQualityMetric(failure: "invalid")
170169
}
171170
}
172171

@@ -235,55 +234,51 @@ internal class DataUploadWorker: DataUploadWorkerType {
235234
}
236235
}
237236

238-
private func sendUploadQualityMetric(blockers: [DataUploadConditions.Blocker]) {
239-
guard !blockers.isEmpty else {
240-
return sendUploadQualityMetric()
241-
}
242-
243-
sendUploadQualityMetric(
244-
failure: "blocker",
245-
blockers: blockers.map {
246-
switch $0 {
247-
case .battery: return "low_battery"
248-
case .lowPowerModeOn: return "lpm"
249-
case .networkReachability: return "offline"
250-
}
251-
}
237+
private func sendUploadCycleMetric() {
238+
telemetry.metric(
239+
name: UploadCycleMetric.name,
240+
attributes: [UploadCycleMetric.track: featureName]
252241
)
253242
}
254243

255-
private func sendUploadQualityMetric(status: DataUploadStatus) {
256-
guard let error = status.error else {
257-
return sendUploadQualityMetric()
244+
private func sendBatchBlockedMetric(blockers: [DataUploadConditions.Blocker]) {
245+
guard !blockers.isEmpty else {
246+
return
258247
}
259248

260-
sendUploadQualityMetric(
261-
failure: {
262-
switch error {
263-
case let .httpError(code): return "\(code)"
264-
case let .networkError(error): return "\(error.code)"
265-
}
266-
}()
267-
)
268-
}
269-
270-
private func sendUploadQualityMetric() {
271249
telemetry.metric(
272-
name: UploadQualityMetric.name,
250+
name: BatchBlockedMetric.name,
273251
attributes: [
274-
UploadQualityMetric.track: featureName
275-
]
252+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
253+
BatchMetric.trackKey: featureName,
254+
BatchBlockedMetric.uploaderDelayKey: delay.current,
255+
BatchBlockedMetric.blockers: blockers.map {
256+
switch $0 {
257+
case .battery: return "low_battery"
258+
case .lowPowerModeOn: return "lpm"
259+
case .networkReachability: return "offline"
260+
}
261+
}
262+
],
263+
sampleRate: BatchBlockedMetric.sampleRate
276264
)
277265
}
278266

279-
private func sendUploadQualityMetric(failure: String, blockers: [String] = []) {
267+
private func sendBatchBlockedMetric(error: DataUploadError) {
280268
telemetry.metric(
281-
name: UploadQualityMetric.name,
269+
name: BatchBlockedMetric.name,
282270
attributes: [
283-
UploadQualityMetric.track: featureName,
284-
UploadQualityMetric.failure: failure,
285-
UploadQualityMetric.blockers: blockers
286-
]
271+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
272+
BatchMetric.trackKey: featureName,
273+
BatchBlockedMetric.uploaderDelayKey: delay.current,
274+
BatchBlockedMetric.failure: {
275+
switch error {
276+
case let .httpError(code): return "intake-code-\(code.rawValue)"
277+
case let .networkError(error): return "network-code-\(error.code)"
278+
}
279+
}()
280+
],
281+
sampleRate: BatchBlockedMetric.sampleRate
287282
)
288283
}
289284
}

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)