-
Notifications
You must be signed in to change notification settings - Fork 420
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
Add a minimum connections configuration to the ConnectionPool #1822
Merged
Merged
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
6583d18
Add a minimum connections configuration to the ConnectionPool
gjcairo 64c6caa
Formatting
gjcairo c99f508
PR changes
gjcairo 621dadc
Formatting
gjcairo 3606b44
PR changes
gjcairo b066219
Rename min connections config
gjcairo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,6 +85,11 @@ internal final class ConnectionPool { | |
@usableFromInline | ||
internal let maxWaiters: Int | ||
|
||
/// The number of connections in the pool that should always be kept open (i.e. they won't go idle). | ||
/// In other words, it's the number of connections for which we should ignore idle timers. | ||
@usableFromInline | ||
internal let minConnections: Int | ||
|
||
/// Configuration for backoff between subsequence connection attempts. | ||
@usableFromInline | ||
internal let connectionBackoff: ConnectionBackoff | ||
|
@@ -157,6 +162,7 @@ internal final class ConnectionPool { | |
init( | ||
eventLoop: EventLoop, | ||
maxWaiters: Int, | ||
minConnections: Int, | ||
reservationLoadThreshold: Double, | ||
assumedMaxConcurrentStreams: Int, | ||
connectionBackoff: ConnectionBackoff, | ||
|
@@ -176,6 +182,7 @@ internal final class ConnectionPool { | |
|
||
self._connections = [:] | ||
self.maxWaiters = maxWaiters | ||
self.minConnections = minConnections | ||
self.waiters = CircularBuffer(initialCapacity: 16) | ||
|
||
self.eventLoop = eventLoop | ||
|
@@ -201,17 +208,25 @@ internal final class ConnectionPool { | |
] | ||
) | ||
self._connections.reserveCapacity(connections) | ||
var numberOfKeepOpenConnections = self.minConnections | ||
while self._connections.count < connections { | ||
self.addConnectionToPool() | ||
// If we have less than the minimum number of connections, don't let | ||
// the new connection close when idle. | ||
let idleBehavior = | ||
numberOfKeepOpenConnections > 0 | ||
? ConnectionManager.IdleBehavior.neverGoIdle : .closeWhenIdleTimeout | ||
numberOfKeepOpenConnections -= 1 | ||
self.addConnectionToPool(idleBehavior: idleBehavior) | ||
} | ||
} | ||
|
||
/// Make and add a new connection to the pool. | ||
private func addConnectionToPool() { | ||
private func addConnectionToPool(idleBehavior: ConnectionManager.IdleBehavior) { | ||
let manager = ConnectionManager( | ||
eventLoop: self.eventLoop, | ||
channelProvider: self.channelProvider, | ||
callStartBehavior: .waitsForConnectivity, | ||
idleBehavior: idleBehavior, | ||
connectionBackoff: self.connectionBackoff, | ||
connectivityDelegate: self, | ||
http2Delegate: self, | ||
|
@@ -220,6 +235,19 @@ internal final class ConnectionPool { | |
let id = manager.id | ||
self._connections[id] = PerConnectionState(manager: manager) | ||
self.delegate?.connectionAdded(id: .init(id)) | ||
|
||
// If it's one of the connections that should be kept open, then connect | ||
// straight away. | ||
switch idleBehavior { | ||
case .neverGoIdle: | ||
self.eventLoop.execute { | ||
if manager.sync.isIdle { | ||
manager.sync.startConnecting() | ||
} | ||
} | ||
case .closeWhenIdleTimeout: | ||
() | ||
} | ||
} | ||
|
||
// MARK: - Called from the pool manager | ||
|
@@ -689,8 +717,9 @@ extension ConnectionPool: ConnectionManagerConnectivityDelegate { | |
// Grab the number of reserved streams (before invalidating the index by adding a connection). | ||
let reservedStreams = self._connections.values[index].reservedStreams | ||
|
||
// Replace the connection with a new idle one. | ||
self.addConnectionToPool() | ||
// Replace the connection with a new idle one. Keep the idle behavior, so that | ||
// if it's a connection that should be kept alive, we maintain it. | ||
self.addConnectionToPool(idleBehavior: manager.idleBehavior) | ||
|
||
// Since we're removing this connection from the pool (and no new streams can be created on | ||
// the connection), the pool manager can ignore any streams reserved against this connection. | ||
|
@@ -881,6 +910,22 @@ extension ConnectionPool { | |
return self.pool._connections.values.reduce(0) { $0 &+ ($1.manager.sync.isIdle ? 1 : 0) } | ||
} | ||
|
||
/// The number of active (i.e. connecting or ready) connections in the pool. | ||
internal var activeConnections: Int { | ||
self.pool.eventLoop.assertInEventLoop() | ||
return self.pool._connections.values.reduce(0) { | ||
$0 &+ (($1.manager.sync.isReady || $1.manager.sync.isConnecting) ? 1 : 0) | ||
} | ||
} | ||
|
||
/// The number of connections in the pool in transient failure state. | ||
internal var transientFailureConnections: Int { | ||
self.pool.eventLoop.assertInEventLoop() | ||
return self.pool._connections.values.reduce(0) { | ||
$0 &+ ($1.manager.sync.isTransientFailure ? 1 : 0) | ||
} | ||
} | ||
|
||
Comment on lines
+913
to
+928
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. I think these are unused now? 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, I'm using them in tests. |
||
/// The number of streams currently available to reserve across all connections in the pool. | ||
internal var availableStreams: Int { | ||
self.pool.eventLoop.assertInEventLoop() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
It doesn't look like this property is ever used?
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.
It's used from the
GRPCIdleHandler
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.
Why do we store it here if it's only used in the idle handler? Can't we just pass it the idle handler on
init
?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.
The thing is the min connections configuration lives/is passed in via the
ConnectionPool
: we need to have the context of the pool to understand how many connections are currently being kept open and decide whether we need to keep the connection from going idle or not.The idle handler is created by the
ConnectionManager
instartConnecting(backoffIterator:muxPromise:connectTimeoutOverride:)
for each connection, thus the manager has to be aware of the idle behaviour to be able to pass it down to the handler.