Skip to content

Commit e8c65e5

Browse files
authored
Merge pull request #10 from Outdooractive/9_batch_id
#9: Add a batchId to connections
2 parents ccec1c2 + cbe2c1c commit e8c65e5

File tree

6 files changed

+62
-27
lines changed

6 files changed

+62
-27
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This package requires Swift 5.7 or higher (at least Xcode 13), and compiles on m
1313

1414
```swift
1515
dependencies: [
16-
.package(url: "https://github.com/Outdooractive/PostgresConnectionPool.git", from: "0.6.1"),
16+
.package(url: "https://github.com/Outdooractive/PostgresConnectionPool.git", from: "0.7.0"),
1717
],
1818
targets: [
1919
.target(name: "MyTarget", dependencies: [
@@ -51,9 +51,9 @@ let configuration = PoolConfiguration(
5151
let pool = PostgresConnectionPool(configuration: configuration, logger: logger)
5252

5353
// Fetch a connection from the pool and do something with it...
54-
try await pool.connection(callback: { connection in
54+
try await pool.connection { connection in
5555
try await connection.query(PostgresQuery(stringLiteral: "SELECT 1"), logger: logger)
56-
})
56+
}
5757

5858
// With PostgresKit
5959
func fetchObjects<T: Decodable>(_ sql: SQLQueryString) async throws -> [T] {

Sources/PostgresConnectionPool/PoolConnection.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ final class PoolConnection: Identifiable, Equatable {
99

1010
// TODO: Serialize access
1111
private(set) static var connectionId = 0
12-
private(set) static var globalUsageCounter = 0
1312

1413
private(set) var usageCounter = 0
1514

1615
let id: Int
16+
var batchId: Int?
17+
1718
var connection: PostgresConnection?
1819
var state: PoolConnectionState = .connecting {
1920
didSet {
2021
if case .active = state {
2122
usageCounter += 1
22-
PoolConnection.globalUsageCounter += 1
2323
}
2424
}
2525
}

Sources/PostgresConnectionPool/PoolContinuation.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ typealias PostgresCheckedContinuation = CheckedContinuation<PoolConnection, Erro
1010
final class PoolContinuation {
1111

1212
let added: Date
13+
let batchId: Int?
1314
let continuation: PostgresCheckedContinuation
1415

15-
init(continuation: PostgresCheckedContinuation) {
16+
init(batchId: Int?, continuation: PostgresCheckedContinuation) {
1617
self.added = Date()
18+
self.batchId = batchId
1719
self.continuation = continuation
1820
}
1921

Sources/PostgresConnectionPool/PoolInfo.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public struct PoolInfo {
1616
public let name: String
1717
/// Total number of queries that were sent over this connection.
1818
public let usageCounter: Int
19+
/// The connection's batch Id.
20+
public var batchId: Int?
1921
/// The current query, if available.
2022
public let query: String?
2123
/// The current time for the query, if available.
@@ -32,8 +34,6 @@ public struct PoolInfo {
3234
public let activeConnections: Int
3335
/// The number of connections that are currently available.
3436
public let availableConnections: Int
35-
/// The total number of queries that were sent to the server.
36-
public let usageCounter: Int
3737

3838
/// Information about individual open connections to the server.
3939
public let connections: [ConnectionInfo]
@@ -53,16 +53,15 @@ extension PoolInfo: CustomStringConvertible {
5353
public var description: String {
5454
var lines: [String] = [
5555
"Pool: \(name)",
56-
"Connections: \(openConnections)/\(activeConnections)/\(availableConnections) (open/active/available)",
57-
"Usage: \(usageCounter)",
58-
"Shutdown? \(isShutdown) \(shutdownError != nil ? "(\(shutdownError!.description))" : "")",
56+
" Connections: \(openConnections)/\(activeConnections)/\(availableConnections) (open/active/available)",
57+
" Is shut down? \(isShutdown) \(shutdownError != nil ? "(\(shutdownError!.description))" : "")",
5958
]
6059

6160
if connections.isNotEmpty {
62-
lines.append("Connections:")
61+
lines.append(" Connections:")
6362

6463
for connection in connections.sorted(by: { $0.id < $1.id }) {
65-
lines.append(contentsOf: connection.description.components(separatedBy: "\n").map({ " " + $0 }))
64+
lines.append(contentsOf: connection.description.components(separatedBy: "\n").map({ " " + $0 }))
6665
}
6766
}
6867

@@ -87,6 +86,10 @@ extension PoolInfo.ConnectionInfo: CustomStringConvertible {
8786
}
8887
}
8988

89+
if let batchId {
90+
lines.append(" BatchId: \(batchId)")
91+
}
92+
9093
return lines.joined(separator: "\n")
9194
}
9295

Sources/PostgresConnectionPool/PostgresConnectionPool.swift

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,34 @@ public actor PostgresConnectionPool {
8585
// }
8686

8787
/// Takes one connection from the pool and dishes it out to the caller.
88+
///
89+
/// - Parameters:
90+
/// - batchId: An optional integer value associated with the connection. See also ``abortBatch(_:)``.
91+
/// - callback: A closure with a connection to the database server.
8892
@discardableResult
8993
public func connection<T>(
94+
batchId: Int? = nil,
9095
_ callback: (PostgresConnectionWrapper) async throws -> T)
9196
async throws -> T
9297
{
9398
var poolConnection: PoolConnection?
9499

95100
do {
96-
poolConnection = try await getConnection()
101+
poolConnection = try await getConnection(batchId: batchId)
97102

98103
if Task.isCancelled {
99104
await releaseConnection(poolConnection!)
105+
106+
if let batchId {
107+
await abortBatch(batchId)
108+
}
109+
100110
throw PoolError.cancelled
101111
}
102112

103-
let result = try await PostgresConnectionWrapper.distribute(poolConnection: poolConnection, callback: callback)
113+
let result = try await PostgresConnectionWrapper.distribute(
114+
poolConnection: poolConnection,
115+
callback: callback)
104116

105117
await releaseConnection(poolConnection!)
106118

@@ -118,11 +130,11 @@ public actor PostgresConnectionPool {
118130
}
119131

120132
/// Adds a connection placeholder to the list of waiting connections.
121-
private func getConnection() async throws -> PoolConnection {
133+
private func getConnection(batchId: Int? = nil) async throws -> PoolConnection {
122134
guard !isShutdown else { throw PoolError.poolDestroyed(shutdownError) }
123135

124136
return try await withCheckedThrowingContinuation({ (continuation: PostgresCheckedContinuation) in
125-
self.continuations.append(PoolContinuation(continuation: continuation))
137+
self.continuations.append(PoolContinuation(batchId: batchId, continuation: continuation))
126138

127139
Task.detached { [weak self] in
128140
await self?.handleNextContinuation()
@@ -154,11 +166,31 @@ public actor PostgresConnectionPool {
154166
assert(available.contains(connection), "Connections in state 'available' should be available")
155167
}
156168

169+
connection.query = nil
170+
connection.batchId = nil
171+
157172
Task.detached { [weak self] in
158173
await self?.handleNextContinuation()
159174
}
160175
}
161176

177+
/// Aborts all waiting queries with the given `batchId`.
178+
public func abortBatch(_ batchId: Int) async {
179+
let countBefore = continuations.count
180+
181+
continuations.removeAll(where: { poolContinuation in
182+
guard poolContinuation.batchId == batchId else { return false }
183+
184+
poolContinuation.continuation.resume(throwing: PoolError.cancelled)
185+
return true
186+
})
187+
188+
let countRemoved = countBefore - continuations.count
189+
if countRemoved > 0 {
190+
logger.debug("[\(poolName)] Removed \(countRemoved) continuations for batch \(batchId)")
191+
}
192+
}
193+
162194
/// Releases all resources in the pool and shuts down the event loop.
163195
/// All further uses of the pool will throw an error.
164196
///
@@ -220,12 +252,17 @@ public actor PostgresConnectionPool {
220252
}
221253

222254
/// Information about the pool and its open connections.
223-
public func poolInfo() async -> PoolInfo {
255+
public func poolInfo(batchId: Int? = nil) async -> PoolInfo {
224256
let connections = connections.compactMap { connection -> PoolInfo.ConnectionInfo? in
225-
PoolInfo.ConnectionInfo(
257+
if let batchId, connection.batchId != batchId {
258+
return nil
259+
}
260+
261+
return PoolInfo.ConnectionInfo(
226262
id: connection.id,
227263
name: nameForConnection(id: connection.id),
228264
usageCounter: connection.usageCounter,
265+
batchId: connection.batchId,
229266
query: connection.query,
230267
queryRuntime: connection.queryRuntime,
231268
state: connection.state)
@@ -236,7 +273,6 @@ public actor PostgresConnectionPool {
236273
openConnections: connections.count,
237274
activeConnections: connections.count - available.count,
238275
availableConnections: available.count,
239-
usageCounter: PoolConnection.globalUsageCounter,
240276
connections: connections,
241277
isShutdown: isShutdown,
242278
shutdownError: shutdownError)
@@ -388,6 +424,7 @@ public actor PostgresConnectionPool {
388424
}
389425

390426
poolConnection.state = .active(Date())
427+
poolConnection.batchId = poolContinuation.batchId
391428

392429
do {
393430
// Connection check, etc.

Tests/PostgresConnectionPoolTests/ConnectionTests.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,13 @@ final class ConnectionTests: XCTestCase {
3535
}
3636

3737
func testPoolInfo() async throws {
38-
let initialUsageCounter = PoolConnection.globalUsageCounter
3938
let pool = PostgresConnectionPool(configuration: PostgresHelpers.poolConfiguration(), logger: logger)
4039

4140
let poolInfoBefore = await pool.poolInfo()
4241
print(poolInfoBefore)
4342
XCTAssertEqual(poolInfoBefore.activeConnections, 0)
4443
XCTAssertEqual(poolInfoBefore.availableConnections, 0)
4544
XCTAssertEqual(poolInfoBefore.openConnections, 0)
46-
XCTAssertEqual(poolInfoBefore.usageCounter, initialUsageCounter)
4745
XCTAssertEqual(poolInfoBefore.connections.count, poolInfoBefore.openConnections)
4846
XCTAssertFalse(poolInfoBefore.isShutdown)
4947
XCTAssertNil(poolInfoBefore.shutdownError)
@@ -66,7 +64,6 @@ final class ConnectionTests: XCTestCase {
6664
XCTAssertEqual(poolInfo.activeConnections, 0)
6765
XCTAssertGreaterThan(poolInfo.availableConnections, 0)
6866
XCTAssertGreaterThan(poolInfo.openConnections, 0)
69-
XCTAssertEqual(poolInfo.usageCounter, 1000 + initialUsageCounter)
7067
XCTAssertEqual(poolInfo.connections.count, poolInfo.openConnections)
7168
XCTAssertFalse(poolInfo.isShutdown)
7269
XCTAssertNil(poolInfo.shutdownError)
@@ -78,14 +75,12 @@ final class ConnectionTests: XCTestCase {
7875
XCTAssertEqual(poolInfoAfterShutdown.activeConnections, 0)
7976
XCTAssertEqual(poolInfoAfterShutdown.availableConnections, 0)
8077
XCTAssertEqual(poolInfoAfterShutdown.openConnections, 0)
81-
XCTAssertGreaterThan(poolInfoAfterShutdown.usageCounter, 0)
8278
XCTAssertEqual(poolInfoAfterShutdown.connections.count, 0)
8379
XCTAssertTrue(poolInfoAfterShutdown.isShutdown)
8480
XCTAssertNil(poolInfoAfterShutdown.shutdownError)
8581
}
8682

8783
func testPoolSize100() async throws {
88-
let initialUsageCounter = PoolConnection.globalUsageCounter
8984
let pool = PostgresConnectionPool(configuration: PostgresHelpers.poolConfiguration(poolSize: 100), logger: logger)
9085

9186
let start = 1
@@ -105,7 +100,6 @@ final class ConnectionTests: XCTestCase {
105100
XCTAssertEqual(poolInfo.activeConnections, 0)
106101
XCTAssertGreaterThan(poolInfo.availableConnections, 0)
107102
XCTAssertGreaterThan(poolInfo.openConnections, 0)
108-
XCTAssertEqual(poolInfo.usageCounter, 10000 + initialUsageCounter)
109103
XCTAssertEqual(poolInfo.connections.count, poolInfo.openConnections)
110104
XCTAssertFalse(poolInfo.isShutdown)
111105
XCTAssertNil(poolInfo.shutdownError)
@@ -116,7 +110,6 @@ final class ConnectionTests: XCTestCase {
116110
XCTAssertEqual(poolInfoIdleClosed.activeConnections, 0)
117111
XCTAssertEqual(poolInfoIdleClosed.availableConnections, 0)
118112
XCTAssertEqual(poolInfoIdleClosed.openConnections, 0)
119-
XCTAssertEqual(poolInfoIdleClosed.usageCounter, 10000 + initialUsageCounter)
120113
XCTAssertEqual(poolInfoIdleClosed.connections.count, poolInfoIdleClosed.openConnections)
121114
XCTAssertFalse(poolInfoIdleClosed.isShutdown)
122115
XCTAssertNil(poolInfoIdleClosed.shutdownError)

0 commit comments

Comments
 (0)