Skip to content

Commit ab4d3fe

Browse files
committed
RUM-8042 Batch Blocked aggregator
1 parent e5c6406 commit ab4d3fe

File tree

12 files changed

+282
-65
lines changed

12 files changed

+282
-65
lines changed

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,10 @@
13951395
D2A7A9002BA1C24A00F46845 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D2A7A8FE2BA1C24A00F46845 /* PrivacyInfo.xcprivacy */; };
13961396
D2A7A9022BA1C4B100F46845 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D2A7A9012BA1C4B100F46845 /* PrivacyInfo.xcprivacy */; };
13971397
D2A7A9032BA1C4B100F46845 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = D2A7A9012BA1C4B100F46845 /* PrivacyInfo.xcprivacy */; };
1398+
D2AB80BD2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AB80BC2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift */; };
1399+
D2AB80BE2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AB80BC2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift */; };
1400+
D2AB80DF2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AB80DE2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift */; };
1401+
D2AB80E02D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AB80DE2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift */; };
13981402
D2AD1CC32CE4AE6600106C74 /* Color+Reflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AD1CBA2CE4AE6600106C74 /* Color+Reflection.swift */; };
13991403
D2AD1CC42CE4AE6600106C74 /* DisplayList+Reflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AD1CBD2CE4AE6600106C74 /* DisplayList+Reflection.swift */; };
14001404
D2AD1CC52CE4AE6600106C74 /* DisplayList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AD1CBC2CE4AE6600106C74 /* DisplayList.swift */; };
@@ -3071,6 +3075,8 @@
30713075
D2A7840229A536AD003B03BB /* PrintFunctionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrintFunctionMock.swift; sourceTree = "<group>"; };
30723076
D2A7A8FE2BA1C24A00F46845 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../Resources/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
30733077
D2A7A9012BA1C4B100F46845 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../Resources/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
3078+
D2AB80BC2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchBlockedMetricAggregator.swift; sourceTree = "<group>"; };
3079+
D2AB80DE2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchBlockedMetricAggregatorTests.swift; sourceTree = "<group>"; };
30743080
D2AD1CB92CE4AE6600106C74 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
30753081
D2AD1CBA2CE4AE6600106C74 /* Color+Reflection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Reflection.swift"; sourceTree = "<group>"; };
30763082
D2AD1CBC2CE4AE6600106C74 /* DisplayList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayList.swift; sourceTree = "<group>"; };
@@ -5041,8 +5047,9 @@
50415047
6174D6082BFDDD1E00EC7469 /* SDKMetrics */ = {
50425048
isa = PBXGroup;
50435049
children = (
5044-
D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */,
50455050
614396712A67D74F00197326 /* BatchMetrics.swift */,
5051+
D2AB80BC2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift */,
5052+
D2E6E8FA2D8039B200FF1398 /* BenchmarkURLSessionTaskDelegate.swift */,
50465053
);
50475054
path = SDKMetrics;
50485055
sourceTree = "<group>";
@@ -5051,6 +5058,7 @@
50515058
isa = PBXGroup;
50525059
children = (
50535060
6134CDB02A691E850061CCD9 /* BatchMetricsTests.swift */,
5061+
D2AB80DE2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift */,
50545062
);
50555063
path = SDKMetrics;
50565064
sourceTree = "<group>";
@@ -8004,6 +8012,7 @@
80048012
D29CDD3228211A2200F7DAA5 /* TLVBlock.swift in Sources */,
80058013
6128F5742BA3280300D35B08 /* DataStoreFileReader.swift in Sources */,
80068014
D2553829288F0B2400727FAD /* LowPowerModePublisher.swift in Sources */,
8015+
D2AB80BE2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift in Sources */,
80078016
D224430629E95C2C00274EC7 /* MessageBus.swift in Sources */,
80088017
61F930BE2BA1ACAC005F0EE2 /* Storage+TLV.swift in Sources */,
80098018
6128F5772BA32DE500D35B08 /* DataStoreFileWriter.swift in Sources */,
@@ -8138,6 +8147,7 @@
81388147
6176991B2A86121B0030022B /* HTTPClientMock.swift in Sources */,
81398148
61133C612423990D00786299 /* URLSessionClientTests.swift in Sources */,
81408149
61133C6A2423990D00786299 /* DatadogTests.swift in Sources */,
8150+
D2AB80DF2D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift in Sources */,
81418151
D22743DB29DEB8B4001A7EF9 /* VitalCPUReaderTests.swift in Sources */,
81428152
61DA8CAC2861C3720074A606 /* DirectoriesTests.swift in Sources */,
81438153
612C13D62AAB35EB0086B5D1 /* SRSegmentMatcher.swift in Sources */,
@@ -9343,6 +9353,7 @@
93439353
6128F5752BA3280300D35B08 /* DataStoreFileReader.swift in Sources */,
93449354
D2303A0B298D5412001A1FA3 /* AsyncWriter.swift in Sources */,
93459355
D224430729E95C2E00274EC7 /* MessageBus.swift in Sources */,
9356+
D2AB80BD2D91AA0800B4A7FC /* BatchBlockedMetricAggregator.swift in Sources */,
93469357
61F930BF2BA1ACAC005F0EE2 /* Storage+TLV.swift in Sources */,
93479358
6128F5782BA32DE500D35B08 /* DataStoreFileWriter.swift in Sources */,
93489359
D2CB6E3627C50EAE00A62B57 /* ObjcAppLaunchHandler.m in Sources */,
@@ -9407,6 +9418,7 @@
94079418
D24C9C4E29A7BA41002057CF /* LogsMocks.swift in Sources */,
94089419
D2CB6EDE27C520D400A62B57 /* RUMEventMatcher.swift in Sources */,
94099420
D2CB6EE027C520D400A62B57 /* SpanMatcher.swift in Sources */,
9421+
D2AB80E02D931C0800B4A7FC /* BatchBlockedMetricAggregatorTests.swift in Sources */,
94109422
D2552AF32BBC47D300A45725 /* WebRecordIntegrationTests.swift in Sources */,
94119423
6179DB572B6022EA00E9E04E /* SendingCrashReportTests.swift in Sources */,
94129424
61112F8F2A4417D6006FFCA6 /* DDRUM+apiTests.m in Sources */,

DatadogCore/Sources/Core/DatadogCore.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ internal final class DatadogCore {
7171
/// Maximum number of batches per upload.
7272
internal let maxBatchesPerUpload: Int
7373

74+
/// Instance to aggregate batch-blocked metric to be sent when the
75+
/// application goes to background.
76+
private let batchBlockedMetricAggregator = BatchBlockedMetricAggregator()
77+
7478
/// Creates a core instance.
7579
///
7680
/// - Parameters:
@@ -82,6 +86,10 @@ internal final class DatadogCore {
8286
/// - encryption: The on-disk data encryption.
8387
/// - contextProvider: The core context provider.
8488
/// - applicationVersion: The application version.
89+
/// - maxBatchesPerUpload: Number of batch to process during an upload cycle.
90+
/// - backgroundTasksEnabled: Enables upload background task.
91+
/// - isRunFromExtension: Set `true` when the SDK is initialised from an extension.
92+
/// - notificationCenter: The Notification center to observe.
8593
init(
8694
directory: CoreDirectory,
8795
dateProvider: DateProvider,
@@ -93,7 +101,8 @@ internal final class DatadogCore {
93101
applicationVersion: String,
94102
maxBatchesPerUpload: Int,
95103
backgroundTasksEnabled: Bool,
96-
isRunFromExtension: Bool = false
104+
isRunFromExtension: Bool = false,
105+
notificationCenter: NotificationCenter = .default
97106
) {
98107
self.directory = directory
99108
self.dateProvider = dateProvider
@@ -118,6 +127,14 @@ internal final class DatadogCore {
118127
self.contextProvider.publish { [weak self] context in
119128
self?.send(message: .context(context))
120129
}
130+
131+
// observe application entering background
132+
notificationCenter.addObserver(
133+
self,
134+
selector: #selector(applicationDidEnterBackground),
135+
name: ApplicationNotifications.didEnterBackground,
136+
object: nil
137+
)
121138
}
122139

123140
/// Sets current user information.
@@ -230,6 +247,15 @@ internal final class DatadogCore {
230247
stores = [:]
231248
features = [:]
232249
}
250+
251+
@objc
252+
private func applicationDidEnterBackground() {
253+
// Report aggregated 'Batch Blocked' telemetry metric
254+
// when the application enters background.
255+
for metric in batchBlockedMetricAggregator.flush() {
256+
telemetry.send(telemetry: .metric(metric))
257+
}
258+
}
233259
}
234260

235261
extension DatadogCore: DatadogCoreProtocol {
@@ -273,7 +299,8 @@ extension DatadogCore: DatadogCoreProtocol {
273299
performance: performancePreset,
274300
backgroundTasksEnabled: backgroundTasksEnabled,
275301
isRunFromExtension: isRunFromExtension,
276-
telemetry: telemetry
302+
telemetry: telemetry,
303+
batchBlockedMetricAggregator: batchBlockedMetricAggregator
277304
)
278305

279306
stores[T.name] = (

DatadogCore/Sources/Core/MessageBus.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,8 @@ internal final class MessageBus {
8888
/// - fallback: The fallback closure to call when the message could not be
8989
/// processed by any Features on the bus.
9090
func send(message: FeatureMessage, else fallback: @escaping () -> Void = {}) {
91-
if // Configuration Telemetry Message
92-
case .telemetry(let telemetry) = message,
93-
case .configuration(let configuration) = telemetry {
91+
if case .telemetry(.configuration(let configuration) ) = message {
92+
// Configuration Telemetry Message
9493
return save(configuration: configuration)
9594
}
9695

DatadogCore/Sources/Core/Upload/DataUploadWorker.swift

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
4444

4545
private var previousUploadStatus: DataUploadStatus?
4646

47+
private let batchBlockedMetricAggregator: BatchBlockedMetricAggregator?
48+
4749
init(
4850
queue: DispatchQueue,
4951
fileReader: Reader,
@@ -54,7 +56,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
5456
featureName: String,
5557
telemetry: Telemetry,
5658
maxBatchesPerUpload: Int,
57-
backgroundTaskCoordinator: BackgroundTaskCoordinator? = nil
59+
backgroundTaskCoordinator: BackgroundTaskCoordinator? = nil,
60+
batchBlockedMetricAggregator: BatchBlockedMetricAggregator? = nil
5861
) {
5962
self.queue = queue
6063
self.fileReader = fileReader
@@ -65,6 +68,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
6568
self.delay = delay
6669
self.featureName = featureName
6770
self.telemetry = telemetry
71+
self.batchBlockedMetricAggregator = batchBlockedMetricAggregator
72+
6873
let readWorkItem = DispatchWorkItem { [weak self] in
6974
guard let self = self else {
7075
return
@@ -249,22 +254,16 @@ internal class DataUploadWorker: DataUploadWorkerType {
249254
return
250255
}
251256

252-
telemetry.metric(
253-
name: BatchBlockedMetric.name,
254-
attributes: [
255-
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
256-
BatchMetric.trackKey: featureName,
257-
BatchBlockedMetric.uploaderDelayKey: delay.current,
258-
BatchBlockedMetric.batchCount: batchCount,
259-
BatchBlockedMetric.blockers: blockers.map {
260-
switch $0 {
261-
case .battery: return "low_battery"
262-
case .lowPowerModeOn: return "lpm"
263-
case .networkReachability: return "offline"
264-
}
257+
batchBlockedMetricAggregator?.increment(
258+
by: batchCount,
259+
track: featureName,
260+
blockers: blockers.map {
261+
switch $0 {
262+
case .battery: return "low_battery"
263+
case .lowPowerModeOn: return "lpm"
264+
case .networkReachability: return "offline"
265265
}
266-
],
267-
sampleRate: BatchBlockedMetric.sampleRate
266+
}
268267
)
269268
}
270269

@@ -273,21 +272,15 @@ internal class DataUploadWorker: DataUploadWorkerType {
273272
return
274273
}
275274

276-
telemetry.metric(
277-
name: BatchBlockedMetric.name,
278-
attributes: [
279-
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
280-
BatchMetric.trackKey: featureName,
281-
BatchBlockedMetric.uploaderDelayKey: delay.current,
282-
BatchBlockedMetric.batchCount: batchCount,
283-
BatchBlockedMetric.failure: {
284-
switch error {
285-
case let .httpError(code): return "intake-code-\(code.rawValue)"
286-
case let .networkError(error): return "network-code-\(error.code)"
287-
}
288-
}()
289-
],
290-
sampleRate: BatchBlockedMetric.sampleRate
275+
batchBlockedMetricAggregator?.increment(
276+
by: batchCount,
277+
track: featureName,
278+
failure: {
279+
switch error {
280+
case let .httpError(code): return "intake-code-\(code.rawValue)"
281+
case let .networkError(error): return "network-code-\(error.code)"
282+
}
283+
}()
291284
)
292285
}
293286
}

DatadogCore/Sources/Core/Upload/FeatureUpload.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ internal struct FeatureUpload {
2020
performance: PerformancePreset,
2121
backgroundTasksEnabled: Bool,
2222
isRunFromExtension: Bool,
23-
telemetry: Telemetry
23+
telemetry: Telemetry,
24+
batchBlockedMetricAggregator: BatchBlockedMetricAggregator? = nil
2425
) {
2526
let uploadQueue = DispatchQueue(
2627
label: "com.datadoghq.ios-sdk-\(featureName)-upload",
@@ -63,7 +64,8 @@ internal struct FeatureUpload {
6364
featureName: featureName,
6465
telemetry: telemetry,
6566
maxBatchesPerUpload: performance.maxBatchesPerUpload,
66-
backgroundTaskCoordinator: backgroundTaskCoordinator
67+
backgroundTaskCoordinator: backgroundTaskCoordinator,
68+
batchBlockedMetricAggregator: batchBlockedMetricAggregator
6769
)
6870
)
6971
}

DatadogCore/Sources/Datadog.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,8 @@ extension DatadogCore {
583583
applicationVersion: applicationVersion,
584584
maxBatchesPerUpload: configuration.batchProcessingLevel.maxBatchesPerUpload,
585585
backgroundTasksEnabled: configuration.backgroundTasksEnabled,
586-
isRunFromExtension: isRunFromExtension
586+
isRunFromExtension: isRunFromExtension,
587+
notificationCenter: configuration.notificationCenter
587588
)
588589

589590
telemetry.configuration(
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
import DatadogInternal
9+
10+
internal final class BatchBlockedMetricAggregator {
11+
private struct AggregationKey: Hashable {
12+
let track: String
13+
let failure: String?
14+
let blockers: [String]?
15+
}
16+
17+
let sampleRate: SampleRate
18+
19+
@ReadWriteLock
20+
private var aggregations: [AggregationKey: Int] = [:]
21+
22+
init(sampleRate: SampleRate = MetricTelemetry.defaultSampleRate) {
23+
self.sampleRate = sampleRate
24+
}
25+
26+
func increment(by count: Int, track: String, failure: String) {
27+
increment(by: count, key: AggregationKey(track: track, failure: failure, blockers: nil))
28+
}
29+
30+
func increment(by count: Int, track: String, blockers: [String]) {
31+
increment(by: count, key: AggregationKey(track: track, failure: nil, blockers: blockers))
32+
}
33+
34+
private func increment(by count: Int, key: AggregationKey) {
35+
_aggregations.mutate { $0[key, default: 0] += count }
36+
}
37+
38+
func flush() -> [MetricTelemetry] {
39+
_aggregations.mutate { aggregations in
40+
defer { aggregations = [:] }
41+
42+
return aggregations.compactMap { key, value in
43+
if let failure = key.failure {
44+
return MetricTelemetry(
45+
name: BatchBlockedMetric.name,
46+
attributes: [
47+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
48+
BatchMetric.trackKey: key.track,
49+
BatchBlockedMetric.batchCount: value,
50+
BatchBlockedMetric.failure: failure
51+
],
52+
sampleRate: sampleRate
53+
)
54+
}
55+
56+
if let blockers = key.blockers {
57+
return MetricTelemetry(
58+
name: BatchBlockedMetric.name,
59+
attributes: [
60+
SDKMetricFields.typeKey: BatchBlockedMetric.typeValue,
61+
BatchMetric.trackKey: key.track,
62+
BatchBlockedMetric.batchCount: value,
63+
BatchBlockedMetric.blockers: blockers
64+
],
65+
sampleRate: sampleRate
66+
)
67+
}
68+
69+
return nil
70+
}
71+
}
72+
}
73+
}

DatadogCore/Sources/SDKMetrics/BatchMetrics.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,10 @@ internal enum BatchBlockedMetric {
138138
static let name = "Batch Blocked"
139139
/// Metric type value.
140140
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.current"
146141
/// The key for count of bacthes being blocked.
147-
static let batchCount = "batch_count"
148-
149-
/// List of upload blockers
142+
static let batchCount = "count"
143+
/// List of upload blocker reasons
150144
static let blockers = "blockers"
145+
/// The blocking failure reason.
151146
static let failure = "failure"
152147
}

0 commit comments

Comments
 (0)