Skip to content

Commit

Permalink
Merge pull request #8 from Outdooractive/5_pool_errors
Browse files Browse the repository at this point in the history
#5: More tests, added 'closeIdleConnections()', added a description to PoolInfo
  • Loading branch information
trasch authored May 11, 2023
2 parents 20efbf7 + 2be276f commit ccec1c2
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 112 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This package requires Swift 5.7 or higher (at least Xcode 13), and compiles on m

```swift
dependencies: [
.package(url: "https://github.com/Outdooractive/PostgresConnectionPool.git", from: "0.5.5"),
.package(url: "https://github.com/Outdooractive/PostgresConnectionPool.git", from: "0.6.1"),
],
targets: [
.target(name: "MyTarget", dependencies: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Foundation

extension Double {

/// Returns the maximum of `self` and the other value.
func atLeast(_ minValue: Double) -> Double {
Swift.max(minValue, self)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/PostgresConnectionPool/Extensions/EmptyTestable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Foundation
protocol EmptyTestable {

var isEmpty: Bool { get }

/// A Boolean value indicating whether the collection is **not** empty.
var isNotEmpty: Bool { get }

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import Foundation

extension FloatingPoint {

/// Returns rounded FloatingPoint to specified number of places
/// Returns rounded FloatingPoint to specified number of places.
func rounded(toPlaces places: Int) -> Self {
guard places >= 0 else { return self }
var divisor: Self = 1
for _ in 0..<places { divisor *= 10 }
return (self * divisor).rounded() / divisor
}

/// Rounds current FloatingPoint to specified number of places
/// Rounds current FloatingPoint to specified number of places.
mutating func round(toPlaces places: Int) {
self = rounded(toPlaces: places)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Foundation

extension Int {

/// Returns the maximum of `self` and the other value.
func atLeast(_ minValue: Int) -> Int {
Swift.max(minValue, self)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import PostgresNIO

extension PSQLError: CustomStringConvertible {

/// A short error description.
public var description: String {
if let serverInfo = self.serverInfo,
let severity = serverInfo[.severity],
Expand All @@ -26,12 +27,14 @@ extension PSQLError: CustomStringConvertible {

extension PSQLError: CustomDebugStringConvertible {

/// A detailed error description suitable for debugging queries and other problems with the server.
public var debugDescription: String {
var messageElements: [String] = [
"code: \(self.code)"
]

if let serverInfo = self.serverInfo {
// Field -> display name
let fields: OrderedDictionary<PSQLError.ServerInfo.Field, String> = [
.severity: "severity",
.message: "message",
Expand All @@ -48,9 +51,9 @@ extension PSQLError: CustomDebugStringConvertible {
.sqlState: "sqlState",
]

let serverInfoELements = fields.compactMap({ field -> String? in
guard let value = serverInfo[field.0] else { return nil }
return "\(field.1): \(value)"
let serverInfoELements = fields.compactMap({ fieldAndName -> String? in
guard let value = serverInfo[fieldAndName.0] else { return nil }
return "\(fieldAndName.1): \(value)"
})

messageElements.append("serverInfo: [\(serverInfoELements.joined(separator: ", "))]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Foundation

extension String {

/// A Boolean value indicating whether the string matches a regular expression.
func matches(
_ regexp: String,
caseInsensitive: Bool = false)
Expand All @@ -19,6 +20,7 @@ extension String {
return self.range(of: regexp, options: options) != nil
}

/// Returns a new string with the matches of the regular expression replaced with some other string.
func replacingPattern(
_ regexp: String,
with replacement: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Foundation

extension Task where Failure == Error {

/// Perform a task after some time.
@discardableResult
static func after(
seconds: TimeInterval,
Expand Down
63 changes: 49 additions & 14 deletions Sources/PostgresConnectionPool/PoolError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public enum PoolError: Error {
case cancelled
/// The connection to the database was unexpectedly closed.
case connectionFailed
/// The pool was already shut down.
case poolDestroyed
/// The pool was already shut down. Includes the original `PSQLError`
/// if the pool was shutdown due to a permanent server error.
case poolDestroyed(PSQLError?)
/// Some PostgreSQL error.
case postgresError(PSQLError)
/// The query was cancelled by the server.
Expand All @@ -27,14 +28,31 @@ public enum PoolError: Error {

extension PoolError: CustomStringConvertible {

/// A short error description.
public var description: String {
switch self {
case .cancelled: return "<PoolError: cancelled>"
case .connectionFailed: return "<PoolError: connectionFailed>"
case .poolDestroyed: return "<PoolError: poolDestroyed>"
case .postgresError(let psqlError): return "<PoolError: postgresError=\(psqlError.description)>"
case .queryCancelled: return "<PoolError: queryCancelled>"
case .unknown: return "<PoolError: unknown>"
case .cancelled:
return "<PoolError: cancelled>"

case .connectionFailed:
return "<PoolError: connectionFailed>"

case .poolDestroyed(let psqlError):
if let psqlError {
return "<PoolError: poolDestroyed=\(psqlError.description)>"
}
else {
return "<PoolError: poolDestroyed>"
}

case .postgresError(let psqlError):
return "<PoolError: postgresError=\(psqlError.description)>"

case .queryCancelled:
return "<PoolError: queryCancelled>"

case .unknown:
return "<PoolError: unknown>"
}
}

Expand All @@ -44,14 +62,31 @@ extension PoolError: CustomStringConvertible {

extension PoolError: CustomDebugStringConvertible {

/// A detailed error description suitable for debugging queries and other problems with the server.
public var debugDescription: String {
switch self {
case .cancelled: return "<PoolError: cancelled>"
case .connectionFailed: return "<PoolError: connectionFailed>"
case .poolDestroyed: return "<PoolError: poolDestroyed>"
case .postgresError(let psqlError): return "<PoolError: postgresError=\(psqlError.debugDescription)>"
case .queryCancelled(query: let query, runtime: let runtime): return "<PoolError: query '\(query)' cancelled after \(runtime.rounded(toPlaces: 3))s>"
case .unknown: return "<PoolError: unknown>"
case .cancelled:
return "<PoolError: cancelled>"

case .connectionFailed:
return "<PoolError: connectionFailed>"

case .poolDestroyed(let psqlError):
if let psqlError {
return "<PoolError: poolDestroyed=\(psqlError.debugDescription)>"
}
else {
return "<PoolError: poolDestroyed>"
}

case .postgresError(let psqlError):
return "<PoolError: postgresError=\(psqlError.debugDescription)>"

case .queryCancelled(query: let query, runtime: let runtime):
return "<PoolError: query '\(query)' cancelled after \(runtime.rounded(toPlaces: 3))s>"

case .unknown:
return "<PoolError: unknown>"
}
}

Expand Down
53 changes: 53 additions & 0 deletions Sources/PostgresConnectionPool/PoolInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

import Foundation
import PostgresNIO

/// General information about the pool and its open connections.
public struct PoolInfo {
Expand Down Expand Up @@ -37,4 +38,56 @@ public struct PoolInfo {
/// Information about individual open connections to the server.
public let connections: [ConnectionInfo]


/// Whether the pool is accepting connections or was shutdown.
public let isShutdown: Bool
/// The Postgres error If the pool was shutdown forcibly.
public let shutdownError: PSQLError?

}

// MARK: - CustomStringConvertible

extension PoolInfo: CustomStringConvertible {

public var description: String {
var lines: [String] = [
"Pool: \(name)",
"Connections: \(openConnections)/\(activeConnections)/\(availableConnections) (open/active/available)",
"Usage: \(usageCounter)",
"Shutdown? \(isShutdown) \(shutdownError != nil ? "(\(shutdownError!.description))" : "")",
]

if connections.isNotEmpty {
lines.append("Connections:")

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

return lines.joined(separator: "\n")
}

}

extension PoolInfo.ConnectionInfo: CustomStringConvertible {

public var description: String {
var lines: [String] = [
"Connection: \(id) (\(name))",
" State: \(state)",
" Usage: \(usageCounter)",
]

if let query {
lines.append(" Query: \(query)")
if let queryRuntime {
lines.append(" Runtime: \(queryRuntime.rounded(toPlaces: 3))s")
}
}

return lines.joined(separator: "\n")
}

}
Loading

0 comments on commit ccec1c2

Please sign in to comment.