Skip to content

Commit b6d5534

Browse files
SWIFT-1466 Implement updateAverageRoundTripTime for ServerDescription (#736)
1 parent b6bec73 commit b6d5534

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

Sources/MongoSwift/SDAM.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ public struct ServerDescription {
142142
/// The duration in milliseconds of the server's last "hello" call.
143143
public let roundTripTime: Int?
144144

145+
/// The average duration in milliseconds of the server's "hello" calls.
146+
internal var averageRoundTripTimeMS: Double?
147+
145148
/// The date of the most recent write operation seen by this server.
146149
public var lastWriteDate: Date?
147150

@@ -194,6 +197,9 @@ public struct ServerDescription {
194197
self.address = ServerAddress(mongoc_server_description_host(description))
195198
self.serverId = mongoc_server_description_id(description)
196199
self.roundTripTime = Int(mongoc_server_description_round_trip_time(description))
200+
// averageRoundTripTimeMS is an internally-calculated value used for server selection. Because we still rely
201+
// upon libmongoc for server selection, this value is not relevant.
202+
self.averageRoundTripTimeMS = nil
197203
self.lastUpdateTime = Date(msSinceEpoch: mongoc_server_description_last_update_time(description))
198204
self.type = ServerType(rawValue: String(cString: mongoc_server_description_type(description))) ?? .unknown
199205

@@ -233,6 +239,7 @@ public struct ServerDescription {
233239
// these fields are not used by the server selection tests
234240
self.serverId = 0
235241
self.roundTripTime = 0
242+
self.averageRoundTripTimeMS = nil
236243
self.lastUpdateTime = Date()
237244
self.lastWriteDate = nil
238245
self.minWireVersion = 0
@@ -247,6 +254,31 @@ public struct ServerDescription {
247254
self.passives = []
248255
self.arbiters = []
249256
}
257+
258+
// Used for RTT calculation tests.
259+
internal init(averageRoundTripTimeMS: Double?) {
260+
self.averageRoundTripTimeMS = averageRoundTripTimeMS
261+
262+
// these fields are not used by the RTT calculation tests
263+
self.address = ServerAddress(host: "test", port: 0)
264+
self.type = .unknown
265+
self.tags = [:]
266+
self.serverId = 0
267+
self.roundTripTime = nil
268+
self.lastWriteDate = nil
269+
self.minWireVersion = 0
270+
self.maxWireVersion = 0
271+
self.me = nil
272+
self.hosts = []
273+
self.arbiters = []
274+
self.passives = []
275+
self.setName = nil
276+
self.setVersion = nil
277+
self.electionID = nil
278+
self.primary = nil
279+
self.lastUpdateTime = Date()
280+
self.logicalSessionTimeoutMinutes = nil
281+
}
250282
}
251283

252284
extension ServerDescription: Equatable {
@@ -447,3 +479,14 @@ extension TopologyDescription {
447479
return []
448480
}
449481
}
482+
483+
extension ServerDescription {
484+
internal mutating func updateAverageRoundTripTime(roundTripTime: Double) {
485+
if let oldAverageRTT = self.averageRoundTripTimeMS {
486+
let alpha = 0.2
487+
self.averageRoundTripTimeMS = alpha * roundTripTime + (1 - alpha) * oldAverageRTT
488+
} else {
489+
self.averageRoundTripTimeMS = roundTripTime
490+
}
491+
}
492+
}

Sources/TestsCommon/CodableExtensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ extension ServerDescription: StrictDecodable {
205205
let address = try ServerAddress(try values.decode(String.self, forKey: .address))
206206
let type = try values.decode(ServerType.self, forKey: .type)
207207
let tags = try values.decodeIfPresent([String: String].self, forKey: .tags) ?? [:]
208-
// TODO: SWIFT-1461: decode and set averageRoundTripTimeMS
208+
// TODO: SWIFT-1456: decode and set averageRoundTripTimeMS
209209

210210
self.init(address: address, type: type, tags: tags)
211211
}

Tests/MongoSwiftTests/ServerSelectionTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ private enum OperationType: String, Decodable {
2222
case read, write
2323
}
2424

25+
private struct RTTCalculationTestFile: Decodable {
26+
let averageRoundTripTimeMS: Double?
27+
let newRoundTripTimeMS: Double
28+
let newAverageRoundTripTimeMS: Double
29+
30+
enum CodingKeys: String, CodingKey {
31+
case averageRoundTripTimeMS = "avg_rtt_ms", newRoundTripTimeMS = "new_rtt_ms",
32+
newAverageRoundTripTimeMS = "new_avg_rtt"
33+
}
34+
35+
internal init(from decoder: Decoder) throws {
36+
let values = try decoder.container(keyedBy: CodingKeys.self)
37+
// The tests specify a non-present initial average RTT as "NULL", so if decoding to a Double fails, ignore and
38+
// set to nil.
39+
self.averageRoundTripTimeMS = try? values.decode(Double.self, forKey: .averageRoundTripTimeMS)
40+
self.newRoundTripTimeMS = try values.decode(Double.self, forKey: .newRoundTripTimeMS)
41+
self.newAverageRoundTripTimeMS = try values.decode(Double.self, forKey: .newAverageRoundTripTimeMS)
42+
}
43+
}
44+
2545
final class ServerSelectionTests: MongoSwiftTestCase {
2646
func testServerSelectionLogic() throws {
2747
let tests = try retrieveSpecTestFiles(
@@ -38,4 +58,18 @@ final class ServerSelectionTests: MongoSwiftTestCase {
3858
expect(selectedServers).to(contain(test.suitableServers))
3959
}
4060
}
61+
62+
func testRoundTripTimeCalculation() throws {
63+
let tests = try retrieveSpecTestFiles(
64+
specName: "server-selection",
65+
subdirectory: "rtt",
66+
asType: RTTCalculationTestFile.self
67+
)
68+
for (filename, test) in tests {
69+
print("Running test from \(filename)...")
70+
var serverDescription = ServerDescription(averageRoundTripTimeMS: test.averageRoundTripTimeMS)
71+
serverDescription.updateAverageRoundTripTime(roundTripTime: test.newRoundTripTimeMS)
72+
expect(serverDescription.averageRoundTripTimeMS).to(equal(test.newAverageRoundTripTimeMS))
73+
}
74+
}
4175
}

0 commit comments

Comments
 (0)