Skip to content

Commit c13c2ae

Browse files
committed
RUM-3889 feat: Send retry information with network requests
1 parent 088d191 commit c13c2ae

File tree

24 files changed

+593
-145
lines changed

24 files changed

+593
-145
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- [FIX] Refresh rate vital for variable refresh rate displays when over performing. See [#1973][]
44
- [FIX] Alamofire extension types are deprecated now. See [#1988][]
5+
- [IMPROVEMENT] Send retry information with network requests (eg. retry_count, last_failure_status and idempotency key). See [#1991][]
56

67
# 2.14.2 / 26-07-2024
78

@@ -743,6 +744,7 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
743744
[#1967]: https://github.com/DataDog/dd-sdk-ios/pull/1967
744745
[#1973]: https://github.com/DataDog/dd-sdk-ios/pull/1973
745746
[#1988]: https://github.com/DataDog/dd-sdk-ios/pull/1988
747+
[#1991]: https://github.com/DataDog/dd-sdk-ios/pull/1991
746748
[@00fa9a]: https://github.com/00FA9A
747749
[@britton-earnin]: https://github.com/Britton-Earnin
748750
[@hengyu]: https://github.com/Hengyu

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
3C3235A02B55387A000B4258 /* OTelSpanLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C32359F2B55387A000B4258 /* OTelSpanLinkTests.swift */; };
4343
3C3235A12B55387A000B4258 /* OTelSpanLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C32359F2B55387A000B4258 /* OTelSpanLinkTests.swift */; };
4444
3C33E4072BEE35A8003B2988 /* RUMContextMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C33E4062BEE35A7003B2988 /* RUMContextMocks.swift */; };
45+
3C3C9E292C64F277003AF22F /* URL+QueryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E282C64F277003AF22F /* URL+QueryItem.swift */; };
46+
3C3C9E2A2C64F277003AF22F /* URL+QueryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E282C64F277003AF22F /* URL+QueryItem.swift */; };
47+
3C3C9E2C2C64F3CA003AF22F /* Data+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E2B2C64F3CA003AF22F /* Data+Crypto.swift */; };
48+
3C3C9E2D2C64F3CA003AF22F /* Data+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E2B2C64F3CA003AF22F /* Data+Crypto.swift */; };
49+
3C3C9E2F2C64F470003AF22F /* Data+CryptoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E2E2C64F470003AF22F /* Data+CryptoTests.swift */; };
50+
3C3C9E302C64F470003AF22F /* Data+CryptoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E2E2C64F470003AF22F /* Data+CryptoTests.swift */; };
51+
3C3C9E322C64F47F003AF22F /* URL+QueryItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E312C64F47F003AF22F /* URL+QueryItemTests.swift */; };
52+
3C3C9E332C64F47F003AF22F /* URL+QueryItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C9E312C64F47F003AF22F /* URL+QueryItemTests.swift */; };
4553
3C3EF2B02C1AEBAB009E9E57 /* LaunchReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3EF2AF2C1AEBAB009E9E57 /* LaunchReport.swift */; };
4654
3C3EF2B12C1AEBAB009E9E57 /* LaunchReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3EF2AF2C1AEBAB009E9E57 /* LaunchReport.swift */; };
4755
3C41693C29FBF4D50042B9D2 /* DatadogWebViewTracking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CE119FE29F7BE0100202522 /* DatadogWebViewTracking.framework */; };
@@ -2112,6 +2120,10 @@
21122120
3C32359C2B55386C000B4258 /* OTelSpanLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTelSpanLink.swift; sourceTree = "<group>"; };
21132121
3C32359F2B55387A000B4258 /* OTelSpanLinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTelSpanLinkTests.swift; sourceTree = "<group>"; };
21142122
3C33E4062BEE35A7003B2988 /* RUMContextMocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RUMContextMocks.swift; sourceTree = "<group>"; };
2123+
3C3C9E282C64F277003AF22F /* URL+QueryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+QueryItem.swift"; sourceTree = "<group>"; };
2124+
3C3C9E2B2C64F3CA003AF22F /* Data+Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Crypto.swift"; sourceTree = "<group>"; };
2125+
3C3C9E2E2C64F470003AF22F /* Data+CryptoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+CryptoTests.swift"; sourceTree = "<group>"; };
2126+
3C3C9E312C64F47F003AF22F /* URL+QueryItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+QueryItemTests.swift"; sourceTree = "<group>"; };
21152127
3C3EF2AF2C1AEBAB009E9E57 /* LaunchReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchReport.swift; sourceTree = "<group>"; };
21162128
3C43A3862C188970000BFB21 /* WatchdogTerminationMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchdogTerminationMonitorTests.swift; sourceTree = "<group>"; };
21172129
3C4CF9972C47CC8C006DE1C0 /* MemoryWarningMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryWarningMonitorTests.swift; sourceTree = "<group>"; };
@@ -5927,6 +5939,8 @@
59275939
D23039D7298D5235001A1FA3 /* Foundation+Datadog.swift */,
59285940
D22F06D529DAFD500026CC3C /* FixedWidthInteger+Convenience.swift */,
59295941
D22F06D629DAFD500026CC3C /* TimeInterval+Convenience.swift */,
5942+
3C3C9E282C64F277003AF22F /* URL+QueryItem.swift */,
5943+
3C3C9E2B2C64F3CA003AF22F /* Data+Crypto.swift */,
59305944
);
59315945
path = Extensions;
59325946
sourceTree = "<group>";
@@ -6136,6 +6150,8 @@
61366150
children = (
61376151
D263BCB229DB014900FA0E21 /* FixedWidthInteger+ConvenienceTests.swift */,
61386152
D263BCB329DB014900FA0E21 /* TimeInterval+ConvenienceTests.swift */,
6153+
3C3C9E2E2C64F470003AF22F /* Data+CryptoTests.swift */,
6154+
3C3C9E312C64F47F003AF22F /* URL+QueryItemTests.swift */,
61396155
);
61406156
path = Extensions;
61416157
sourceTree = "<group>";
@@ -8679,6 +8695,7 @@
86798695
D2F8235329915E12003C7E99 /* DatadogSite.swift in Sources */,
86808696
D2D3199A29E98D970004F169 /* DefaultJSONEncoder.swift in Sources */,
86818697
6128F56A2BA2237300D35B08 /* DataStore.swift in Sources */,
8698+
3C3C9E292C64F277003AF22F /* URL+QueryItem.swift in Sources */,
86828699
3C3EF2B02C1AEBAB009E9E57 /* LaunchReport.swift in Sources */,
86838700
6167E7002B81EF7500C3CA2D /* BacktraceReportingFeature.swift in Sources */,
86848701
D2EBEE2729BA160F00B15732 /* B3HTTPHeadersWriter.swift in Sources */,
@@ -8697,6 +8714,7 @@
86978714
D23039E5298D5236001A1FA3 /* DateProvider.swift in Sources */,
86988715
D23039E0298D5235001A1FA3 /* DatadogCoreProtocol.swift in Sources */,
86998716
D23039FD298D5236001A1FA3 /* DataCompression.swift in Sources */,
8717+
3C3C9E2C2C64F3CA003AF22F /* Data+Crypto.swift in Sources */,
87008718
D2EA0F462C0E1AE300CB20F8 /* SessionReplayConfiguration.swift in Sources */,
87018719
6167E6F92B81E95900C3CA2D /* BinaryImage.swift in Sources */,
87028720
6174D60C2BFDDEDF00EC7469 /* SDKMetricFields.swift in Sources */,
@@ -9655,6 +9673,7 @@
96559673
D2F8235429915E12003C7E99 /* DatadogSite.swift in Sources */,
96569674
D2D3199B29E98D970004F169 /* DefaultJSONEncoder.swift in Sources */,
96579675
6128F56B2BA2237300D35B08 /* DataStore.swift in Sources */,
9676+
3C3C9E2A2C64F277003AF22F /* URL+QueryItem.swift in Sources */,
96589677
3C3EF2B12C1AEBAB009E9E57 /* LaunchReport.swift in Sources */,
96599678
6167E7012B81EF7500C3CA2D /* BacktraceReportingFeature.swift in Sources */,
96609679
D2EBEE3529BA161100B15732 /* B3HTTPHeadersWriter.swift in Sources */,
@@ -9673,6 +9692,7 @@
96739692
D2DA237B298D57AA00C6C7E6 /* DateProvider.swift in Sources */,
96749693
D2DA237C298D57AA00C6C7E6 /* DatadogCoreProtocol.swift in Sources */,
96759694
D2DA237D298D57AA00C6C7E6 /* DataCompression.swift in Sources */,
9695+
3C3C9E2D2C64F3CA003AF22F /* Data+Crypto.swift in Sources */,
96769696
D2C9A26A2C0F3F5A007526F5 /* SessionReplayConfiguration.swift in Sources */,
96779697
6167E6FA2B81E95900C3CA2D /* BinaryImage.swift in Sources */,
96789698
6174D60D2BFDDEDF00EC7469 /* SDKMetricFields.swift in Sources */,
@@ -9710,6 +9730,7 @@
97109730
D2DA23A1298D58F400C6C7E6 /* ReadWriteLockTests.swift in Sources */,
97119731
D2160CD829C0DF6700FAA9A5 /* FirstPartyHostsTests.swift in Sources */,
97129732
D284C7402C2059F3005142CC /* ObjcExceptionTests.swift in Sources */,
9733+
3C3C9E322C64F47F003AF22F /* URL+QueryItemTests.swift in Sources */,
97139734
D2C5D5282B83FD5300B63F36 /* WebViewMessageTests.swift in Sources */,
97149735
D20731CD29A52E8700ECBF94 /* SamplerTests.swift in Sources */,
97159736
D2DA23A6298D58F400C6C7E6 /* AnyCoderTests.swift in Sources */,
@@ -9731,6 +9752,7 @@
97319752
D2160CD429C0DF6700FAA9A5 /* NetworkInstrumentationFeatureTests.swift in Sources */,
97329753
D263BCB629DB014900FA0E21 /* TimeInterval+ConvenienceTests.swift in Sources */,
97339754
D2DA23AA298D58F400C6C7E6 /* FeatureMessageReceiverTests.swift in Sources */,
9755+
3C3C9E2F2C64F470003AF22F /* Data+CryptoTests.swift in Sources */,
97349756
D2DA23A8298D58F400C6C7E6 /* DeviceInfoTests.swift in Sources */,
97359757
);
97369758
runOnlyForDeploymentPostprocessing = 0;
@@ -9760,6 +9782,7 @@
97609782
D2DA23B5298D59DC00C6C7E6 /* ReadWriteLockTests.swift in Sources */,
97619783
D2160CD929C0DF6700FAA9A5 /* FirstPartyHostsTests.swift in Sources */,
97629784
D284C7412C2059F3005142CC /* ObjcExceptionTests.swift in Sources */,
9785+
3C3C9E332C64F47F003AF22F /* URL+QueryItemTests.swift in Sources */,
97639786
D2C5D5292B83FD5400B63F36 /* WebViewMessageTests.swift in Sources */,
97649787
D20731CE29A52E8700ECBF94 /* SamplerTests.swift in Sources */,
97659788
D2160CEA29C0E00200FAA9A5 /* MethodSwizzlerTests.swift in Sources */,
@@ -9781,6 +9804,7 @@
97819804
D2160CD529C0DF6700FAA9A5 /* NetworkInstrumentationFeatureTests.swift in Sources */,
97829805
D263BCB729DB014900FA0E21 /* TimeInterval+ConvenienceTests.swift in Sources */,
97839806
D2DA23B8298D59DC00C6C7E6 /* FeatureMessageReceiverTests.swift in Sources */,
9807+
3C3C9E302C64F470003AF22F /* Data+CryptoTests.swift in Sources */,
97849808
D2DA23BA298D59DC00C6C7E6 /* DeviceInfoTests.swift in Sources */,
97859809
);
97869810
runOnlyForDeploymentPostprocessing = 0;

