-
-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fix] Query Hangs if Connection is Closed #487
Changes from 18 commits
9206b25
5ff285b
3e49ef1
c2192a7
a288cc2
d98944a
00c11b6
2ed7946
e92b2f0
e127327
9ff54d9
fe8f9fe
2a43b88
e41c922
607f26e
f00099b
c43e781
8fe12f5
843d6a0
3e56e49
552c913
c97e127
138aea3
22db6e4
ceaa688
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -359,5 +359,4 @@ final class IntegrationTests: XCTestCase { | |
XCTAssertEqual(obj?.bar, 2) | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -638,6 +638,63 @@ class PostgresConnectionTests: XCTestCase { | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
func testQueriesFailIfConnectionIsClosed() async throws { | ||||||||||||||||||||||||||||||
let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try await connection.closeGracefully() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
XCTAssertEqual(channel.isActive, false) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
do { | ||||||||||||||||||||||||||||||
_ = try await connection.query("SELECT version;", logger: self.logger) | ||||||||||||||||||||||||||||||
XCTFail("Expected to fail") | ||||||||||||||||||||||||||||||
} catch let error as ChannelError { | ||||||||||||||||||||||||||||||
XCTAssertEqual(error, .ioOnClosedChannel) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
func testListenFailsIfConnectionIsClosed() async throws { | ||||||||||||||||||||||||||||||
let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try await connection.closeGracefully() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
XCTAssertEqual(channel.isActive, false) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
do { | ||||||||||||||||||||||||||||||
_ = try await connection.listen("test_channel") | ||||||||||||||||||||||||||||||
XCTFail("Expected to fail") | ||||||||||||||||||||||||||||||
} catch let error as ChannelError { | ||||||||||||||||||||||||||||||
XCTAssertEqual(error, .ioOnClosedChannel) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
func testListenFailsIfConnectionIsClosedMidway() async throws { | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What connection state do you want to test here? In which connection state does this happen? Can we mock this explicitly and not by accident? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test is consistent, it only waits 3 seconds to make sure the listener has started listening. It tests that if there is a listen in process, and then the connection is closed, the listener sequence needs to fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah! in that case we should wait for the client messages on the channel instead of using a timer. See here how it is done: postgres-nio/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift Lines 83 to 96 in 7b621c1
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the clues, I did this in the new test. @fabianfett i think i no longer need to make another PR out of this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fabianfett I think this PR's changes are good, but I noticed this results in a fatal error regardless of my changes: func testAddListenerFailsIfConnectionIsClosed() async throws {
let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
connection.addListener(channel: "example") { context, notification in
XCTFail("Did not expect to receive")
}
try await connection.close()
XCTAssertEqual(channel.isActive, false)
} |
||||||||||||||||||||||||||||||
let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try await withThrowingTaskGroup(of: Void.self) { taskGroup in | ||||||||||||||||||||||||||||||
taskGroup.addTask { | ||||||||||||||||||||||||||||||
do { | ||||||||||||||||||||||||||||||
let listenSequence = try await connection.listen("test_channel") | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
for try await _ in listenSequence { | ||||||||||||||||||||||||||||||
XCTFail("Expected to fail") | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} catch let error as PSQLError { | ||||||||||||||||||||||||||||||
XCTAssertEqual(error.code, .listenFailed) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
taskGroup.addTask { | ||||||||||||||||||||||||||||||
try await Task.sleep(for: .seconds(3)) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try await connection.close().get() | ||||||||||||||||||||||||||||||
MahdiBM marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||
XCTAssertEqual(channel.isActive, false) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
try await taskGroup.waitForAll() | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fabianfett This is related to #458? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. revamped this test so this discussion is no longer valid. Though #487 (comment) is related to the crash. |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
func makeTestConnectionWithAsyncTestingChannel() async throws -> (PostgresConnection, NIOAsyncTestingChannel) { | ||||||||||||||||||||||||||||||
let eventLoop = NIOAsyncTestingEventLoop() | ||||||||||||||||||||||||||||||
let channel = await NIOAsyncTestingChannel(handlers: [ | ||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://github.com/vapor/postgres-nio/pull/487/files#r1647790890.
This is needed otherwise things will crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we get this fix into another pr with a dedicated unit test case and explanation why this can happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm sure. I'll remove the related test/changes from this PR.
EDIT: See #487 (comment)