Skip to content
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 crash in PoolStateMachine+ConnectionGroup when closing connection while keepAlive is running #444

Merged
merged 11 commits into from
Dec 12, 2023

Conversation

lovetodream
Copy link
Contributor

Fixes: #443

@gwynne gwynne requested a review from fabianfett December 11, 2023 18:37
@codecov-commenter
Copy link

codecov-commenter commented Dec 11, 2023

Codecov Report

Merging #444 (e0438af) into main (54f491c) will increase coverage by 0.10%.
The diff coverage is 88.88%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #444      +/-   ##
==========================================
+ Coverage   59.72%   59.82%   +0.10%     
==========================================
  Files         124      124              
  Lines        9889     9915      +26     
==========================================
+ Hits         5906     5932      +26     
  Misses       3983     3983              
Files Coverage Δ
Sources/ConnectionPoolModule/ConnectionPool.swift 94.42% <100.00%> (+1.59%) ⬆️
...nPoolModule/PoolStateMachine+ConnectionState.swift 84.89% <100.00%> (+0.29%) ⬆️
...ources/ConnectionPoolModule/PoolStateMachine.swift 88.82% <80.00%> (+0.15%) ⬆️
...nPoolModule/PoolStateMachine+ConnectionGroup.swift 88.05% <89.47%> (-0.48%) ⬇️

... and 3 files with indirect coverage changes

@fabianfett fabianfett added semver-patch No public API change. ConnectionPool Features and bugs that are related to the impl in ConnectionPoolModule labels Dec 12, 2023
Copy link
Collaborator

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great progress! However we need to go a bit deeper at some points.

@@ -449,6 +449,21 @@ extension PoolStateMachine {
return (index, context)
}

/// Returns `true` if the connection should be explicitly closed, `false` if nothing needs to be done.
@inlinable
mutating func keepAliveFailed(_ connectionID: Connection.ID) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we return an enum for this? KeepAliveFailedAction with two cases: close and none? Makes this more explicit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now returning an optional CloseAction. It should be explicit enough now, let me know if not.

mutating func keepAliveFailed(_ connectionID: Connection.ID) -> Bool {
// We don't have to inform the ConnectionStates about any of this, because the connection will close
// immediately after this or is closed already
switch self.connections.first(where: { $0.id == connectionID })?.state {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please don‘t access the connection‘s state here. the state is only visible here, to allow inlining. Instead please forward a call keepAliveFailed into the ConnectionStateMachine. Also make sure to change the state to closing, if we need to explicitly close the connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection state is receiving the failure now and changes the state accordingly.

@inlinable
mutating func connectionKeepAliveFailed(_ connection: Connection) -> Action {
if self.connections.keepAliveFailed(connection.id) {
return self.connectionClosed(connection)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn‘t look right, if we the keep alive failed, we can‘t declare the connection dead right away, we need to create a close action. Also if we close and have waiting requests, we might need to create a new connection right away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection is now properly closed by reusing the existing closed states and actions. I've added a test to ensure a new connection is established if required.

Tests/ConnectionPoolModuleTests/Mocks/MockConnection.swift Outdated Show resolved Hide resolved
Copy link
Collaborator

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superb PR. Thanks so much for looking into this!

Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift Outdated Show resolved Hide resolved
Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift Outdated Show resolved Hide resolved
@fabianfett fabianfett merged commit e60e495 into vapor:main Dec 12, 2023
13 checks passed
@fabianfett
Copy link
Collaborator

Thanks @lovetodream. Great catch and great fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ConnectionPool Features and bugs that are related to the impl in ConnectionPoolModule semver-patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Connection Pool Crash: Precondition failure when ping and close happen at the same time
4 participants