Skip to content

Commit d63c53d

Browse files
authored
SWIFT-1459 Handle errors that occur when opening connection pool (#738)
1 parent d496b17 commit d63c53d

File tree

1 file changed

+52
-20
lines changed

1 file changed

+52
-20
lines changed

Sources/MongoSwift/ConnectionPool.swift

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ internal class ConnectionPool {
4545
case opening(future: EventLoopFuture<OpaquePointer>)
4646
/// Indicates that the `ConnectionPool` is open and using the associated pointer to a `mongoc_client_pool_t`.
4747
case open(pool: OpaquePointer)
48+
/// Indicates that the `ConnectionPool` failed to open due to the associated error.
49+
case failedToOpen(error: Error)
4850
/// Indicates that the `ConnectionPool` is in the process of closing. Connections can be checked back in, but
4951
/// no new connections can be checked out.
5052
case closing(pool: OpaquePointer)
@@ -86,23 +88,38 @@ internal class ConnectionPool {
8688
) throws {
8789
let poolFut = executor.execute(on: nil) { () -> OpaquePointer in
8890
try connString.withMongocURI { uriPtr in
89-
guard let pool = mongoc_client_pool_new(uriPtr) else {
90-
throw MongoError.InternalError(message: "Failed to initialize libmongoc client pool")
91+
var error = bson_error_t()
92+
// this can fail if e.g. an invalid option is specified via a TXT record.
93+
guard let pool = mongoc_client_pool_new_with_error(uriPtr, &error) else {
94+
throw extractMongoError(error: error)
9195
}
9296

93-
guard mongoc_client_pool_set_error_api(pool, MONGOC_ERROR_API_VERSION_2) else {
94-
fatalError("Could not configure error handling on client pool")
95-
}
96-
97-
// We always set min_heartbeat_frequency because the hard-coded default in the vendored mongoc
98-
// was lowered to 50. Setting it here brings it to whatever was specified, or 500 if it wasn't.
99-
mongoc_client_pool_set_min_heartbeat_frequency_msec(pool, UInt64(connString.minHeartbeatFrequencyMS))
97+
do {
98+
// this should only fail due to an error of our own, e.g. calling this method multiple times or
99+
// passing in an invalid error API version.
100+
guard mongoc_client_pool_set_error_api(pool, MONGOC_ERROR_API_VERSION_2) else {
101+
throw MongoError.InternalError(message: "Could not configure error handling on client pool")
102+
}
100103

101-
try serverAPI?.withMongocServerAPI { apiPtr in
102-
var error = bson_error_t()
103-
guard mongoc_client_pool_set_server_api(pool, apiPtr, &error) else {
104-
throw extractMongoError(error: error)
104+
// We always set min_heartbeat_frequency because the hard-coded default in the vendored mongoc
105+
// was lowered to 50. Setting it here brings it to whatever was specified, or 500 if it wasn't.
106+
mongoc_client_pool_set_min_heartbeat_frequency_msec(
107+
pool,
108+
UInt64(connString.minHeartbeatFrequencyMS)
109+
)
110+
111+
// this should only fail due to an error of our own, e.g. calling this method multiple times or
112+
// passing in an invalid API version.
113+
try serverAPI?.withMongocServerAPI { apiPtr in
114+
guard mongoc_client_pool_set_server_api(pool, apiPtr, &error) else {
115+
throw extractMongoError(error: error)
116+
}
105117
}
118+
} catch {
119+
// manually clean up the pool since we won't return it, which would be necessary for the usual
120+
// cleanup logic to execute.
121+
mongoc_client_pool_destroy(pool)
122+
throw error
106123
}
107124

108125
return pool
@@ -130,9 +147,16 @@ internal class ConnectionPool {
130147
case let .open(pool):
131148
return try body(pool)
132149
case let .opening(future):
133-
let pool = try future.wait()
134-
self.state = .open(pool: pool)
135-
return try body(pool)
150+
do {
151+
let pool = try future.wait()
152+
self.state = .open(pool: pool)
153+
return try body(pool)
154+
} catch {
155+
self.state = .failedToOpen(error: error)
156+
throw error
157+
}
158+
case let .failedToOpen(error):
159+
throw error
136160
case .closed, .closing:
137161
throw Self.PoolClosedError
138162
}
@@ -147,10 +171,18 @@ internal class ConnectionPool {
147171
case let .open(pool):
148172
self.state = .closing(pool: pool)
149173
case let .opening(future):
150-
let pool = try future.wait()
151-
self.state = .closing(pool: pool)
174+
do {
175+
let pool = try future.wait()
176+
self.state = .closing(pool: pool)
177+
} catch {
178+
self.state = .closed
179+
return
180+
}
152181
case .closing, .closed:
153182
throw Self.PoolClosedError
183+
case .failedToOpen:
184+
self.state = .closed
185+
return
154186
}
155187

156188
while self.connCount > 0 {
@@ -159,7 +191,7 @@ internal class ConnectionPool {
159191
}
160192

161193
switch self.state {
162-
case .open, .closed, .opening:
194+
case .open, .closed, .opening, .failedToOpen:
163195
throw MongoError.InternalError(
164196
message: "ConnectionPool in unexpected state \(self.state) during close()"
165197
)
@@ -209,7 +241,7 @@ internal class ConnectionPool {
209241
self.stateLock.signal()
210242
case .closed:
211243
throw Self.PoolClosedError
212-
case .opening:
244+
case .opening, .failedToOpen:
213245
fatalError("ConnectionPool in unexpected state \(self.state) while checking in a connection")
214246
}
215247
}

0 commit comments

Comments
 (0)