Skip to content

Conversation

@adam-fowler
Copy link
Contributor

@adam-fowler adam-fowler commented Dec 9, 2025

This PR fixes ConnectionPool.triggerForceShutdown() so that it actually shuts down the connection pool and the run() method returns.

I renamed the shutdown connection pool action to initiateShutdown and added a new action shutdown. The initiateShutdown action will close all the connections and related timers and is returned by triggerForceShutdown. The shutdown action will finish the eventStream thus allowing the run method to return. The connectionClosed method will return the shutdown action once the connections array is empty. There is also support for dealing with connections that are established after triggerForceShutdown is called which wasn't there before.

I have also added tests for the state machine, for when connections are idle, leased or a connection request is in process. And a single ConnectionPool test that checks triggerForceShutdown actually causes the run method to return.

Ensure ConnectionPool.triggerForceShutdown works and that it doesn't shutdown until all connections are closed

@adam-fowler adam-fowler changed the title Ensure ConnectionPool .triggerForceShutdown() works and that it doesn't shutdown until all connections are closed Ensure ConnectionPool.triggerForceShutdown() works and that it doesn't shutdown until all connections are closed Dec 9, 2025
@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 83.60656% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.74%. Comparing base (9217854) to head (f6016ed).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ources/ConnectionPoolModule/PoolStateMachine.swift 73.77% 16 Missing ⚠️
...nPoolModule/PoolStateMachine+ConnectionGroup.swift 93.33% 3 Missing ⚠️
...nPoolModule/PoolStateMachine+ConnectionState.swift 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #607      +/-   ##
==========================================
+ Coverage   75.68%   75.74%   +0.05%     
==========================================
  Files         134      134              
  Lines        9912    10009      +97     
==========================================
+ Hits         7502     7581      +79     
- Misses       2410     2428      +18     
Files with missing lines Coverage Δ
Sources/ConnectionPoolModule/ConnectionPool.swift 94.88% <100.00%> (+0.07%) ⬆️
...nPoolModule/PoolStateMachine+ConnectionState.swift 86.61% <91.66%> (+0.01%) ⬆️
...nPoolModule/PoolStateMachine+ConnectionGroup.swift 89.64% <93.33%> (+0.79%) ⬆️
...ources/ConnectionPoolModule/PoolStateMachine.swift 86.07% <73.77%> (-2.89%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

}

@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@Test func testForceShutdown() async throws {
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 add a second test that tests, that we even close leased connections?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added, plus I added an extra test that ensure we shutdown correctly if a connection request is in progress

index: Int,
availableContext: ConnectionGroup.AvailableConnectionContext
availableContext: ConnectionGroup.AvailableConnectionContext,
shuttingDown: Bool
Copy link
Collaborator

Choose a reason for hiding this comment

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

we should not pass around shuttingDown here and instead rely on self.state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

case shutdown(Shutdown)

case initiateShutdown(Shutdown)
case shutdown([TimerCancellationToken])
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is the difference between initiateShutdown and shutdown? can we add a developer comment here please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wanna rename this to cancelEventStreamAndFinalCleanup?

Comment on lines 483 to 506
case .idle(_, let newIdle):
if shuttingDown {
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 use a case ... where here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

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.

Overall this is really, really great. Thanks so much for the improvements!

Now that we keep a record of all connections when shutting down it is possible for a connection to be released while in the closing state.
case .backingOff, .starting, .idle, .closing, .closed:

case .closing:
return nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that we keep a record of all the connections during the shutdown It is now possible to catch a connection in the closing state when release is called after triggerForceShutdown

@fabianfett fabianfett merged commit e017bd9 into vapor:main Dec 11, 2025
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants