Skip to content

Commit 33eea14

Browse files
authored
Ignore errors when closing the connection (#151)
1 parent d97f268 commit 33eea14

File tree

6 files changed

+41
-13
lines changed

6 files changed

+41
-13
lines changed

Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,7 @@ struct ConnectionStateMachine {
313313
.waitingToStartAuthentication,
314314
.authenticated,
315315
.readyForQuery,
316-
.error,
317-
.closing:
316+
.error:
318317
return self.closeConnectionAndCleanup(.server(errorMessage))
319318
case .authenticating(var authState):
320319
if authState.isComplete {
@@ -352,6 +351,13 @@ struct ConnectionStateMachine {
352351
machine.state = .prepareStatement(preparedState, connectionContext)
353352
return machine.modify(with: action)
354353
}
354+
case .closing:
355+
// If the state machine is in state `.closing`, the connection shutdown was initiated
356+
// by the client. This means a `TERMINATE` message has already been sent and the
357+
// connection close was passed on to the channel. Therefore we await a channelInactive
358+
// as the next event.
359+
// Since a connection close was already issued, we should keep cool and just wait.
360+
return .wait
355361
case .initialized, .closed:
356362
preconditionFailure("We should not receive server errors if we are not connected")
357363
case .modifying:
@@ -367,8 +373,7 @@ struct ConnectionStateMachine {
367373
.sslHandlerAdded,
368374
.waitingToStartAuthentication,
369375
.authenticated,
370-
.readyForQuery,
371-
.closing:
376+
.readyForQuery:
372377
return self.closeConnectionAndCleanup(error)
373378
case .authenticating(var authState):
374379
let action = authState.errorHappened(error)
@@ -396,6 +401,16 @@ struct ConnectionStateMachine {
396401
}
397402
case .error:
398403
return .wait
404+
case .closing:
405+
// If the state machine is in state `.closing`, the connection shutdown was initiated
406+
// by the client. This means a `TERMINATE` message has already been sent and the
407+
// connection close was passed on to the channel. Therefore we await a channelInactive
408+
// as the next event.
409+
// For some reason Azure Postgres does not end ssl cleanly when terminating the
410+
// connection. More documentation can be found in the issue:
411+
// https://github.com/vapor/postgres-nio/issues/150
412+
// Since a connection close was already issued, we should keep cool and just wait.
413+
return .wait
399414
case .closed:
400415
return self.closeConnectionAndCleanup(error)
401416

@@ -1108,8 +1123,8 @@ struct AuthContext: Equatable, CustomDebugStringConvertible {
11081123

11091124
var debugDescription: String {
11101125
"""
1111-
(username: \(String(reflecting: self.username)), \
1112-
password: \(self.password != nil ? String(reflecting: self.password!) : "nil"), \
1126+
AuthContext(username: \(String(reflecting: self.username)), \
1127+
password: \(self.password != nil ? "********" : "nil"), \
11131128
database: \(self.database != nil ? String(reflecting: self.database!) : "nil"))
11141129
"""
11151130
}

Sources/PostgresNIO/New/PSQLChannelHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ final class PSQLChannelHandler: ChannelDuplexHandler {
7575
}
7676

7777
func errorCaught(context: ChannelHandlerContext, error: Error) {
78-
self.logger.error("Channel error caught.", metadata: [.error: "\(error)"])
78+
self.logger.debug("Channel error caught.", metadata: [.error: "\(error)"])
7979
let action = self.state.errorHappened(.channel(underlying: error))
8080
self.run(action, with: context)
8181
}
@@ -470,7 +470,7 @@ final class PSQLChannelHandler: ChannelDuplexHandler {
470470
_ cleanup: ConnectionStateMachine.ConnectionAction.CleanUpContext,
471471
context: ChannelHandlerContext)
472472
{
473-
self.logger.error("Channel error caught. Closing connection.", metadata: [.error: "\(cleanup.error)"])
473+
self.logger.debug("Cleaning up and closing connection.", metadata: [.error: "\(cleanup.error)"])
474474

475475
// 1. fail all tasks
476476
cleanup.tasks.forEach { task in

Sources/PostgresNIO/New/PSQLConnection.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,6 @@ final class PSQLConnection {
140140

141141
self.channel.write(PSQLTask.extendedQuery(context), promise: nil)
142142

143-
// success is logged in PSQLQuery
144-
promise.futureResult.whenFailure { error in
145-
logger.error("Query failed", metadata: [.error: "\(error)"])
146-
}
147143
return promise.futureResult
148144
}
149145

Sources/PostgresNIO/New/PSQLRows.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ final class PSQLRows {
9999
}
100100

101101
internal func noticeReceived(_ notice: PSQLBackendMessage.NoticeResponse) {
102-
self.logger.notice("Notice Received \(notice)")
102+
self.logger.debug("Notice Received", metadata: [
103+
.notice: "\(notice)"
104+
])
103105
}
104106

105107
internal func finalForward(_ finalForward: Result<(CircularBuffer<[PSQLData]>, commandTag: String), PSQLError>?) {

Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ class ConnectionStateMachineTests: XCTestCase {
9191
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .unexpectedBackendMessage(.readyForQuery(.idle)), closePromise: nil)))
9292
}
9393

94+
func testErrorIsIgnoredWhenClosingConnection() {
95+
// test ignore unclean shutdown when closing connection
96+
var stateIgnoreChannelError = ConnectionStateMachine(.closing)
97+
98+
XCTAssertEqual(stateIgnoreChannelError.errorHappened(.channel(underlying: NIOSSLError.uncleanShutdown)), .wait)
99+
XCTAssertEqual(stateIgnoreChannelError.closed(), .fireChannelInactive)
100+
101+
// test ignore any other error when closing connection
102+
103+
var stateIgnoreErrorMessage = ConnectionStateMachine(.closing)
104+
XCTAssertEqual(stateIgnoreErrorMessage.errorReceived(.init(fields: [:])), .wait)
105+
XCTAssertEqual(stateIgnoreErrorMessage.closed(), .fireChannelInactive)
106+
}
94107

95108
func testFailQueuedQueriesOnAuthenticationFailure() throws {
96109
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)

Tests/PostgresNIOTests/New/Extensions/ConnectionAction+TestUtils.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ extension ConnectionStateMachine.ConnectionAction: Equatable {
7474
return lhsName == rhsName && lhsQuery == rhsQuery
7575
case (.succeedPreparedStatementCreation(let lhsContext, let lhsRowDescription), .succeedPreparedStatementCreation(let rhsContext, let rhsRowDescription)):
7676
return lhsContext === rhsContext && lhsRowDescription == rhsRowDescription
77+
case (.fireChannelInactive, .fireChannelInactive):
78+
return true
7779
default:
7880
return false
7981
}

0 commit comments

Comments
 (0)