Skip to content

Commit

Permalink
Merge pull request #178 from vapor/pivots
Browse files Browse the repository at this point in the history
improved pivots
  • Loading branch information
tanner0101 authored Feb 14, 2017
2 parents 512f000 + 02731e0 commit 14e9855
Show file tree
Hide file tree
Showing 66 changed files with 1,399 additions and 1,031 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ Packages
.build
.DS_Store
*.xcodeproj
Package.pins

8 changes: 3 additions & 5 deletions Sources/Fluent/Database/Connection.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
The lowest level executor. All
calls to higher level executors
eventually end up here.
*/
/// The lowest level executor. All
/// calls to higher level executors
/// eventually end up here.
public protocol Connection: Executor {
/// Indicates whether the connection has
/// closed permanently and should be discarded.
Expand Down
38 changes: 17 additions & 21 deletions Sources/Fluent/Database/Database.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
/**
References a database with a single `Driver`.
Statically maps `Model`s to `Database`s.
*/
public class Database: Executor {
/**
Maps `Model` names to their respective
`Database`. This allows multiple models
in the same application to use different
methods of data persistence.
*/
/// References a database with a single `Driver`.
/// Statically maps `Model`s to `Database`s.
public final class Database: Executor {
/// Maps `Model` names to their respective
/// `Database`. This allows multiple models
/// in the same application to use different
/// methods of data persistence.
public static var map: [String: Database] = [:]

/**
The default database for all `Model` types.
*/
/// The default database for all `Model` types.
public static var `default`: Database?

/// The `Driver` powering this database.
Expand All @@ -26,28 +20,30 @@ public class Database: Executor {

/// Creates a `Database` with the supplied
/// `Driver`. This cannot be changed later.
public init(_ driver: Driver) {
public init(_ driver: Driver, maxConnections: Int = 128) {
threadConnectionPool = ThreadConnectionPool(
makeConnection: driver.makeConnection,
maxConnections: 128 // some number larger than the max threads
maxConnections: maxConnections // some number larger than the max threads
)
self.driver = driver
}

// MARK: Executor
}

// MARK: Executor

/// @see Executor protocol.
extension Database {
/// See Executor protocol.
@discardableResult
public func query<T: Entity>(_ query: Query<T>) throws -> Node {
return try threadConnectionPool.connection().query(query)
}

/// @see Executor protocol.
/// Seeee Executor protocol.
public func schema(_ schema: Schema) throws {
try threadConnectionPool.connection().schema(schema)
}

/// @see Executor protocol.
/// Seeee Executor protocol.
@discardableResult
public func raw(_ raw: String, _ values: [Node]) throws -> Node {
return try threadConnectionPool.connection().raw(raw, values)
Expand Down
71 changes: 30 additions & 41 deletions Sources/Fluent/Database/Driver.swift
Original file line number Diff line number Diff line change
@@ -1,62 +1,51 @@
/**
A `Driver` execute queries
and returns an array of results.
It is responsible for interfacing
with the data store powering Fluent.
*/
/// A `Driver` execute queries
/// and returns an array of results.
/// It is responsible for interfacing
/// with the data store powering Fluent.
public protocol Driver: Executor {
/**
The string value for the
default identifier key.
The `idKey` will be used when
`Model.find(_:)` or other find
by identifier methods are used.
This value is overriden by
entities that implement the
`Entity.idKey` static property.
*/
/// The string value for the
/// default identifier key.
///
/// The `idKey` will be used when
/// `Model.find(_:)` or other find
/// by identifier methods are used.
///
/// This value is overriden by
/// entities that implement the
/// `Entity.idKey` static property.
var idKey: String { get }

/**
Creates a connection for executing
queries. This method is used to
automatically create a connection
if any Executor methods are called on
the Driver.
*/
/// The default type for values stored against the identifier key.
///
/// The `idType` will be accessed by those Entity implementations
/// which do not themselves implement `Entity.idType`.
var idType: IdentifierType { get }

/// Creates a connection for executing
/// queries. This method is used to
/// automatically create a connection
/// if any Executor methods are called on
/// the Driver.
func makeConnection() throws -> Connection
}

// MARK: Executor

extension Driver {
/// See Executor protocol.
@discardableResult
public func query<T: Entity>(_ query: Query<T>) throws -> Node {
return try makeConnection().query(query)
}


/// See Executor protocol.
public func schema(_ schema: Schema) throws {
return try makeConnection().schema(schema)
}


/// See Executor protocol.
@discardableResult
public func raw(_ raw: String, _ values: [Node]) throws -> Node {
return try makeConnection().raw(raw, values)
}
}


// MARK: Deprecated

enum DriverError: Error {
case connectionsNotSupported(String)
}

extension Driver {
public func makeConnection() throws -> Connection {
throw DriverError.connectionsNotSupported("Please update your database driver package to be compatible with Fluent 1.4.")
}
}

52 changes: 22 additions & 30 deletions Sources/Fluent/Database/Executor.swift
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
/**
An Executor is any entity that can execute
the queries for retreiving/sending data and
building databases that Fluent relies on.
Executors may include varying layers of
performance optimizations such as connection
and thread pooling.
The lowest level executor is usually a connection
while the highest level executor can have many
layers of abstraction on top of the connection
for performance and convenience.
*/
/// An Executor is any entity that can execute
/// the queries for retreiving/sending data and
/// building databases that Fluent relies on.
///
/// Executors may include varying layers of
/// performance optimizations such as connection
/// and thread pooling.
///
/// The lowest level executor is usually a connection
/// while the highest level executor can have many
/// layers of abstraction on top of the connection
/// for performance and convenience.
public protocol Executor {
/**
Executes a `Query` from and
returns an array of results fetched,
created, or updated by the action.
*/
/// Executes a `Query` from and
/// returns an array of results fetched,
/// created, or updated by the action.
@discardableResult
func query<T: Entity>(_ query: Query<T>) throws -> Node

/**
Creates the `Schema` indicated
by the `Builder`.
*/
/// Creates the `Schema` indicated
/// by the `Builder`.
func schema(_ schema: Schema) throws

/**
Drivers that support raw querying
accept string queries and parameterized values.
This allows Fluent extensions to be written that
can support custom querying behavior.
*/
/// Drivers that support raw querying
/// accept string queries and parameterized values.
///
/// This allows Fluent extensions to be written that
/// can support custom querying behavior.
@discardableResult
func raw(_ raw: String, _ values: [Node]) throws -> Node
}
Expand Down
91 changes: 35 additions & 56 deletions Sources/Fluent/Database/ThreadConnectionPool.swift
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
import Foundation
import Core

/**
Responsible for maintaing a pool
of connections, one for each thread.
*/
/// Responsible for maintaing a pool
/// of connections, one for each thread.
public final class ThreadConnectionPool {

/**
Thread Pool Errors
*/
/// Thread Pool Errors
public enum Error: Swift.Error {
/**
Something in our internal lock mechanism has unexpectedly failed
... should never see this except for more widespread system
dispatch errors
*/
//// Something in our internal lock mechanism has unexpectedly failed
/// ... should never see this except for more widespread system
/// dispatch errors
case lockFailure

/**
The maximum number of active connections has been reached and the pool
is no longer capable of creating new ones.
*/
/// The maximum number of active connections has been reached and the pool
/// is no longer capable of creating new ones.
case maxConnectionsReached(max: Int)

/**
This is here to allow extensibility w/o breaking apis, it is not currently
used, but should be accounted for by end user if they are handling the
error
*/
/// This is here to allow extensibility w/o breaking apis, it is not currently
/// used, but should be accounted for by end user if they are handling the
/// error
case unspecified(Swift.Error)
}

/**
The constructor used by the factory to create new connections
*/
/// The constructor used by the factory to create new connections
public typealias ConnectionFactory = () throws -> Connection


Expand All @@ -43,29 +31,23 @@ public final class ThreadConnectionPool {
return pthread_self()
}

/**
The maximum amount of connections permitted in the pool
*/
/// The maximum amount of connections permitted in the pool
public var maxConnections: Int

/**
When the maximum amount of connections has been reached and all connections
are in use at time of request, how long should the system wait
until it gives up and throws an error.
default is 10 seconds.
*/
/// When the maximum amount of connections has been reached and all connections
/// are in use at time of request, how long should the system wait
/// until it gives up and throws an error.
///
/// default is 10 seconds.
public var connectionPendingTimeoutSeconds: Int = 10

private var connections: [pthread_t: Connection]
private let connectionsLock: NSLock
private let makeConnection: ConnectionFactory

/**
Initializes a thread pool with a connectionFactory intended to construct
new connections when appropriate and an Integer defining the maximum
number of connections the pool is allowed to make
*/
/// Initializes a thread pool with a connectionFactory intended to construct
/// new connections when appropriate and an Integer defining the maximum
/// number of connections the pool is allowed to make
public init(makeConnection: @escaping ConnectionFactory, maxConnections: Int) {
self.makeConnection = makeConnection
self.maxConnections = maxConnections
Expand All @@ -74,17 +56,15 @@ public final class ThreadConnectionPool {
}

internal func connection() throws -> Connection {
/**
Because we might wait inside of the makeNewConnection function,
do NOT attempt to wrap this within connectionLock or it may
be blocked from other threads
In the makeConnection call, there will be a threadsafe check to prevent
creating duplicates.
It shouldn't happen that two calls come on same thread anyways, but
in the interest of 'just in case'
*/
// Because we might wait inside of the makeNewConnection function,
// do NOT attempt to wrap this within connectionLock or it may
// be blocked from other threads
//
// In the makeConnection call, there will be a threadsafe check to prevent
// creating duplicates.
//
// It shouldn't happen that two calls come on same thread anyways, but
// in the interest of 'just in case'
guard let existing = connections[ThreadConnectionPool.threadId] else { return try makeNewConnection() }
return existing
}
Expand All @@ -95,12 +75,11 @@ public final class ThreadConnectionPool {

var connection: Connection?
try connectionsLock.locked {
/**
Just in case our first attempt to access failed in a non thread safe manner
to prevent duplicates, we do a quick check here.
Likely redundant, but beneficial for safety.
*/
// Just in case our first attempt to access failed in a non thread safe manner
// to prevent duplicates, we do a quick check here.
//
// Likely redundant, but beneficial for safety.
//
if let existing = connections[threadId] {
connection = existing
return
Expand Down
Loading

0 comments on commit 14e9855

Please sign in to comment.