Skip to content

Commit 2a47a1d

Browse files
authored
[HTTPClient.Configuration] Make connection pool size configurable (#437)
1 parent 7ff7ab4 commit 2a47a1d

File tree

4 files changed

+56
-2
lines changed

4 files changed

+56
-2
lines changed

Diff for: Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ final class HTTPConnectionPool {
7171
self._state = StateMachine(
7272
eventLoopGroup: eventLoopGroup,
7373
idGenerator: idGenerator,
74-
maximumConcurrentHTTP1Connections: 8
74+
maximumConcurrentHTTP1Connections: clientConfiguration.connectionPool.concurrentHTTP1ConnectionsPerHostSoftLimit
7575
)
7676
}
7777

Diff for: Sources/AsyncHTTPClient/HTTPClient.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -849,11 +849,21 @@ extension HTTPClient.Configuration {
849849

850850
/// Connection pool configuration.
851851
public struct ConnectionPool: Hashable {
852-
// Specifies amount of time connections are kept idle in the pool.
852+
/// Specifies amount of time connections are kept idle in the pool. After this time has passed without a new
853+
/// request the connections are closed.
853854
public var idleTimeout: TimeAmount
854855

856+
/// The maximum number of connections that are kept alive in the connection pool per host. If requests with
857+
/// an explicit eventLoopRequirement are sent, this number might be exceeded due to overflow connections.
858+
public var concurrentHTTP1ConnectionsPerHostSoftLimit: Int
859+
855860
public init(idleTimeout: TimeAmount = .seconds(60)) {
861+
self.init(idleTimeout: idleTimeout, concurrentHTTP1ConnectionsPerHostSoftLimit: 8)
862+
}
863+
864+
public init(idleTimeout: TimeAmount, concurrentHTTP1ConnectionsPerHostSoftLimit: Int) {
856865
self.idleTimeout = idleTimeout
866+
self.concurrentHTTP1ConnectionsPerHostSoftLimit = concurrentHTTP1ConnectionsPerHostSoftLimit
857867
}
858868
}
859869
}

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ extension HTTPClientTests {
135135
("testCloseWhileBackpressureIsExertedIsFine", testCloseWhileBackpressureIsExertedIsFine),
136136
("testErrorAfterCloseWhileBackpressureExerted", testErrorAfterCloseWhileBackpressureExerted),
137137
("testRequestSpecificTLS", testRequestSpecificTLS),
138+
("testConnectionPoolSizeConfigValueIsRespected", testConnectionPoolSizeConfigValueIsRespected),
138139
]
139140
}
140141
}

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+43
Original file line numberDiff line numberDiff line change
@@ -3060,4 +3060,47 @@ class HTTPClientTests: XCTestCase {
30603060
XCTAssertEqual(firstConnectionNumber, secondConnectionNumber, "Identical TLS configurations did not use the same connection")
30613061
XCTAssertNotEqual(thirdConnectionNumber, firstConnectionNumber, "Different TLS configurations did not use different connections.")
30623062
}
3063+
3064+
func testConnectionPoolSizeConfigValueIsRespected() {
3065+
let numberOfRequestsPerThread = 1000
3066+
let numberOfParallelWorkers = 16
3067+
let poolSize = 12
3068+
3069+
let httpBin = HTTPBin()
3070+
defer { XCTAssertNoThrow(try httpBin.shutdown()) }
3071+
3072+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 4)
3073+
defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) }
3074+
3075+
let configuration = HTTPClient.Configuration(
3076+
connectionPool: .init(
3077+
idleTimeout: .seconds(30),
3078+
concurrentHTTP1ConnectionsPerHostSoftLimit: poolSize
3079+
)
3080+
)
3081+
let client = HTTPClient(eventLoopGroupProvider: .shared(group), configuration: configuration)
3082+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
3083+
3084+
let g = DispatchGroup()
3085+
for workerID in 0..<numberOfParallelWorkers {
3086+
DispatchQueue(label: "\(#file):\(#line):worker-\(workerID)").async(group: g) {
3087+
func makeRequest() {
3088+
let url = "http://127.0.0.1:\(httpBin.port)"
3089+
XCTAssertNoThrow(try client.get(url: url).wait())
3090+
}
3091+
for _ in 0..<numberOfRequestsPerThread {
3092+
makeRequest()
3093+
}
3094+
}
3095+
}
3096+
let timeout = DispatchTime.now() + .seconds(60)
3097+
switch g.wait(timeout: timeout) {
3098+
case .success:
3099+
break
3100+
case .timedOut:
3101+
XCTFail("Timed out")
3102+
}
3103+
3104+
XCTAssertEqual(httpBin.createdConnections, poolSize)
3105+
}
30633106
}

0 commit comments

Comments
 (0)