DatadogCore/Sources/Core/Upload/DataUploadStatus.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,32 @@ internal struct DataUploadStatus {
7272
let userDebugDescription: String
7373

7474
let error: DataUploadError?
75+
76+
let attempt: UInt
7577
}
7678

7779
extension DataUploadStatus {
7880
// MARK: - Initialization
7981

80-
init(httpResponse: HTTPURLResponse, ddRequestID: String?) {
82+
init(httpResponse: HTTPURLResponse, ddRequestID: String?, attempt: UInt) {
8183
let statusCode = HTTPResponseStatusCode(rawValue: httpResponse.statusCode) ?? .unexpected
8284

8385
self.init(
8486
needsRetry: statusCode.needsRetry,
8587
responseCode: httpResponse.statusCode,
86-
userDebugDescription: "[response code: \(httpResponse.statusCode) (\(statusCode)), request ID: \(ddRequestID ?? "(???)")]",
87-
error: DataUploadError(status: httpResponse.statusCode)
88+
userDebugDescription: "[response code: \(httpResponse.statusCode) (\(statusCode)), request ID: \(ddRequestID ?? "(???)")",
89+
error: DataUploadError(status: httpResponse.statusCode),
90+
attempt: attempt
8891
)
8992
}
9093

91-
init(networkError: Error) {
94+
init(networkError: Error, attempt: UInt) {
9295
self.init(
9396
needsRetry: true, // retry this upload as it failed due to network transport isse
94-
responseCode: nil,
97+
responseCode: (networkError as NSError).code,
9598
userDebugDescription: "[error: \(DDError(error: networkError).message)]", // e.g. "[error: A data connection is not currently allowed]"
96-
error: DataUploadError(networkError: networkError)
99+
error: DataUploadError(networkError: networkError),
100+
attempt: attempt
97101
)
98102
}
99103
}

DatadogCore/Sources/Core/Upload/DataUploadWorker.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ internal class DataUploadWorker: DataUploadWorkerType {
4444
/// Background task coordinator responsible for registering and ending background tasks for UIKit targets.
4545
private var backgroundTaskCoordinator: BackgroundTaskCoordinator?
4646

47+
private var previousUploadStatus: DataUploadStatus?
48+
4749
init(
4850
queue: DispatchQueue,
4951
fileReader: Reader,
@@ -113,8 +115,11 @@ internal class DataUploadWorker: DataUploadWorkerType {
113115
do {
114116
let uploadStatus = try self.dataUploader.upload(
115117
events: batch.events,
116-
context: context
118+
context: context,
119+
previous: previousUploadStatus
117120
)
121+
previousUploadStatus = uploadStatus
122+
118123
if uploadStatus.needsRetry {
119124
DD.logger.debug(" → (\(self.featureName)) not delivered, will be retransmitted: \(uploadStatus.userDebugDescription)")
120125
self.delay.increase()
@@ -129,6 +134,7 @@ internal class DataUploadWorker: DataUploadWorkerType {
129134
batch,
130135
reason: .intakeCode(responseCode: uploadStatus.responseCode)
131136
)
137+
previousUploadStatus = nil
132138
}
133139

134140
if let error = uploadStatus.error {
@@ -144,6 +150,7 @@ internal class DataUploadWorker: DataUploadWorkerType {
144150
} catch let error {
145151
// If upload can't be initiated do not retry, so drop the batch:
146152
self.fileReader.markBatchAsRead(batch, reason: .invalid)
153+
previousUploadStatus = nil
147154
self.telemetry.error("Failed to initiate '\(self.featureName)' data upload", error: error)
148155
}
149156
}
@@ -173,12 +180,21 @@ internal class DataUploadWorker: DataUploadWorkerType {
173180
// metrics or telemetry. This is legitimate as long as `flush()` routine is only available for testing
174181
// purposes and never run in production apps.
175182
self.fileReader.markBatchAsRead(nextBatch, reason: .flushed)
183+
previousUploadStatus = nil
176184
}
177185
do {
178186
// Try uploading the batch and do one more retry on failure.
179-
_ = try self.dataUploader.upload(events: nextBatch.events, context: self.contextProvider.read())
187+
previousUploadStatus = try self.dataUploader.upload(
188+
events: nextBatch.events,
189+
context: self.contextProvider.read(),
190+
previous: previousUploadStatus
191+
)
180192
} catch {
181-
_ = try? self.dataUploader.upload(events: nextBatch.events, context: self.contextProvider.read())
193+
previousUploadStatus = try? self.dataUploader.upload(
194+
events: nextBatch.events,
195+
context: self.contextProvider.read(),
196+
previous: previousUploadStatus
197+
)
182198
}
183199
}
184200
}

DatadogCore/Sources/Core/Upload/DataUploader.swift

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66

77
import Foundation
88
import DatadogInternal
9+
import CommonCrypto
910

1011
/// A type that performs data uploads.
1112
internal protocol DataUploaderType {
12-
func upload(events: [Event], context: DatadogContext) throws -> DataUploadStatus
13+
func upload(events: [Event], context: DatadogContext, previous: DataUploadStatus?) throws -> DataUploadStatus
1314
}
1415

1516
/// Synchronously uploads data to server using `HTTPClient`.
@@ -19,7 +20,8 @@ internal final class DataUploader: DataUploaderType {
1920
needsRetry: false,
2021
responseCode: nil,
2122
userDebugDescription: "",
22-
error: nil
23+
error: nil,
24+
attempt: 0
2325
)
2426

2527
private let httpClient: HTTPClient
@@ -32,20 +34,45 @@ internal final class DataUploader: DataUploaderType {
3234

3335
/// Uploads data synchronously (will block current thread) and returns the upload status.
3436
/// Uses timeout configured for `HTTPClient`.
35-
func upload(events: [Event], context: DatadogContext) throws -> DataUploadStatus {
36-
let request = try requestBuilder.request(for: events, with: context)
37+
func upload(events: [Event], context: DatadogContext, previous: DataUploadStatus?) throws -> DataUploadStatus {
38+
var request = try requestBuilder.request(for: events, with: context)
39+
let attempt: UInt
40+
if let previous = previous {
41+
attempt = previous.attempt + 1
42+
} else {
43+
attempt = 0
44+
}
45+
3746
let requestID = request.value(forHTTPHeaderField: URLRequestBuilder.HTTPHeader.ddRequestIDHeaderField)
3847

48+
// add retry related information
49+
request.url?.updateDDTagsQueryItem(key: "retry_count", value: "\(attempt + 1)")
50+
if let responseCode = previous?.responseCode {
51+
request.url?.updateDDTagsQueryItem(key: "last_failure_status", value: "\(responseCode)")
52+
}
53+
54+
// set idempotency key for POST
55+
if let body = request.httpBody {
56+
request.setValue(body.sha1(), forHTTPHeaderField: "DD-IDEMPOTENCY-KEY")
57+
}
58+
3959
var uploadStatus: DataUploadStatus?
4060

4161
let semaphore = DispatchSemaphore(value: 0)
4262

4363
httpClient.send(request: request) { result in
4464
switch result {
4565
case .success(let httpResponse):
46-
uploadStatus = DataUploadStatus(httpResponse: httpResponse, ddRequestID: requestID)
66+
uploadStatus = DataUploadStatus(
67+
httpResponse: httpResponse,
68+
ddRequestID: requestID,
69+
attempt: attempt
70+
)
4771
case .failure(let error):
48-
uploadStatus = DataUploadStatus(networkError: error)
72+
uploadStatus = DataUploadStatus(
73+
networkError: error,
74+
attempt: attempt
75+
)
4976
}
5077

5178
semaphore.signal()

0 commit comments

Comments
 (0)