44//
55
66import Foundation
7- import os
87
98@MainActor
109final class MetadataConnectionPool {
@@ -20,91 +19,53 @@ final class MetadataConnectionPool {
2019 var lastUsed : Date
2120 var inFlightCount : Int
2221 var closeWhenIdle : Bool
23- var tail : Task < Void , Never >
2422
2523 init ( driver: DatabaseDriver ) {
2624 self . driver = driver
2725 self . lastUsed = Date ( )
2826 self . inFlightCount = 0
2927 self . closeWhenIdle = false
30- self . tail = Task { }
3128 }
3229 }
3330
3431 private var entries : [ Key : Entry ] = [ : ]
3532 private var pending : [ Key : Task < Void , Error > ] = [ : ]
3633 private let maxPerConnection = 4
3734 private let connectTimeoutSeconds : UInt64 = 15
38- private static let logger = Logger ( subsystem: " com.TablePro " , category: " MetadataConnectionPool " )
3935
4036 private init ( ) { }
4137
4238 func withDriver< T: Sendable > (
4339 connectionId: UUID ,
4440 database: String ,
45- _ body: @Sendable @ escaping ( DatabaseDriver) async throws -> T
41+ _ body: @Sendable ( DatabaseDriver) async throws -> T
4642 ) async throws -> T {
47- Self . logger. debug (
48- " [metadata-pool] withDriver acquire connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) "
49- )
5043 let entry = try await acquireEntry ( connectionId: connectionId, database: database)
5144 entry. inFlightCount += 1
5245 entry. lastUsed = Date ( )
5346 defer { releaseEntry ( entry) }
54-
55- let previous = entry. tail
56- let driver = entry. driver
57- let work = Task { @MainActor ( ) async throws -> T in
58- await previous. value
59- return try await body ( driver)
60- }
61- entry. tail = Task { @MainActor in _ = try ? await work. value }
62- do {
63- let result = try await work. value
64- Self . logger. debug (
65- " [metadata-pool] withDriver done connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) "
66- )
67- return result
68- } catch {
69- Self . logger. debug (
70- " [metadata-pool] withDriver threw connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) error= \( error. localizedDescription, privacy: . public) "
71- )
72- throw error
73- }
74- }
75-
76- private func releaseEntry( _ entry: Entry ) {
77- entry. inFlightCount -= 1
78- guard entry. inFlightCount == 0 , entry. closeWhenIdle else { return }
79- entry. driver. disconnect ( )
80- }
81-
82- func invalidate( connectionId: UUID , database: String ) {
83- let key = Key ( connectionId: connectionId, database: database)
84- pending [ key] ? . cancel ( )
85- pending. removeValue ( forKey: key)
86- closeOrDeferEntry ( forKey: key)
47+ return try await body ( entry. driver)
8748 }
8849
8950 func closeAll( connectionId: UUID ) {
90- for key in pending. keys. filter ( { $0 . connectionId == connectionId } ) {
51+ for key in pending. keys where key . connectionId == connectionId {
9152 pending [ key] ? . cancel ( )
9253 pending. removeValue ( forKey: key)
9354 }
94- let keys = entries. keys. filter { $0. connectionId == connectionId }
95- for key in keys {
55+ for key in entries. keys where key. connectionId == connectionId {
9656 closeOrDeferEntry ( forKey: key)
9757 }
98- if !keys. isEmpty {
99- Self . logger. info (
100- " [metadata-pool] closed all connId= \( connectionId, privacy: . public) count= \( keys. count, privacy: . public) "
101- )
58+ }
59+
60+ private func releaseEntry( _ entry: Entry ) {
61+ entry. inFlightCount -= 1
62+ if entry. inFlightCount == 0 , entry. closeWhenIdle {
63+ entry. driver. disconnect ( )
10264 }
10365 }
10466
10567 private func closeOrDeferEntry( forKey key: Key ) {
106- guard let entry = entries [ key] else { return }
107- entries. removeValue ( forKey: key)
68+ guard let entry = entries. removeValue ( forKey: key) else { return }
10869 if entry. inFlightCount == 0 {
10970 entry. driver. disconnect ( )
11071 } else {
@@ -115,21 +76,16 @@ final class MetadataConnectionPool {
11576 private func acquireEntry( connectionId: UUID , database: String ) async throws -> Entry {
11677 let key = Key ( connectionId: connectionId, database: database)
11778 if let entry = entries [ key] , entry. driver. status == . connected {
118- Self . logger. debug ( " [metadata-pool] reuse connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) " )
11979 return entry
12080 }
12181
12282 if let inFlight = pending [ key] {
123- Self . logger. debug ( " [metadata-pool] await-pending connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) " )
12483 try await inFlight. value
12584 guard let entry = entries [ key] else { throw DatabaseError . notConnected }
12685 return entry
12786 }
12887
12988 guard DatabaseManager . shared. session ( for: connectionId) != nil else {
130- Self . logger. debug (
131- " [metadata-pool] acquire-no-session connId= \( connectionId, privacy: . public) db= \( database, privacy: . public) "
132- )
13389 throw DatabaseError . notConnected
13490 }
13591
@@ -144,13 +100,8 @@ final class MetadataConnectionPool {
144100 entries [ key] = entry
145101 }
146102 pending [ key] = task
147- do {
148- try await task. value
149- } catch {
150- if pending [ key] == task { pending. removeValue ( forKey: key) }
151- throw error
152- }
153- if pending [ key] == task { pending. removeValue ( forKey: key) }
103+ defer { if pending [ key] == task { pending. removeValue ( forKey: key) } }
104+ try await task. value
154105
155106 guard let entry = entries [ key] else { throw DatabaseError . notConnected }
156107 return entry
@@ -160,12 +111,11 @@ final class MetadataConnectionPool {
160111 guard let session = DatabaseManager . shared. session ( for: key. connectionId) else {
161112 throw DatabaseError . notConnected
162113 }
163- let baseConnection = session. effectiveConnection ?? session. connection
164- var cloned = baseConnection
165- cloned. database = key. database
114+ var connection = session. effectiveConnection ?? session. connection
115+ connection. database = key. database
166116
167117 let driver = try await DatabaseDriverFactory . createDriver (
168- for: cloned ,
118+ for: connection ,
169119 passwordOverride: session. cachedPassword,
170120 awaitPlugins: true
171121 )
@@ -175,26 +125,17 @@ final class MetadataConnectionPool {
175125 driver. disconnect ( )
176126 throw error
177127 }
178- Self . logger. info (
179- " [metadata-pool] opened connId= \( key. connectionId, privacy: . public) db= \( key. database, privacy: . public) "
180- )
181128 return Entry ( driver: driver)
182129 }
183130
184131 private func connectWithTimeout( driver: DatabaseDriver , database: String ) async throws {
185132 let timeoutNanos = connectTimeoutSeconds * 1_000_000_000
186133 try await withThrowingTaskGroup ( of: Void . self) { group in
187- group. addTask {
188- try await driver. connect ( )
189- }
134+ group. addTask { try await driver. connect ( ) }
190135 group. addTask {
191136 try await Task . sleep ( nanoseconds: timeoutNanos)
192- throw NSError (
193- domain: " MetadataConnectionPool " ,
194- code: NSURLErrorTimedOut,
195- userInfo: [ NSLocalizedDescriptionKey: String (
196- format: String ( localized: " Connecting to '%@' timed out. " ) , database
197- ) ]
137+ throw DatabaseError . connectionFailed (
138+ String ( format: String ( localized: " Connecting to '%@' timed out. " ) , database)
198139 )
199140 }
200141 try await group. next ( )
@@ -206,12 +147,11 @@ final class MetadataConnectionPool {
206147 let live = entries. filter { $0. key. connectionId == connectionId }
207148 let pendingCount = pending. keys. filter { $0. connectionId == connectionId } . count
208149 guard live. count + pendingCount >= maxPerConnection else { return }
209- let idle = live. filter { $0. value. inFlightCount == 0 }
210- guard let oldest = idle. min ( by: { $0. value. lastUsed < $1. value. lastUsed } ) else { return }
211- entries [ oldest. key] ? . driver. disconnect ( )
212- entries. removeValue ( forKey: oldest. key)
213- Self . logger. info (
214- " [metadata-pool] evicted connId= \( connectionId, privacy: . public) db= \( oldest. key. database, privacy: . public) "
215- )
150+ let oldestIdle = live
151+ . filter { $0. value. inFlightCount == 0 }
152+ . min { $0. value. lastUsed < $1. value. lastUsed }
153+ guard let oldestIdle else { return }
154+ oldestIdle. value. driver. disconnect ( )
155+ entries. removeValue ( forKey: oldestIdle. key)
216156 }
217157}
0 commit comments