Skip to content

ConnectionPool should support draining connections#638

Merged
fabianfett merged 4 commits intovapor:mainfrom
fabianfett:ff-connection-draining
Mar 30, 2026
Merged

ConnectionPool should support draining connections#638
fabianfett merged 4 commits intovapor:mainfrom
fabianfett:ff-connection-draining

Conversation

@fabianfett
Copy link
Copy Markdown
Collaborator

No description provided.

Fix weird states

Test crash fix.
@fabianfett fabianfett requested a review from gwynne as a code owner March 30, 2026 11:20
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 89.80583% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.56%. Comparing base (26b1d68) to head (3026d24).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...nPoolModule/PoolStateMachine+ConnectionState.swift 89.01% 10 Missing ⚠️
...nPoolModule/PoolStateMachine+ConnectionGroup.swift 89.53% 9 Missing ⚠️
...ources/ConnectionPoolModule/PoolStateMachine.swift 91.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #638      +/-   ##
==========================================
+ Coverage   76.39%   76.56%   +0.17%     
==========================================
  Files         135      135              
  Lines       10239    10383     +144     
==========================================
+ Hits         7822     7950     +128     
- Misses       2417     2433      +16     
Files with missing lines Coverage Δ
Sources/ConnectionPoolModule/ConnectionPool.swift 95.17% <100.00%> (+0.48%) ⬆️
Sources/ConnectionPoolModule/Max2Sequence.swift 97.87% <100.00%> (ø)
...tionPoolModule/PoolStateMachine+RequestQueue.swift 100.00% <ø> (ø)
...ources/ConnectionPoolModule/TinyFastSequence.swift 97.60% <100.00%> (ø)
...ources/ConnectionPoolModule/PoolStateMachine.swift 90.13% <91.66%> (-0.07%) ⬇️
...nPoolModule/PoolStateMachine+ConnectionGroup.swift 89.15% <89.53%> (+0.47%) ⬆️
...nPoolModule/PoolStateMachine+ConnectionState.swift 88.21% <89.01%> (-0.17%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@gwynne gwynne left a comment

Choose a reason for hiding this comment

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

Couple of minor remarks, nothing blocking.


@inlinable
mutating func connectionWillClose(_ connectionID: Connection.ID) -> ConnectionWillCloseAction {
guard let index = self.connections.firstIndex(where: { $0.id == connectionID }) else {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

General question: Why is self.connections an Array rather than a Dictionary or OrderedDictionary? I would assume it has something to do with speed on hotter paths than this particular line of code; I just cringe when I see "oh look, O(n) iteration of an ordered collection looking for an easily-hashed identifier".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah, actually O(n) is smaller than calculating O(1) hashes :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'll take your word for it. 😆

Comment on lines +471 to +476
let newUsedStreams = usedStreams - returnedStreams
if newUsedStreams == 0 {
self.state = .closing(connection)
return .drainingComplete(connection)
} else {
self.state = .draining(connection, usedStreams: newUsedStreams)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
let newUsedStreams = usedStreams - returnedStreams
if newUsedStreams == 0 {
self.state = .closing(connection)
return .drainingComplete(connection)
} else {
self.state = .draining(connection, usedStreams: newUsedStreams)
if usedStreams == returnedStreams {
self.state = .closing(connection)
return .drainingComplete(connection)
} else {
self.state = .draining(connection, usedStreams: usedStreams - returnedStreams)

@fabianfett fabianfett merged commit 9b40f0d into vapor:main Mar 30, 2026
10 checks passed
@fabianfett fabianfett deleted the ff-connection-draining branch March 30, 2026 14:31
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