Skip to content

Commit

Permalink
Adopt Kuery 3 (#85)
Browse files Browse the repository at this point in the history
* Update for SwiftKuery 3
* Remove getConnection API
* Add typealias for database tasks
* Fix @escaping attributes and correct sync semaphore usage
* Add usage docs
  • Loading branch information
kilnerm authored and djones6 committed Jan 3, 2019
1 parent fd9657f commit 60c7c73
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 268 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url:"https://github.com/IBM-Swift/Swift-Kuery.git", from: "2.0.0"),
.package(url:"https://github.com/IBM-Swift/Swift-Kuery.git", from: "3.0.0"),
.package(url:"https://github.com/IBM-Swift/KituraContracts.git", from: "1.0.0"),
.package(url:"https://github.com/IBM-Swift/TypeDecoder.git", from: "1.0.0")
],
Expand Down
68 changes: 58 additions & 10 deletions Sources/SwiftKueryORM/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public typealias ConnectionPoolOptions = SwiftKuery.ConnectionPoolOptions

public class Database {

/// Definition of a DatabaseTask completion handler which accepts an optional Connection and optional Error
public typealias DatabaseTask = (Connection?, QueryError?) -> ()

/// Global default Database for the application
public static var `default`: Database?

Expand All @@ -45,12 +48,29 @@ public class Database {
/// connection generator
private enum ConnectionStrategy {
case pool(ConnectionPool)
case generator(() -> Connection?)
case generator(((@escaping DatabaseTask)) -> ())
}

private let connectionStrategy: ConnectionStrategy

/// Constructor for a single connection which becomes a connection pool
/**
Create a Database instance which uses a single connection to perform each operation. The connection will remain open for the lifetime of the Database.
It is safe for you to issue concurrent operations on the database: the ORM will ensure those operations are executed sequentially.
This connection strategy provides lower latency requests, as a new connection does not need to be established for each operation, and has a small resource overhead. However, scalability is limited as all database operations are serialized.
Below is example code which creates a connection and uses it to create a Database instance:
```swift
var opts = [ConnectionOptions]()
opts.append(ConnectionOptions.userName("myUser"))
opts.append(ConnectionOptions.password("myPassword"))
let connection = PostgreSQLConnection(host: host, port: port, options: opts)
let result = connection.connectSync()
guard let result.success else {
// Handle error
return
}
let db = Database(single: connection)
```
*/
public convenience init(single connection: Connection) {
// Create single entry connection pool for thread safety
let singleConnectionPool = ConnectionPool(options: ConnectionPoolOptions(initialCapacity: 1, maxCapacity: 1),
Expand All @@ -59,22 +79,50 @@ public class Database {
self.init(singleConnectionPool)
}

/// Default constructor for a connection pool
/**
Create a Database instance with multiple connections, managed by a connection pool, allowing operations to be performed concurrently. These connections will remain open for the lifetime of the Database.
This connection strategy provides lower latency requests, as a new connection does not need to be established for each operation. You can choose between better performance or lower resource consumption by adjusting the number of connections in the pool.
Below is an example code which creates a connection pool and uses it to create a Database instance:
```swift
let connectionPool = PostgreSQLConnection.createPool(host: host, port: port, options: [.userName("myUser"), .password("myPassword")], poolOptions: ConnectionPoolOptions(initialCapacity: 5, maxCapacity: 10))
let db = Database(connectionPool)
```
*/
public init(_ pool: ConnectionPool) {
self.connectionStrategy = .pool(pool)
}

/// Constructor for a custom connection generator
public init(generator: @escaping () -> Connection?) {
/**
Create a Database instance which uses short-lived connections that are generated on demand. A new Connection is created for every operation, and will be closed once the operation completes.
This connection strategy allows for minimal resource consumption when idle, and allows multiple operations to be performed concurrently. However, the process of establishing a connection will increase the time taken to process each operation.
Below is an example of a function that can be used as a connection generator and the call to create the Database instance:
```swift
func getConnectionAndRunTask(task: @escaping (Connection?, QueryError?) -> ()) {
var opts = [ConnectionOptions]()
opts.append(ConnectionOptions.userName("myUser"))
opts.append(ConnectionOptions.password("myPassword"))
let connection = PostgreSQLConnection(host: host, port: port, options: opts)
connection.connect() { result in
guard result.success else {
// Handle error
return task(nil, QueryError.connection(result.asError?.localizedDescription ?? "Unknown connection error"))
}
return task(connection, nil)
}
}
let db = Database(generator: getConnectionAndRunTask)```
*/
public init(generator: @escaping (@escaping DatabaseTask) -> ()) {
self.connectionStrategy = .generator(generator)
}

/// Connection getter: either new connection from pool
/// or call the custom generator
public func getConnection() -> Connection? {
/// Function that redirects the passed databaseTask based on the current connectionStrategy
internal func executeTask(task: @escaping DatabaseTask) {
switch connectionStrategy {
case .pool(let pool): return pool.getConnection()
case .generator(let generator): return generator()
case .pool(let pool): return pool.getConnection(poolTask: task)
case .generator(let generator): return generator(task)
}
}
}
Loading

0 comments on commit 60c7c73

Please sign in to comment.