Skip to content

Commit b509e82

Browse files
authored
SWIFT-1293 Add support for loadBalanced option (#660)
1 parent a7f5dba commit b509e82

File tree

5 files changed

+185
-23
lines changed

5 files changed

+185
-23
lines changed

Sources/MongoSwift/ConnectionString.swift

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,6 @@ internal class ConnectionString {
7676
self.readConcern = rc
7777
}
7878

79-
if let replicaSet = options?.replicaSet {
80-
try self.setUTF8Option(MONGOC_URI_REPLICASET, to: replicaSet)
81-
}
82-
8379
if let rr = options?.retryReads {
8480
try self.setBoolOption(MONGOC_URI_RETRYREADS, to: rr)
8581
}
@@ -360,24 +356,15 @@ internal class ConnectionString {
360356

361357
/// Sets and validates SDAM-related on the underlying `mongoc_uri_t`.
362358
private func applyAndValidateSDAMOptions(_ options: MongoClientOptions?) throws {
359+
// First, apply all the options...
360+
361+
if let replicaSet = options?.replicaSet {
362+
try self.setUTF8Option(MONGOC_URI_REPLICASET, to: replicaSet)
363+
}
364+
363365
// Per SDAM spec: If the ``directConnection`` option is not specified, newly developed drivers MUST behave as
364366
// if it was specified with the false value.
365367
if let dc = options?.directConnection {
366-
guard !(dc && self.usesDNSSeedlistFormat) else {
367-
throw MongoError.InvalidArgumentError(
368-
message: "\(MONGOC_URI_DIRECTCONNECTION)=true is incompatible with mongodb+srv connection strings"
369-
)
370-
}
371-
372-
if let hosts = self.hosts {
373-
guard !(dc && hosts.count > 1) else {
374-
throw MongoError.InvalidArgumentError(
375-
message: "\(MONGOC_URI_DIRECTCONNECTION)=true is incompatible with multiple seeds. " +
376-
"got seeds: \(hosts)"
377-
)
378-
}
379-
}
380-
381368
try self.setBoolOption(MONGOC_URI_DIRECTCONNECTION, to: dc)
382369
} else if !self.hasOption(MONGOC_URI_DIRECTCONNECTION) {
383370
try self.setBoolOption(MONGOC_URI_DIRECTCONNECTION, to: false)
@@ -399,6 +386,52 @@ internal class ConnectionString {
399386

400387
try self.setInt32Option(MONGOC_URI_HEARTBEATFREQUENCYMS, to: value)
401388
}
389+
390+
if let loadBalanced = options?.loadBalanced {
391+
try self.setBoolOption(MONGOC_URI_LOADBALANCED, to: loadBalanced)
392+
}
393+
394+
// Then, validate them...
395+
let lb = self.options?[MONGOC_URI_LOADBALANCED]?.boolValue
396+
let dc = self.options?[MONGOC_URI_DIRECTCONNECTION]?.boolValue
397+
let hosts = self.hosts ?? []
398+
399+
if dc == true {
400+
guard !self.usesDNSSeedlistFormat else {
401+
throw MongoError.InvalidArgumentError(
402+
message: "\(MONGOC_URI_DIRECTCONNECTION)=true is incompatible with mongodb+srv connection strings"
403+
)
404+
}
405+
406+
guard lb != true else {
407+
throw MongoError.InvalidArgumentError(
408+
message: "\(MONGOC_URI_DIRECTCONNECTION)=true is incompatible with \(MONGOC_URI_LOADBALANCED)=true"
409+
)
410+
}
411+
412+
guard hosts.count <= 1 else {
413+
throw MongoError.InvalidArgumentError(
414+
message: "\(MONGOC_URI_DIRECTCONNECTION)=true is incompatible with multiple seeds. " +
415+
"got seeds: \(hosts)"
416+
)
417+
}
418+
}
419+
420+
if lb == true {
421+
guard self.replicaSet == nil else {
422+
throw MongoError.InvalidArgumentError(
423+
message: "\(MONGOC_URI_LOADBALANCED)=true is incompatible with replicaSet option: found option " +
424+
"\(MONGOC_URI_REPLICASET)=\(self.replicaSet ?? "")"
425+
)
426+
}
427+
428+
guard hosts.count <= 1 else {
429+
throw MongoError.InvalidArgumentError(
430+
message: "\(MONGOC_URI_LOADBALANCED)=true is incompatible with multiple seeds. " +
431+
"got seeds: \(hosts)"
432+
)
433+
}
434+
}
402435
}
403436

404437
/// Sets and validates server selection-related on the underlying `mongoc_uri_t`.

Sources/MongoSwift/MongoClient.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public struct MongoClientOptions: CodingStrategyProvider {
4040
/// Defaults to 10 seconds (10,000 ms). Must be at least 500ms.
4141
public var heartbeatFrequencyMS: Int?
4242

43+
/// Indicates whether the driver is connecting to a load balancer.
44+
public var loadBalanced: Bool?
45+
4346
/// The size (in milliseconds) of the permitted latency window beyond the fastest round-trip time amongst all
4447
/// servers. By default, only servers within 15ms of the fastest round-trip time receive queries.
4548
public var localThresholdMS: Int?
@@ -145,6 +148,7 @@ public struct MongoClientOptions: CodingStrategyProvider {
145148
dateCodingStrategy: DateCodingStrategy? = nil,
146149
directConnection: Bool? = nil,
147150
heartbeatFrequencyMS: Int? = nil,
151+
loadBalanced: Bool? = nil,
148152
localThresholdMS: Int? = nil,
149153
maxPoolSize: Int? = nil,
150154
readConcern: ReadConcern? = nil,
@@ -173,6 +177,7 @@ public struct MongoClientOptions: CodingStrategyProvider {
173177
self.dateCodingStrategy = dateCodingStrategy
174178
self.directConnection = directConnection
175179
self.heartbeatFrequencyMS = heartbeatFrequencyMS
180+
self.loadBalanced = loadBalanced
176181
self.localThresholdMS = localThresholdMS
177182
self.maxPoolSize = maxPoolSize
178183
self.minHeartbeatFrequencyMS = nil

Tests/MongoSwiftTests/ConnectionStringTests.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,13 @@ let shouldWarnButLibmongocErrors: [String: [String]] = [
109109

110110
// tests we skip because we don't support the specified behavior.
111111
let skipUnsupported: [String: [String]] = [
112-
// we don't support maxIdleTimeMS.
113112
"connection-pool-options.json": [
113+
// we don't support maxIdleTimeMS.
114114
"Too low maxIdleTimeMS causes a warning",
115115
"Non-numeric maxIdleTimeMS causes a warning",
116-
"Valid connection pool options are parsed correctly"
116+
"Valid connection pool options are parsed correctly",
117+
// We don't support minPoolSize.
118+
"minPoolSize=0 does not error"
117119
],
118120
// requires maxIdleTimeMS
119121
"connection-options.json": [
@@ -581,5 +583,33 @@ final class ConnectionStringTests: MongoSwiftTestCase {
581583
// directConnection=true cannot be used with multiple seeds
582584
expect(try ConnectionString("mongodb://localhost:27017,localhost:27018", options: opts))
583585
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
586+
587+
// The cases where the conflicting options are all in the connection string are already covered by the URI
588+
// options tests, so here we only check behavior for cases where 1+ option is specified via the options struct.
589+
590+
// loadBalanced=true cannot be used with multiple seeds
591+
opts = MongoClientOptions(loadBalanced: true)
592+
expect(try ConnectionString("mongodb://localhost:27017,localhost:27018", options: opts))
593+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
594+
595+
// loadBalanced=true cannot be used with replica set option
596+
expect(try ConnectionString("mongodb://localhost:27017/?replicaSet=xyz", options: opts))
597+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
598+
opts.replicaSet = "xyz"
599+
expect(try ConnectionString("mongodb://localhost:27017", options: opts))
600+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
601+
602+
// loadBalanced=true cannot be used with directConnection=true
603+
opts = MongoClientOptions(directConnection: true, loadBalanced: true)
604+
expect(try ConnectionString("mongodb://localhost:27017", options: opts))
605+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
606+
607+
opts = MongoClientOptions(directConnection: true)
608+
expect(try ConnectionString("mongodb://localhost:27017/?loadBalanced=true", options: opts))
609+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
610+
611+
opts = MongoClientOptions(loadBalanced: true)
612+
expect(try ConnectionString("mongodb://localhost:27017/?directConnection=true", options: opts))
613+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
584614
}
585615
}

Tests/Specs/uri-options/tests/connection-options.json

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,76 @@
168168
"hosts": null,
169169
"auth": null,
170170
"options": {}
171+
},
172+
{
173+
"description": "loadBalanced=true",
174+
"uri": "mongodb://example.com/?loadBalanced=true",
175+
"valid": true,
176+
"warning": false,
177+
"hosts": null,
178+
"auth": null,
179+
"options": {
180+
"loadBalanced": true
181+
}
182+
},
183+
{
184+
"description": "loadBalanced=true with directConnection=false",
185+
"uri": "mongodb://example.com/?loadBalanced=true&directConnection=false",
186+
"valid": true,
187+
"warning": false,
188+
"hosts": null,
189+
"auth": null,
190+
"options": {
191+
"loadBalanced": true,
192+
"directConnection": false
193+
}
194+
},
195+
{
196+
"description": "loadBalanced=false",
197+
"uri": "mongodb://example.com/?loadBalanced=false",
198+
"valid": true,
199+
"warning": false,
200+
"hosts": null,
201+
"auth": null,
202+
"options": {
203+
"loadBalanced": false
204+
}
205+
},
206+
{
207+
"description": "Invalid loadBalanced value",
208+
"uri": "mongodb://example.com/?loadBalanced=1",
209+
"valid": true,
210+
"warning": true,
211+
"hosts": null,
212+
"auth": null,
213+
"options": {}
214+
},
215+
{
216+
"description": "loadBalanced=true with multiple hosts causes an error",
217+
"uri": "mongodb://example1,example2/?loadBalanced=true",
218+
"valid": false,
219+
"warning": false,
220+
"hosts": null,
221+
"auth": null,
222+
"options": {}
223+
},
224+
{
225+
"description": "loadBalanced=true with directConnection=true causes an error",
226+
"uri": "mongodb://example.com/?loadBalanced=true&directConnection=true",
227+
"valid": false,
228+
"warning": false,
229+
"hosts": null,
230+
"auth": null,
231+
"options": {}
232+
},
233+
{
234+
"description": "loadBalanced=true with replicaSet causes an error",
235+
"uri": "mongodb://example.com/?loadBalanced=true&replicaSet=replset",
236+
"valid": false,
237+
"warning": false,
238+
"hosts": null,
239+
"auth": null,
240+
"options": {}
171241
}
172242
]
173243
}

Tests/Specs/uri-options/tests/connection-pool-options.json

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
"tests": [
33
{
44
"description": "Valid connection pool options are parsed correctly",
5-
"uri": "mongodb://example.com/?maxIdleTimeMS=50000",
5+
"uri": "mongodb://example.com/?maxIdleTimeMS=50000&maxPoolSize=5&minPoolSize=3",
66
"valid": true,
77
"warning": false,
88
"hosts": null,
99
"auth": null,
1010
"options": {
11-
"maxIdleTimeMS": 50000
11+
"maxIdleTimeMS": 50000,
12+
"maxPoolSize": 5,
13+
"minPoolSize": 3
1214
}
1315
},
1416
{
@@ -28,6 +30,28 @@
2830
"hosts": null,
2931
"auth": null,
3032
"options": {}
33+
},
34+
{
35+
"description": "maxPoolSize=0 does not error",
36+
"uri": "mongodb://example.com/?maxPoolSize=0",
37+
"valid": true,
38+
"warning": false,
39+
"hosts": null,
40+
"auth": null,
41+
"options": {
42+
"maxPoolSize": 0
43+
}
44+
},
45+
{
46+
"description": "minPoolSize=0 does not error",
47+
"uri": "mongodb://example.com/?minPoolSize=0",
48+
"valid": true,
49+
"warning": false,
50+
"hosts": null,
51+
"auth": null,
52+
"options": {
53+
"minPoolSize": 0
54+
}
3155
}
3256
]
3357
}

0 commit comments

Comments
 (0)