From e81d83120052772f5840eab9dde698c16b01e3d9 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Fri, 8 May 2020 17:03:57 -0500 Subject: [PATCH 01/29] Pass ID of database to migrate into Migrator --- Sources/FluentKit/Migration/Migrations.swift | 3 + Sources/FluentKit/Migration/Migrator.swift | 70 ++++++++++---------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrations.swift b/Sources/FluentKit/Migration/Migrations.swift index ad289966..731c1928 100644 --- a/Sources/FluentKit/Migration/Migrations.swift +++ b/Sources/FluentKit/Migration/Migrations.swift @@ -5,12 +5,15 @@ public final class Migrations { } var storage: [Item] + var databases: [DatabaseID] public init() { self.storage = [] + self.databases = [] } public func add(_ migration: Migration, to id: DatabaseID? = nil) { self.storage.append(.init(id: id, migration: migration)) + if let id = id { self.databases.append(id) } } } diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index a39461b7..2c994eac 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -33,15 +33,15 @@ public struct Migrator { // MARK: Setup - public func setupIfNeeded() -> EventLoopFuture { - MigrationLog.migration.prepare(on: self.database(nil)) + public func setupIfNeeded(on databaseID: DatabaseID? = nil) -> EventLoopFuture { + MigrationLog.migration.prepare(on: self.database(databaseID)) } // MARK: Prepare - public func prepareBatch() -> EventLoopFuture { - self.unpreparedMigrations().flatMap { migrations in - self.lastBatchNumber() + public func prepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { + self.unpreparedMigrations(on: databaseID).flatMap { migrations in + self.lastBatchNumber(on: databaseID) .and(value: migrations) }.flatMap { (lastBatch, migrations) in .andAllSync(migrations.map { item in @@ -52,43 +52,43 @@ public struct Migrator { // MARK: Revert - public func revertLastBatch() -> EventLoopFuture { - self.lastBatchNumber().flatMap { - self.revertBatch(number: $0) + public func revertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { + self.lastBatchNumber(on: databaseID).flatMap { + self.revertBatch(number: $0, on: databaseID) } } - public func revertBatch(number: Int) -> EventLoopFuture { - self.preparedMigrations(batch: number).flatMap { migrations in + public func revertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture { + self.preparedMigrations(batch: number, on: databaseID).flatMap { migrations in EventLoopFuture.andAllSync(migrations.map { item in { self.revert(item) } }, on: self.eventLoop) } } - public func revertAllBatches() -> EventLoopFuture { - self.preparedMigrations().flatMap { migrations in + public func revertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture { + self.preparedMigrations(on: databaseID).flatMap { migrations in .andAllSync(migrations.map { item in { self.revert(item) } }, on: self.eventLoop) }.flatMap { _ in - self.revertMigrationLog() + self.revertMigrationLog(on: databaseID) } } // MARK: Preview - public func previewPrepareBatch() -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.unpreparedMigrations().map { items in + public func previewPrepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { + self.unpreparedMigrations(on: databaseID).map { items in items.map { item in (item.migration, item.id) } } } - public func previewRevertLastBatch() -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.lastBatchNumber().flatMap { lastBatch in - self.preparedMigrations(batch: lastBatch) + public func previewRevertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { + self.lastBatchNumber(on: databaseID).flatMap { lastBatch in + self.preparedMigrations(batch: lastBatch, on: databaseID) }.map { items in items.map { item in (item.migration, item.id) @@ -96,16 +96,16 @@ public struct Migrator { } } - public func previewRevertBatch(number: Int) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.preparedMigrations(batch: number).map { items -> [(Migration, DatabaseID?)] in + public func previewRevertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { + self.preparedMigrations(batch: number, on: databaseID).map { items -> [(Migration, DatabaseID?)] in items.map { item -> (Migration, DatabaseID?) in return (item.migration, item.id) } } } - public func previewRevertAllBatches() -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.preparedMigrations().map { items -> [(Migration, DatabaseID?)] in + public func previewRevertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { + self.preparedMigrations(on: databaseID).map { items -> [(Migration, DatabaseID?)] in items.map { item -> (Migration, DatabaseID?) in return (item.migration, item.id) } @@ -129,43 +129,43 @@ public struct Migrator { } } - private func revertMigrationLog() -> EventLoopFuture { - MigrationLog.migration.revert(on: self.database(nil)) + private func revertMigrationLog(on databaseID: DatabaseID?) -> EventLoopFuture { + MigrationLog.migration.revert(on: self.database(databaseID)) } - private func lastBatchNumber() -> EventLoopFuture { - MigrationLog.query(on: self.database(nil)).sort(\.$batch, .descending).first().map { log in + private func lastBatchNumber(on databaseID: DatabaseID?) -> EventLoopFuture { + MigrationLog.query(on: self.database(databaseID)).sort(\.$batch, .descending).first().map { log in log?.batch ?? 0 } } - private func preparedMigrations() -> EventLoopFuture<[Migrations.Item]> { - MigrationLog.query(on: self.database(nil)).all().map { logs -> [Migrations.Item] in + private func preparedMigrations(on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { + MigrationLog.query(on: self.database(databaseID)).all().map { logs -> [Migrations.Item] in self.migrations.storage.filter { storage in logs.contains { log in storage.migration.name == log.name - } + } && storage.id == databaseID }.reversed() } } - private func preparedMigrations(batch: Int) -> EventLoopFuture<[Migrations.Item]> { - MigrationLog.query(on: self.database(nil)).filter(\.$batch == batch).all().map { logs in + private func preparedMigrations(batch: Int, on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { + MigrationLog.query(on: self.database(databaseID)).filter(\.$batch == batch).all().map { logs in self.migrations.storage.filter { storage in logs.contains { log in storage.migration.name == log.name - } + } && storage.id == databaseID }.reversed() } } - private func unpreparedMigrations() -> EventLoopFuture<[Migrations.Item]> { - return MigrationLog.query(on: self.database(nil)) + private func unpreparedMigrations(on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { + return MigrationLog.query(on: self.database(databaseID)) .all() .map { logs -> [Migrations.Item] in return self.migrations.storage.compactMap { item in - if logs.filter({ $0.name == item.migration.name }).count == 0 { + if item.id == databaseID && logs.filter({ $0.name == item.migration.name }).count == 0 { return item } else { // log found, this has been prepared From 6c0a5799541d0b392f7e329fcd61e9a9d0153569 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 11:16:31 -0500 Subject: [PATCH 02/29] Migrate each registered DatabaseID unless a specific database is specified --- Sources/FluentKit/Migration/Migrator.swift | 143 +++++++++++++++------ 1 file changed, 106 insertions(+), 37 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 2c994eac..53da1fe5 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -33,82 +33,122 @@ public struct Migrator { // MARK: Setup - public func setupIfNeeded(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - MigrationLog.migration.prepare(on: self.database(databaseID)) + public func setupIfNeeded(on database: DatabaseID? = nil) -> EventLoopFuture { + self.run(on: database, MigrationLog.migration.prepare(on:)) } // MARK: Prepare public func prepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.unpreparedMigrations(on: databaseID).flatMap { migrations in - self.lastBatchNumber(on: databaseID) - .and(value: migrations) - }.flatMap { (lastBatch, migrations) in - .andAllSync(migrations.map { item in - { self.prepare(item, batch: lastBatch + 1) } - }, on: self.eventLoop) + self.run(on: databaseID) { database in + self.unpreparedMigrations(on: database).flatMap { migrations in + self.lastBatchNumber(on: database) + .and(value: migrations) + }.flatMap { (lastBatch, migrations) in + .andAllSync(migrations.map { item in + { self.prepare(item, batch: lastBatch + 1) } + }, on: self.eventLoop) + } } } // MARK: Revert public func revertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.lastBatchNumber(on: databaseID).flatMap { - self.revertBatch(number: $0, on: databaseID) + self.run(on: databaseID) { database in + self.lastBatchNumber(on: database).flatMap { + self.revertBatch(number: $0, on: database) + } } } public func revertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.preparedMigrations(batch: number, on: databaseID).flatMap { migrations in - EventLoopFuture.andAllSync(migrations.map { item in - { self.revert(item) } - }, on: self.eventLoop) + self.run(on: databaseID) { database in + self.preparedMigrations(batch: number, on: database).flatMap { migrations in + EventLoopFuture.andAllSync(migrations.map { item in + { self.revert(item) } + }, on: self.eventLoop) + } } } public func revertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.preparedMigrations(on: databaseID).flatMap { migrations in - .andAllSync(migrations.map { item in - { self.revert(item) } - }, on: self.eventLoop) - }.flatMap { _ in - self.revertMigrationLog(on: databaseID) + self.run(on: databaseID) { database in + self.preparedMigrations(on: database).flatMap { migrations in + .andAllSync(migrations.map { item in + { self.revert(item) } + }, on: self.eventLoop) + }.flatMap { _ in + self.revertMigrationLog(on: database) + } } } // MARK: Preview public func previewPrepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.unpreparedMigrations(on: databaseID).map { items in - items.map { item in - (item.migration, item.id) + var batch: [(Migration, DatabaseID?)] = [] + var failed: Error? = nil + + return self.run(on: databaseID) { database in + self.unpreparedMigrations(on: database).map { items in + batch.append(contentsOf: items.map { ($0.migration, $0.id) }) + }.flatMapErrorThrowing { error in + failed = error } + }.flatMapThrowing { + if let error = failed { throw error } + return batch } } public func previewRevertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.lastBatchNumber(on: databaseID).flatMap { lastBatch in - self.preparedMigrations(batch: lastBatch, on: databaseID) - }.map { items in - items.map { item in - (item.migration, item.id) + var batch: [(Migration, DatabaseID?)] = [] + var failed: Error? = nil + + return self.run(on: databaseID) { database in + self.lastBatchNumber(on: database).flatMap { lastBatch in + self.preparedMigrations(batch: lastBatch, on: database) + }.map { items in + batch.append(contentsOf: items.map { ($0.migration, $0.id) }) + }.flatMapErrorThrowing { error in + failed = error } + }.flatMapThrowing { + if let error = failed { throw error } + return batch } } public func previewRevertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.preparedMigrations(batch: number, on: databaseID).map { items -> [(Migration, DatabaseID?)] in - items.map { item -> (Migration, DatabaseID?) in - return (item.migration, item.id) + var batch: [(Migration, DatabaseID?)] = [] + var failed: Error? = nil + + return self.run(on: databaseID) { database in + self.preparedMigrations(on: database).map { items in + batch.append(contentsOf: items.map { ($0.migration, $0.id) }) + }.flatMapErrorThrowing { error in + failed = error } + }.flatMapThrowing { + if let error = failed { throw error } + return batch } } public func previewRevertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - self.preparedMigrations(on: databaseID).map { items -> [(Migration, DatabaseID?)] in - items.map { item -> (Migration, DatabaseID?) in - return (item.migration, item.id) + var batch: [(Migration, DatabaseID?)] = [] + var failed: Error? = nil + + return self.run(on: databaseID) { database in + self.preparedMigrations(on: database).map { items in + batch.append(contentsOf: items.map { ($0.migration, $0.id) }) + }.flatMapErrorThrowing { error in + failed = error } + }.flatMapThrowing { + if let error = failed { throw error } + return batch } } @@ -174,10 +214,39 @@ public struct Migrator { } } } - + + private func database(_ id: DatabaseID?) -> Database { self.databaseFactory(id) } + + private func run(on database: DatabaseID? = nil, _ query: @escaping (Database) -> EventLoopFuture) -> EventLoopFuture { + if let id = database { + return query(self.database(id)) + } + + let queries = self.migrations.databases.map { id -> () -> EventLoopFuture in + return { query(self.database(id)) } + } + + return EventLoopFuture.andAllSync(queries, on: self.eventLoop) + } + + private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID) -> EventLoopFuture) -> EventLoopFuture { + if let id = database { + return query(id) + } + + let queries = self.migrations.databases.map { id -> () -> EventLoopFuture in + return { query(id) } + } + + return EventLoopFuture.andAllSync(queries, on: self.eventLoop) + } +} + +enum MigrationError: Error { + case databaseNoFound(DatabaseID) } extension EventLoopFuture { @@ -186,7 +255,7 @@ extension EventLoopFuture { on eventLoop: EventLoop ) -> EventLoopFuture { let promise = eventLoop.makePromise(of: Void.self) - + var iterator = futures.makeIterator() func handle(_ future: () -> EventLoopFuture) { future().whenComplete { res in From 0a2fd3d2b4f9ef761647a540e01e1427d6cb1184 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 11:19:56 -0500 Subject: [PATCH 03/29] Removed Migrator.run(on:_:) method that passes Database instance into 'query' closure --- Sources/FluentKit/Migration/Migrator.swift | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 53da1fe5..15a1c076 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -33,8 +33,8 @@ public struct Migrator { // MARK: Setup - public func setupIfNeeded(on database: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: database, MigrationLog.migration.prepare(on:)) + public func setupIfNeeded(on databaseID: DatabaseID? = nil) -> EventLoopFuture { + self.run(on: databaseID) { MigrationLog.migration.prepare(on: self.database($0)) } } // MARK: Prepare @@ -220,18 +220,6 @@ public struct Migrator { self.databaseFactory(id) } - private func run(on database: DatabaseID? = nil, _ query: @escaping (Database) -> EventLoopFuture) -> EventLoopFuture { - if let id = database { - return query(self.database(id)) - } - - let queries = self.migrations.databases.map { id -> () -> EventLoopFuture in - return { query(self.database(id)) } - } - - return EventLoopFuture.andAllSync(queries, on: self.eventLoop) - } - private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID) -> EventLoopFuture) -> EventLoopFuture { if let id = database { return query(id) From 5e155d019ea0835a640ba83eaa5e2dae2f7e9442 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 11:21:09 -0500 Subject: [PATCH 04/29] Removed MigrationError enum --- Sources/FluentKit/Migration/Migrator.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 15a1c076..f9568e6c 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -233,17 +233,13 @@ public struct Migrator { } } -enum MigrationError: Error { - case databaseNoFound(DatabaseID) -} - extension EventLoopFuture { public static func andAllSync( _ futures: [() -> EventLoopFuture], on eventLoop: EventLoop ) -> EventLoopFuture { let promise = eventLoop.makePromise(of: Void.self) - + var iterator = futures.makeIterator() func handle(_ future: () -> EventLoopFuture) { future().whenComplete { res in From 8f83338d388fe94acdda865d4d995d8ff2b0b14d Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 11:23:25 -0500 Subject: [PATCH 05/29] Skip subsequent queries if a previous error has occured in Migrator preview methods --- Sources/FluentKit/Migration/Migrator.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index f9568e6c..b764c241 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -91,7 +91,9 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - self.unpreparedMigrations(on: database).map { items in + if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + + return self.unpreparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) }.flatMapErrorThrowing { error in failed = error @@ -107,7 +109,9 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - self.lastBatchNumber(on: database).flatMap { lastBatch in + if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + + return self.lastBatchNumber(on: database).flatMap { lastBatch in self.preparedMigrations(batch: lastBatch, on: database) }.map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) @@ -125,7 +129,9 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - self.preparedMigrations(on: database).map { items in + if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + + return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) }.flatMapErrorThrowing { error in failed = error @@ -141,7 +147,9 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - self.preparedMigrations(on: database).map { items in + if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + + return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) }.flatMapErrorThrowing { error in failed = error @@ -239,7 +247,7 @@ extension EventLoopFuture { on eventLoop: EventLoop ) -> EventLoopFuture { let promise = eventLoop.makePromise(of: Void.self) - + var iterator = futures.makeIterator() func handle(_ future: () -> EventLoopFuture) { future().whenComplete { res in From dc881442829dfc48f5e1016949b003648490eb00 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:03:31 -0500 Subject: [PATCH 06/29] Pass in default database ID (nil) to Migrator.run 'query' closure argument --- Sources/FluentKit/Migration/Migrator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index b764c241..31fe12ce 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -228,7 +228,7 @@ public struct Migrator { self.databaseFactory(id) } - private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID) -> EventLoopFuture) -> EventLoopFuture { + private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID?) -> EventLoopFuture) -> EventLoopFuture { if let id = database { return query(id) } @@ -237,7 +237,7 @@ public struct Migrator { return { query(id) } } - return EventLoopFuture.andAllSync(queries, on: self.eventLoop) + return EventLoopFuture.andAllSync([{ query(nil) }] + queries, on: self.eventLoop) } } From 1aaf1fd196f01c664d49921bf99585a0e3f759e7 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:06:47 -0500 Subject: [PATCH 07/29] Changed Migrations.databases type from an Array to a Set --- Sources/FluentKit/Migration/Migrations.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrations.swift b/Sources/FluentKit/Migration/Migrations.swift index 731c1928..b6af20f0 100644 --- a/Sources/FluentKit/Migration/Migrations.swift +++ b/Sources/FluentKit/Migration/Migrations.swift @@ -5,7 +5,7 @@ public final class Migrations { } var storage: [Item] - var databases: [DatabaseID] + var databases: Set public init() { self.storage = [] @@ -14,6 +14,6 @@ public final class Migrations { public func add(_ migration: Migration, to id: DatabaseID? = nil) { self.storage.append(.init(id: id, migration: migration)) - if let id = id { self.databases.append(id) } + if let id = id { self.databases.insert(id) } } } From 466cff67f57b73f82bc433e6e533d64dda054f99 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:26:35 -0500 Subject: [PATCH 08/29] Use .whenAllSucceed instead of .andAllSync to complete Migrator.run 'query' closure results --- Sources/FluentKit/Migration/Migrator.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 31fe12ce..7914bcf5 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -233,11 +233,9 @@ public struct Migrator { return query(id) } - let queries = self.migrations.databases.map { id -> () -> EventLoopFuture in - return { query(id) } - } - return EventLoopFuture.andAllSync([{ query(nil) }] + queries, on: self.eventLoop) + let queries = self.migrations.databases.map(query) + return EventLoopFuture.whenAllSucceed([query(nil)] + queries, on: self.eventLoop).map { _ in () } } } From 83a4fae5af9faa561a55d4ed3da85ead94b9e318 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:54:01 -0500 Subject: [PATCH 09/29] Defined MigratorTests test cases --- Tests/FluentKitTests/MigratorTests.swift | 145 +++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Tests/FluentKitTests/MigratorTests.swift diff --git a/Tests/FluentKitTests/MigratorTests.swift b/Tests/FluentKitTests/MigratorTests.swift new file mode 100644 index 00000000..bd7cbe39 --- /dev/null +++ b/Tests/FluentKitTests/MigratorTests.swift @@ -0,0 +1,145 @@ +@testable import FluentKit +import XCTest +import NIO + +final class MigratorTests: XCTestCase { + func testSingleMigration() throws { + let migrations = Migrations() + migrations.add(TestMigration(), to: .migrate1) + + let migrator = self.migrator(using: migrations) + + try migrator.setupIfNeeded().wait() + try migrator.prepareBatch().wait() + + let logs = try MigrationLog.query(on: self.database(.migrate1)).all().wait() + XCTAssertEqual(logs.count, 1) + let log = try XCTUnwrap(logs.first) + + XCTAssertEqual(log.batch, 1) + XCTAssertEqual(log.name, TestMigration().name) + + try XCTAssertEqual(MigrationLog.query(on: self.database(.migrate2)).all().wait().count, 0) + } + + func testMultipleMigrations() throws { + let migrations = Migrations() + migrations.add(TestMigration(), to: .migrate1) + migrations.add(TestMigration(), to: .migrate2) + + let migrator = self.migrator(using: migrations) + + try migrator.setupIfNeeded().wait() + try migrator.prepareBatch().wait() + + let logs1 = try MigrationLog.query(on: self.database(.migrate1)).all().wait() + XCTAssertEqual(logs1.count, 1) + + let log1 = try XCTUnwrap(logs1.first) + XCTAssertEqual(log1.batch, 1) + XCTAssertEqual(log1.name, TestMigration().name) + + + let logs2 = try MigrationLog.query(on: self.database(.migrate2)).all().wait() + XCTAssertEqual(logs2.count, 1) + + let log2 = try XCTUnwrap(logs1.first) + XCTAssertEqual(log2.batch, 1) + XCTAssertEqual(log2.name, TestMigration().name) + } + + func testSubsequentMigration() throws { + let migrations = Migrations() + + + migrations.add(TestMigration(), to: .migrate1) + let migrator1 = self.migrator(using: migrations) + + try migrator1.setupIfNeeded().wait() + try migrator1.prepareBatch().wait() + + let logs1 = try MigrationLog.query(on: self.database(.migrate1)).all().wait() + XCTAssertEqual(logs1.count, 1) + + let log1 = try XCTUnwrap(logs1.first) + XCTAssertEqual(log1.batch, 1) + XCTAssertEqual(log1.name, TestMigration().name) + + + migrations.add(TestMigration(), to: .migrate2) + let migrator2 = self.migrator(using: migrations) + + try migrator2.setupIfNeeded().wait() + try migrator2.prepareBatch().wait() + + let logs2 = try MigrationLog.query(on: self.database(.migrate2)).all().wait() + XCTAssertEqual(logs2.count, 1) + + let log2 = try XCTUnwrap(logs2.first) + XCTAssertEqual(log2.batch, 1) + XCTAssertEqual(log2.name, TestMigration().name) + } + + + var eventLoopGroup: EventLoopGroup! + var threadPool: NIOThreadPool! + var databases: Databases! + + + func migrator(using migrations: Migrations) -> Migrator { + return Migrator( + databases: self.databases, + migrations: migrations, + logger: Logger(label: "codes.tests.vapor.migrations"), + on: self.eventLoopGroup.next() + ) + } + + func database(_ id: DatabaseID) -> Database { + return self.databases.database(id, logger: Logger(label: "com.databases.\(id.string)"), on: self.eventLoopGroup.next())! + } + + override func setUpWithError() throws { + try super.setUpWithError() + + self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) + self.threadPool = NIOThreadPool(numberOfThreads: 1) + self.databases = Databases(threadPool: self.threadPool, on: self.eventLoopGroup) + } + + override func tearDownWithError() throws { + self.databases.shutdown() + self.databases = nil + + try self.eventLoopGroup.syncShutdownGracefully() + self.eventLoopGroup = nil + + try self.threadPool.syncShutdownGracefully() + self.threadPool = nil + + try super.tearDownWithError() + } +} + +extension DatabaseID { + static let migrate1 = DatabaseID(string: "migrate1") + static let migrate2 = DatabaseID(string: "migrate2") +} + +struct TestMigration: Migration { + let name = "test_migration" + + func prepare(on database: Database) -> EventLoopFuture { + return database.schema("foo") + .field("id", .uuid, .identifier(auto: false)) + .field("bar", .string, .required) + .field("baz", .int, .required) + .field("fizz", .bool) + .field("buzz", .double) + .create() + } + + func revert(on database: Database) -> EventLoopFuture { + return database.schema("foo").delete() + } +} From 2f5239c4c64c63f290416e5eef5bd07264b39437 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:55:11 -0500 Subject: [PATCH 10/29] Use 'guard' statements to check failed errors in Migrator preview methods --- Sources/FluentKit/Migration/Migrator.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 7914bcf5..0d6e67b9 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -109,7 +109,7 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } return self.lastBatchNumber(on: database).flatMap { lastBatch in self.preparedMigrations(batch: lastBatch, on: database) @@ -129,7 +129,7 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) @@ -147,7 +147,7 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) From 6c0ad52b8f7d561438af1919f99ae6dda37cf3d0 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:56:17 -0500 Subject: [PATCH 11/29] Use .recover instead of .flatMapErrorThrowing to capture failures in Migrator preview methods --- Sources/FluentKit/Migration/Migrator.swift | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 0d6e67b9..5ec9a28a 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -95,9 +95,7 @@ public struct Migrator { return self.unpreparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.flatMapErrorThrowing { error in - failed = error - } + }.recover { failed = $0 } }.flatMapThrowing { if let error = failed { throw error } return batch @@ -115,9 +113,7 @@ public struct Migrator { self.preparedMigrations(batch: lastBatch, on: database) }.map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.flatMapErrorThrowing { error in - failed = error - } + }.recover { failed = $0 } }.flatMapThrowing { if let error = failed { throw error } return batch @@ -133,9 +129,7 @@ public struct Migrator { return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.flatMapErrorThrowing { error in - failed = error - } + }.recover { failed = $0 } }.flatMapThrowing { if let error = failed { throw error } return batch @@ -151,9 +145,7 @@ public struct Migrator { return self.preparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.flatMapErrorThrowing { error in - failed = error - } + }.recover { failed = $0 } }.flatMapThrowing { if let error = failed { throw error } return batch From 0803c7a3813e4f6f7b3e5ac9697518b51f453b97 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:58:17 -0500 Subject: [PATCH 12/29] Use !contains instead of count check in Migrator.unpreparedMigrations(on:) method --- Sources/FluentKit/Migration/Migrator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 5ec9a28a..f2f27402 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -205,7 +205,7 @@ public struct Migrator { .map { logs -> [Migrations.Item] in return self.migrations.storage.compactMap { item in - if item.id == databaseID && logs.filter({ $0.name == item.migration.name }).count == 0 { + if item.id == databaseID && !logs.contains(where: { $0.name == item.migration.name }) { return item } else { // log found, this has been prepared From aeb0e4f259cc5910de42f078d699f4084ec3f718 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Mon, 11 May 2020 12:58:40 -0500 Subject: [PATCH 13/29] Cleanup newlines in Migrator.run(on:_:) method --- Sources/FluentKit/Migration/Migrator.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index f2f27402..53135ded 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -221,10 +221,7 @@ public struct Migrator { } private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID?) -> EventLoopFuture) -> EventLoopFuture { - if let id = database { - return query(id) - } - + if let id = database { return query(id) } let queries = self.migrations.databases.map(query) return EventLoopFuture.whenAllSucceed([query(nil)] + queries, on: self.eventLoop).map { _ in () } From c48b0cae721187b73da3e81ad4774e192d0dd610 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 10:46:24 -0500 Subject: [PATCH 14/29] Give database configurations access to database ID in DatabaseConfigurationFactory closure --- Sources/FluentKit/Database/Databases.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Sources/FluentKit/Database/Databases.swift b/Sources/FluentKit/Database/Databases.swift index 705f8478..886cf4ac 100644 --- a/Sources/FluentKit/Database/Databases.swift +++ b/Sources/FluentKit/Database/Databases.swift @@ -3,10 +3,22 @@ import class NIOConcurrencyHelpers.Lock @_exported import class NIO.NIOThreadPool public struct DatabaseConfigurationFactory { - public let make: () -> DatabaseConfiguration + private let build: (DatabaseID?) -> DatabaseConfiguration + + public var make: () -> DatabaseConfiguration { + return { self.build(nil) } + } public init(make: @escaping () -> DatabaseConfiguration) { - self.make = make + self.build = { _ in make() } + } + + public init(make: @escaping (DatabaseID?) -> DatabaseConfiguration) { + self.build = make + } + + public func make(_ id: DatabaseID?) -> DatabaseConfiguration { + return self.build(id) } } @@ -57,7 +69,7 @@ public final class Databases { as id: DatabaseID, isDefault: Bool? = nil ) { - self.use(configuration.make(), as: id, isDefault: isDefault) + self.use(configuration.make(id), as: id, isDefault: isDefault) } public func use( From 0599e619cee29cd71742fe623d26326e8b423ac2 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 11:20:41 -0500 Subject: [PATCH 15/29] Run MigrationLog queries on Migration.Item database --- Sources/FluentKit/Migration/Migrator.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 53135ded..62eba0e2 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -91,7 +91,7 @@ public struct Migrator { var failed: Error? = nil return self.run(on: databaseID) { database in - if case .some = failed { return self.eventLoop.makeSucceededFuture(()) } + guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } return self.unpreparedMigrations(on: database).map { items in batch.append(contentsOf: items.map { ($0.migration, $0.id) }) @@ -157,13 +157,13 @@ public struct Migrator { private func prepare(_ item: Migrations.Item, batch: Int) -> EventLoopFuture { item.migration.prepare(on: self.database(item.id)).flatMap { MigrationLog(name: item.migration.name, batch: batch) - .save(on: self.database(nil)) + .save(on: self.database(item.id)) } } private func revert(_ item: Migrations.Item) -> EventLoopFuture { item.migration.revert(on: self.database(item.id)).flatMap { - MigrationLog.query(on: self.database(nil)) + MigrationLog.query(on: self.database(item.id)) .filter(\.$name == item.migration.name) .delete() } From 3b48ba7a1896b48acc1d37d7bbd53c5081e67473 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 11:23:10 -0500 Subject: [PATCH 16/29] Require DatabaseIDs for FluentBenchmarker to access separate databases --- Sources/FluentBenchmark/FluentBenchmarker.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/FluentBenchmark/FluentBenchmarker.swift b/Sources/FluentBenchmark/FluentBenchmarker.swift index 82aaaf77..1d8405b9 100644 --- a/Sources/FluentBenchmark/FluentBenchmarker.swift +++ b/Sources/FluentBenchmark/FluentBenchmarker.swift @@ -5,6 +5,7 @@ import XCTest public final class FluentBenchmarker { public let databases: Databases + public let ids: (DatabaseID, DatabaseID) public var database: Database { self.databases.database( @@ -13,8 +14,11 @@ public final class FluentBenchmarker { )! } - public init(databases: Databases) { + public init(databases: Databases, _ ids: (DatabaseID, DatabaseID)) { + precondition(ids.0 != ids.1, "FluentBecnhmarker DatabaseIDs must be non-equivalent") + self.databases = databases + self.ids = ids } public func testAll() throws { From 899abb87f299dbceef926113446510515f0093fc Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 11:23:36 -0500 Subject: [PATCH 17/29] Created FluentBenchmarker.testMigrator_sequence test case --- .../FluentBenchmark/Tests/MigratorTests.swift | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Sources/FluentBenchmark/Tests/MigratorTests.swift b/Sources/FluentBenchmark/Tests/MigratorTests.swift index 2960d6c8..da191624 100644 --- a/Sources/FluentBenchmark/Tests/MigratorTests.swift +++ b/Sources/FluentBenchmark/Tests/MigratorTests.swift @@ -2,6 +2,7 @@ extension FluentBenchmarker { public func testMigrator() throws { try self.testMigrator_success() try self.testMigrator_error() + try self.testMigrator_sequence() } private func testMigrator_success() throws { @@ -53,6 +54,70 @@ extension FluentBenchmarker { try migrator.revertAllBatches().wait() } } + + private func testMigrator_sequence() throws { + try self.runTest(#function, []) { + + // Setup + let database1 = try XCTUnwrap( + self.databases.database( + self.ids.0, + logger: Logger(label: "codes.vapor.tests"), + on: self.databases.eventLoopGroup.next() + ) + ) + let database2 = try XCTUnwrap( + self.databases.database( + self.ids.1, + logger: Logger(label: "codes.vapor.tests"), + on: self.databases.eventLoopGroup.next() + ) + ) + + let migrations = Migrations() + + + // Migration #1 + migrations.add(GalaxyMigration(), to: self.ids.0) + + let migrator = Migrator( + databases: self.databases, + migrations: migrations, + logger: Logger(label: "codes.vapor.tests"), + on: self.databases.eventLoopGroup.next() + ) + + try migrator.setupIfNeeded().wait() + try migrator.prepareBatch().wait() + + let logs1 = try MigrationLog.query(on: database1).all().wait() + XCTAssertEqual(logs1.count, 1) + let log1 = try XCTUnwrap(logs1.first) + XCTAssertEqual(log1.batch, 1) + XCTAssertEqual(log1.name, "\(GalaxyMigration.self)") + + try XCTAssertThrowsError(MigrationLog.query(on: database2).count().wait()) + + + // Migration #2 + migrations.add(GalaxyMigration(), to: self.ids.1) + + try migrator.setupIfNeeded().wait() + try migrator.prepareBatch().wait() + + let logs2 = try MigrationLog.query(on: database2).all().wait() + XCTAssertEqual(logs2.count, 1) + let log2 = try XCTUnwrap(logs2.first) + XCTAssertEqual(log2.batch, 1) + XCTAssertEqual(log2.name, "\(GalaxyMigration.self)") + + try XCTAssertEqual(MigrationLog.query(on: database1).count().wait(), 1) + + + // Teardown + try migrator.revertAllBatches().wait() + } + } } private struct ErrorMigration: Migration { From cc9d2226f539ecaa94c8eeb2b8dc215bb62d3de3 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 11:23:50 -0500 Subject: [PATCH 18/29] Removed FluentKit MigratorTests --- Tests/FluentKitTests/MigratorTests.swift | 145 ----------------------- 1 file changed, 145 deletions(-) delete mode 100644 Tests/FluentKitTests/MigratorTests.swift diff --git a/Tests/FluentKitTests/MigratorTests.swift b/Tests/FluentKitTests/MigratorTests.swift deleted file mode 100644 index bd7cbe39..00000000 --- a/Tests/FluentKitTests/MigratorTests.swift +++ /dev/null @@ -1,145 +0,0 @@ -@testable import FluentKit -import XCTest -import NIO - -final class MigratorTests: XCTestCase { - func testSingleMigration() throws { - let migrations = Migrations() - migrations.add(TestMigration(), to: .migrate1) - - let migrator = self.migrator(using: migrations) - - try migrator.setupIfNeeded().wait() - try migrator.prepareBatch().wait() - - let logs = try MigrationLog.query(on: self.database(.migrate1)).all().wait() - XCTAssertEqual(logs.count, 1) - let log = try XCTUnwrap(logs.first) - - XCTAssertEqual(log.batch, 1) - XCTAssertEqual(log.name, TestMigration().name) - - try XCTAssertEqual(MigrationLog.query(on: self.database(.migrate2)).all().wait().count, 0) - } - - func testMultipleMigrations() throws { - let migrations = Migrations() - migrations.add(TestMigration(), to: .migrate1) - migrations.add(TestMigration(), to: .migrate2) - - let migrator = self.migrator(using: migrations) - - try migrator.setupIfNeeded().wait() - try migrator.prepareBatch().wait() - - let logs1 = try MigrationLog.query(on: self.database(.migrate1)).all().wait() - XCTAssertEqual(logs1.count, 1) - - let log1 = try XCTUnwrap(logs1.first) - XCTAssertEqual(log1.batch, 1) - XCTAssertEqual(log1.name, TestMigration().name) - - - let logs2 = try MigrationLog.query(on: self.database(.migrate2)).all().wait() - XCTAssertEqual(logs2.count, 1) - - let log2 = try XCTUnwrap(logs1.first) - XCTAssertEqual(log2.batch, 1) - XCTAssertEqual(log2.name, TestMigration().name) - } - - func testSubsequentMigration() throws { - let migrations = Migrations() - - - migrations.add(TestMigration(), to: .migrate1) - let migrator1 = self.migrator(using: migrations) - - try migrator1.setupIfNeeded().wait() - try migrator1.prepareBatch().wait() - - let logs1 = try MigrationLog.query(on: self.database(.migrate1)).all().wait() - XCTAssertEqual(logs1.count, 1) - - let log1 = try XCTUnwrap(logs1.first) - XCTAssertEqual(log1.batch, 1) - XCTAssertEqual(log1.name, TestMigration().name) - - - migrations.add(TestMigration(), to: .migrate2) - let migrator2 = self.migrator(using: migrations) - - try migrator2.setupIfNeeded().wait() - try migrator2.prepareBatch().wait() - - let logs2 = try MigrationLog.query(on: self.database(.migrate2)).all().wait() - XCTAssertEqual(logs2.count, 1) - - let log2 = try XCTUnwrap(logs2.first) - XCTAssertEqual(log2.batch, 1) - XCTAssertEqual(log2.name, TestMigration().name) - } - - - var eventLoopGroup: EventLoopGroup! - var threadPool: NIOThreadPool! - var databases: Databases! - - - func migrator(using migrations: Migrations) -> Migrator { - return Migrator( - databases: self.databases, - migrations: migrations, - logger: Logger(label: "codes.tests.vapor.migrations"), - on: self.eventLoopGroup.next() - ) - } - - func database(_ id: DatabaseID) -> Database { - return self.databases.database(id, logger: Logger(label: "com.databases.\(id.string)"), on: self.eventLoopGroup.next())! - } - - override func setUpWithError() throws { - try super.setUpWithError() - - self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - self.threadPool = NIOThreadPool(numberOfThreads: 1) - self.databases = Databases(threadPool: self.threadPool, on: self.eventLoopGroup) - } - - override func tearDownWithError() throws { - self.databases.shutdown() - self.databases = nil - - try self.eventLoopGroup.syncShutdownGracefully() - self.eventLoopGroup = nil - - try self.threadPool.syncShutdownGracefully() - self.threadPool = nil - - try super.tearDownWithError() - } -} - -extension DatabaseID { - static let migrate1 = DatabaseID(string: "migrate1") - static let migrate2 = DatabaseID(string: "migrate2") -} - -struct TestMigration: Migration { - let name = "test_migration" - - func prepare(on database: Database) -> EventLoopFuture { - return database.schema("foo") - .field("id", .uuid, .identifier(auto: false)) - .field("bar", .string, .required) - .field("baz", .int, .required) - .field("fizz", .bool) - .field("buzz", .double) - .create() - } - - func revert(on database: Database) -> EventLoopFuture { - return database.schema("foo").delete() - } -} From 74c93371a6061d6325733e2462b3776e82874b3d Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 16:30:56 -0500 Subject: [PATCH 19/29] Assert database2 log count is 0 if no errors are throw from query --- Sources/FluentBenchmark/Tests/MigratorTests.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/FluentBenchmark/Tests/MigratorTests.swift b/Sources/FluentBenchmark/Tests/MigratorTests.swift index da191624..b547cae8 100644 --- a/Sources/FluentBenchmark/Tests/MigratorTests.swift +++ b/Sources/FluentBenchmark/Tests/MigratorTests.swift @@ -96,7 +96,17 @@ extension FluentBenchmarker { XCTAssertEqual(log1.batch, 1) XCTAssertEqual(log1.name, "\(GalaxyMigration.self)") - try XCTAssertThrowsError(MigrationLog.query(on: database2).count().wait()) + do { + let count = try MigrationLog.query(on: database2).count().wait() + + // This is a valid state to enter. Unlike database in the SQL family, + // some databases such as MongoDB won't throw an error if the table doesn't exist. + XCTAssertEqual(count, 0) + } catch let error { + // This is a valid state to enter. A SQL database with throw an error + // because the `_fluent_migrations` table on the `database2` database + // will have not been created yet. + } // Migration #2 From faa066275b7a48f95ce155aeb76d39948f53380c Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 12 May 2020 16:40:50 -0500 Subject: [PATCH 20/29] Fixed typo in MigrationLog database2 query catch block comment --- Sources/FluentBenchmark/Tests/MigratorTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FluentBenchmark/Tests/MigratorTests.swift b/Sources/FluentBenchmark/Tests/MigratorTests.swift index b547cae8..85b18315 100644 --- a/Sources/FluentBenchmark/Tests/MigratorTests.swift +++ b/Sources/FluentBenchmark/Tests/MigratorTests.swift @@ -103,7 +103,7 @@ extension FluentBenchmarker { // some databases such as MongoDB won't throw an error if the table doesn't exist. XCTAssertEqual(count, 0) } catch let error { - // This is a valid state to enter. A SQL database with throw an error + // This is a valid state to enter. A SQL database will throw an error // because the `_fluent_migrations` table on the `database2` database // will have not been created yet. } From 46ef28d2c738ca660081fb0ebbffc70ed4b85e47 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 10:06:51 -0500 Subject: [PATCH 21/29] Removed DatabaseID injection from DatabaseConfigurationFactory --- Sources/FluentKit/Database/Databases.swift | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Sources/FluentKit/Database/Databases.swift b/Sources/FluentKit/Database/Databases.swift index 886cf4ac..705f8478 100644 --- a/Sources/FluentKit/Database/Databases.swift +++ b/Sources/FluentKit/Database/Databases.swift @@ -3,22 +3,10 @@ import class NIOConcurrencyHelpers.Lock @_exported import class NIO.NIOThreadPool public struct DatabaseConfigurationFactory { - private let build: (DatabaseID?) -> DatabaseConfiguration - - public var make: () -> DatabaseConfiguration { - return { self.build(nil) } - } + public let make: () -> DatabaseConfiguration public init(make: @escaping () -> DatabaseConfiguration) { - self.build = { _ in make() } - } - - public init(make: @escaping (DatabaseID?) -> DatabaseConfiguration) { - self.build = make - } - - public func make(_ id: DatabaseID?) -> DatabaseConfiguration { - return self.build(id) + self.make = make } } @@ -69,7 +57,7 @@ public final class Databases { as id: DatabaseID, isDefault: Bool? = nil ) { - self.use(configuration.make(id), as: id, isDefault: isDefault) + self.use(configuration.make(), as: id, isDefault: isDefault) } public func use( From 6b6f98f05680733fbc2b1ec8c34c329b2dedc26d Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 10:42:26 -0500 Subject: [PATCH 22/29] Change Migrations.databases to a computed property --- Sources/FluentKit/Migration/Migrations.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrations.swift b/Sources/FluentKit/Migration/Migrations.swift index b6af20f0..3c73bd08 100644 --- a/Sources/FluentKit/Migration/Migrations.swift +++ b/Sources/FluentKit/Migration/Migrations.swift @@ -5,15 +5,13 @@ public final class Migrations { } var storage: [Item] - var databases: Set + var databases: Set { Set(self.storage.compactMap(\.id)) } public init() { self.storage = [] - self.databases = [] } public func add(_ migration: Migration, to id: DatabaseID? = nil) { self.storage.append(.init(id: id, migration: migration)) - if let id = id { self.databases.insert(id) } } } From 9af4e1ff91ff22feddd980a4ea6896b8242e9747 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 10:45:47 -0500 Subject: [PATCH 23/29] Added .ids() method to Databases type --- Sources/FluentKit/Database/Databases.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/FluentKit/Database/Databases.swift b/Sources/FluentKit/Database/Databases.swift index 705f8478..e899754b 100644 --- a/Sources/FluentKit/Database/Databases.swift +++ b/Sources/FluentKit/Database/Databases.swift @@ -122,6 +122,10 @@ public final class Databases { } } + public func ids() -> Set { + return self.lock.withLock { Set(self.configurations.keys) } + } + public func shutdown() { self.lock.lock() defer { self.lock.unlock() } From ba290bc6b370b37bb34db7032670709391824c25 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 10:56:21 -0500 Subject: [PATCH 24/29] Get database IDs for FluentBenchmark.testMigrator_sequence from Databases.ids() method --- Sources/FluentBenchmark/FluentBenchmarker.swift | 7 ++----- Sources/FluentBenchmark/Tests/MigratorTests.swift | 13 ++++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/FluentBenchmark/FluentBenchmarker.swift b/Sources/FluentBenchmark/FluentBenchmarker.swift index 1d8405b9..9630fe22 100644 --- a/Sources/FluentBenchmark/FluentBenchmarker.swift +++ b/Sources/FluentBenchmark/FluentBenchmarker.swift @@ -5,7 +5,6 @@ import XCTest public final class FluentBenchmarker { public let databases: Databases - public let ids: (DatabaseID, DatabaseID) public var database: Database { self.databases.database( @@ -14,11 +13,9 @@ public final class FluentBenchmarker { )! } - public init(databases: Databases, _ ids: (DatabaseID, DatabaseID)) { - precondition(ids.0 != ids.1, "FluentBecnhmarker DatabaseIDs must be non-equivalent") - + public init(databases: Databases) { + precondition(databases.ids().count >= 2, "FluentBenchmarker Databases instance must have 2 or more registered databases") self.databases = databases - self.ids = ids } public func testAll() throws { diff --git a/Sources/FluentBenchmark/Tests/MigratorTests.swift b/Sources/FluentBenchmark/Tests/MigratorTests.swift index 85b18315..79076751 100644 --- a/Sources/FluentBenchmark/Tests/MigratorTests.swift +++ b/Sources/FluentBenchmark/Tests/MigratorTests.swift @@ -59,16 +59,19 @@ extension FluentBenchmarker { try self.runTest(#function, []) { // Setup + let ids = Array(self.databases.ids()) + let databaseID = (ids[0], ids[1]) + let database1 = try XCTUnwrap( self.databases.database( - self.ids.0, + databaseID.0, logger: Logger(label: "codes.vapor.tests"), on: self.databases.eventLoopGroup.next() ) ) let database2 = try XCTUnwrap( self.databases.database( - self.ids.1, + databaseID.1, logger: Logger(label: "codes.vapor.tests"), on: self.databases.eventLoopGroup.next() ) @@ -78,7 +81,7 @@ extension FluentBenchmarker { // Migration #1 - migrations.add(GalaxyMigration(), to: self.ids.0) + migrations.add(GalaxyMigration(), to: databaseID.0) let migrator = Migrator( databases: self.databases, @@ -102,7 +105,7 @@ extension FluentBenchmarker { // This is a valid state to enter. Unlike database in the SQL family, // some databases such as MongoDB won't throw an error if the table doesn't exist. XCTAssertEqual(count, 0) - } catch let error { + } catch { // This is a valid state to enter. A SQL database will throw an error // because the `_fluent_migrations` table on the `database2` database // will have not been created yet. @@ -110,7 +113,7 @@ extension FluentBenchmarker { // Migration #2 - migrations.add(GalaxyMigration(), to: self.ids.1) + migrations.add(GalaxyMigration(), to: databaseID.1) try migrator.setupIfNeeded().wait() try migrator.prepareBatch().wait() From 1921982854d97a8e14f2769228bb7c9a01ae99b2 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 16:23:33 -0500 Subject: [PATCH 25/29] Allow nil in Migrations.databases set --- Sources/FluentKit/Migration/Migrations.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FluentKit/Migration/Migrations.swift b/Sources/FluentKit/Migration/Migrations.swift index 3c73bd08..25ce79e6 100644 --- a/Sources/FluentKit/Migration/Migrations.swift +++ b/Sources/FluentKit/Migration/Migrations.swift @@ -5,7 +5,7 @@ public final class Migrations { } var storage: [Item] - var databases: Set { Set(self.storage.compactMap(\.id)) } + var databases: Set { Set(self.storage.map(\.id)) } public init() { self.storage = [] From 86c4641f2d9960ceecb38f3f41d49a7e746a7760 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 16:23:53 -0500 Subject: [PATCH 26/29] Use underlying DatabaseMigrator type to run migrations in Migrator --- Sources/FluentKit/Migration/Migrator.swift | 320 ++++++++++----------- 1 file changed, 148 insertions(+), 172 deletions(-) diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 62eba0e2..8629aecd 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -1,11 +1,12 @@ import Foundation +import AsyncKit import Logging public struct Migrator { public let databaseFactory: (DatabaseID?) -> (Database) public let migrations: Migrations public let eventLoop: EventLoop - + public init( databases: Databases, migrations: Migrations, @@ -33,230 +34,205 @@ public struct Migrator { // MARK: Setup - public func setupIfNeeded(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: databaseID) { MigrationLog.migration.prepare(on: self.database($0)) } + public func setupIfNeeded() -> EventLoopFuture { + return self.migrators() { $0.setupIfNeeded() }.transform(to: ()) } // MARK: Prepare - public func prepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: databaseID) { database in - self.unpreparedMigrations(on: database).flatMap { migrations in - self.lastBatchNumber(on: database) - .and(value: migrations) - }.flatMap { (lastBatch, migrations) in - .andAllSync(migrations.map { item in - { self.prepare(item, batch: lastBatch + 1) } - }, on: self.eventLoop) - } - } + public func prepareBatch() -> EventLoopFuture { + return self.migrators() { $0.prepareBatch() }.transform(to: ()) } // MARK: Revert - public func revertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: databaseID) { database in - self.lastBatchNumber(on: database).flatMap { - self.revertBatch(number: $0, on: database) + public func revertLastBatch() -> EventLoopFuture { + return self.migrators() { $0.revertLastBatch() }.transform(to: ()) + } + + public func revertBatch(number: Int) -> EventLoopFuture { + return self.migrators() { $0.revertBatch(number: number) }.transform(to: ()) + } + + public func revertAllBatches() -> EventLoopFuture { + return self.migrators() { $0.revertAllBatches() }.transform(to: ()) + } + + // MARK: Preview + + public func previewPrepareBatch() -> EventLoopFuture<[(Migration, DatabaseID?)]> { + return self.migrators() { migrator in + return migrator.previewPrepareBatch().and(value: migrator.id) + }.map { items in + return items.reduce(into: []) { result, batch in + let pairs = batch.0.map { ($0, batch.1) } + result.append(contentsOf: pairs) } } } - public func revertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: databaseID) { database in - self.preparedMigrations(batch: number, on: database).flatMap { migrations in - EventLoopFuture.andAllSync(migrations.map { item in - { self.revert(item) } - }, on: self.eventLoop) + public func previewRevertLastBatch() -> EventLoopFuture<[(Migration, DatabaseID?)]> { + return self.migrators() { migrator in + return migrator.previewRevertLastBatch().and(value: migrator.id) + }.map { items in + return items.reduce(into: []) { result, batch in + let pairs = batch.0.map { ($0, batch.1) } + result.append(contentsOf: pairs) } } } - public func revertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture { - self.run(on: databaseID) { database in - self.preparedMigrations(on: database).flatMap { migrations in - .andAllSync(migrations.map { item in - { self.revert(item) } - }, on: self.eventLoop) - }.flatMap { _ in - self.revertMigrationLog(on: database) + public func previewRevertBatch() -> EventLoopFuture<[(Migration, DatabaseID?)]> { + return self.migrators() { migrator in + return migrator.previewPrepareBatch().and(value: migrator.id) + }.map { items in + return items.reduce(into: []) { result, batch in + let pairs = batch.0.map { ($0, batch.1) } + result.append(contentsOf: pairs) } } } - // MARK: Preview - - public func previewPrepareBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - var batch: [(Migration, DatabaseID?)] = [] - var failed: Error? = nil + public func previewRevertAllBatches() -> EventLoopFuture<[(Migration, DatabaseID?)]> { + return self.migrators() { migrator in + return migrator.previewRevertAllBatches().and(value: migrator.id) + }.map { items in + return items.reduce(into: []) { result, batch in + let pairs = batch.0.map { ($0, batch.1) } + result.append(contentsOf: pairs) + } + } + } - return self.run(on: databaseID) { database in - guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } - return self.unpreparedMigrations(on: database).map { items in - batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.recover { failed = $0 } - }.flatMapThrowing { - if let error = failed { throw error } - return batch - } + private func migrators( + _ handler: (DatabaseMigrator) -> EventLoopFuture + ) -> EventLoopFuture<[Result]> { + return self.migrations.databases.map { id in + let migrations = self.migrations.storage.compactMap { item -> Migration? in + guard item.id == id else { return nil } + return item.migration + } + + let migrator = DatabaseMigrator(id: id, database: self.databaseFactory(id), migrations: migrations) + return handler(migrator) + }.flatten(on: self.eventLoop) } - - public func previewRevertLastBatch(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - var batch: [(Migration, DatabaseID?)] = [] - var failed: Error? = nil +} - return self.run(on: databaseID) { database in - guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } +private final class DatabaseMigrator { + let migrations: [Migration] + let database: Database + let id: DatabaseID? - return self.lastBatchNumber(on: database).flatMap { lastBatch in - self.preparedMigrations(batch: lastBatch, on: database) - }.map { items in - batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.recover { failed = $0 } - }.flatMapThrowing { - if let error = failed { throw error } - return batch - } + init(id: DatabaseID?, database: Database, migrations: [Migration]) { + self.migrations = migrations + self.database = database + self.id = id } - - public func previewRevertBatch(number: Int, on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - var batch: [(Migration, DatabaseID?)] = [] - var failed: Error? = nil - return self.run(on: databaseID) { database in - guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } + // MARK: Setup - return self.preparedMigrations(on: database).map { items in - batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.recover { failed = $0 } - }.flatMapThrowing { - if let error = failed { throw error } - return batch - } + func setupIfNeeded() -> EventLoopFuture { + return MigrationLog.migration.prepare(on: self.database) } - - public func previewRevertAllBatches(on databaseID: DatabaseID? = nil) -> EventLoopFuture<[(Migration, DatabaseID?)]> { - var batch: [(Migration, DatabaseID?)] = [] - var failed: Error? = nil - return self.run(on: databaseID) { database in - guard failed == nil else { return self.eventLoop.makeSucceededFuture(()) } + // MARK: Prepare - return self.preparedMigrations(on: database).map { items in - batch.append(contentsOf: items.map { ($0.migration, $0.id) }) - }.recover { failed = $0 } - }.flatMapThrowing { - if let error = failed { throw error } - return batch + func prepareBatch() -> EventLoopFuture { + return self.unpreparedMigrations().flatMap { migrations in + return self.lastBatchNumber().and(value: migrations) + }.flatMap { batch, migrations in + return EventLoopFutureQueue(eventLoop: self.database.eventLoop).append(each: migrations) { migration in + self.prepare(migration, batch: batch) + } } } - - // MARK: Private - - private func prepare(_ item: Migrations.Item, batch: Int) -> EventLoopFuture { - item.migration.prepare(on: self.database(item.id)).flatMap { - MigrationLog(name: item.migration.name, batch: batch) - .save(on: self.database(item.id)) + + // MARK: Revert + + func revertLastBatch() -> EventLoopFuture { + return self.lastBatchNumber().flatMap(self.revertBatch(number:)) + } + + func revertBatch(number: Int) -> EventLoopFuture { + return self.preparedMigrations(batch: number).flatMap { migrations in + return EventLoopFutureQueue(eventLoop: self.database.eventLoop).append(each: migrations, self.revert) } } - - private func revert(_ item: Migrations.Item) -> EventLoopFuture { - item.migration.revert(on: self.database(item.id)).flatMap { - MigrationLog.query(on: self.database(item.id)) - .filter(\.$name == item.migration.name) - .delete() + + func revertAllBatches() -> EventLoopFuture { + return self.preparedMigrations().flatMap { migrations in + return EventLoopFutureQueue(eventLoop: self.database.eventLoop).append(each: migrations, self.revert) } } - - private func revertMigrationLog(on databaseID: DatabaseID?) -> EventLoopFuture { - MigrationLog.migration.revert(on: self.database(databaseID)) + + // MARK: Preview + + func previewPrepareBatch() -> EventLoopFuture<[Migration]> { + return self.unpreparedMigrations() } - - private func lastBatchNumber(on databaseID: DatabaseID?) -> EventLoopFuture { - MigrationLog.query(on: self.database(databaseID)).sort(\.$batch, .descending).first().map { log in - log?.batch ?? 0 + + func previewRevertLastBatch() -> EventLoopFuture<[Migration]> { + return self.lastBatchNumber().flatMap { batch in + return self.preparedMigrations(batch: batch) } } - - private func preparedMigrations(on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { - MigrationLog.query(on: self.database(databaseID)).all().map { logs -> [Migrations.Item] in - self.migrations.storage.filter { storage in - logs.contains { log in - storage.migration.name == log.name - } && storage.id == databaseID - }.reversed() - } + + func previewRevertBatch(number: Int) -> EventLoopFuture<[Migration]> { + return self.preparedMigrations(batch: number) } - - private func preparedMigrations(batch: Int, on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { - MigrationLog.query(on: self.database(databaseID)).filter(\.$batch == batch).all().map { logs in - self.migrations.storage.filter { storage in - logs.contains { log in - storage.migration.name == log.name - } && storage.id == databaseID - }.reversed() - } + + func previewRevertAllBatches() -> EventLoopFuture<[Migration]> { + return self.preparedMigrations() } - - private func unpreparedMigrations(on databaseID: DatabaseID?) -> EventLoopFuture<[Migrations.Item]> { - return MigrationLog.query(on: self.database(databaseID)) - .all() - .map - { logs -> [Migrations.Item] in - return self.migrations.storage.compactMap { item in - if item.id == databaseID && !logs.contains(where: { $0.name == item.migration.name }) { - return item - } else { - // log found, this has been prepared - return nil - } - } + + // MARK: Private + + private func prepare(_ migration: Migration, batch: Int) -> EventLoopFuture { + return migration.prepare(on: self.database).flatMap { + return MigrationLog(name: migration.name, batch: batch).save(on: self.database) } } + private func revert(_ migration: Migration) -> EventLoopFuture { + return migration.revert(on: self.database).flatMap { + return MigrationLog.query(on: self.database).filter(\.$name == migration.name).delete() + } + } - private func database(_ id: DatabaseID?) -> Database { - self.databaseFactory(id) + private func revertMigrationLog() -> EventLoopFuture { + return MigrationLog.migration.revert(on: self.database) } - private func run(on database: DatabaseID? = nil, _ query: @escaping (DatabaseID?) -> EventLoopFuture) -> EventLoopFuture { - if let id = database { return query(id) } + private func lastBatchNumber() -> EventLoopFuture { + return MigrationLog.query(on: self.database).sort(\.$batch, .descending).first().map { log in + log?.batch ?? 0 + } + } - let queries = self.migrations.databases.map(query) - return EventLoopFuture.whenAllSucceed([query(nil)] + queries, on: self.eventLoop).map { _ in () } + private func preparedMigrations() -> EventLoopFuture<[Migration]> { + return MigrationLog.query(on: self.database).all().map { logs in + return self.migrations.filter { migration in + return logs.contains(where: { $0.name == migration.name }) + }.reversed() + } } -} -extension EventLoopFuture { - public static func andAllSync( - _ futures: [() -> EventLoopFuture], - on eventLoop: EventLoop - ) -> EventLoopFuture { - let promise = eventLoop.makePromise(of: Void.self) + private func preparedMigrations(batch: Int) -> EventLoopFuture<[Migration]> { + return MigrationLog.query(on: self.database).filter(\.$batch == batch).all().map { logs in + return self.migrations.filter { migration in + return logs.contains(where: { $0.name == migration.name }) + }.reversed() + } + } - var iterator = futures.makeIterator() - func handle(_ future: () -> EventLoopFuture) { - future().whenComplete { res in - switch res { - case .success: - if let next = iterator.next() { - handle(next) - } else { - promise.succeed(()) - } - case .failure(let error): - promise.fail(error) - } + private func unpreparedMigrations() -> EventLoopFuture<[Migration]> { + return MigrationLog.query(on: self.database).all().map { logs in + return self.migrations.compactMap { migration in + if logs.contains(where: { $0.name == migration.name }) { return nil } + return migration } } - - if let first = iterator.next() { - handle(first) - } else { - promise.succeed(()) - } - - return promise.futureResult } } From 297d83d52f28463c64b151800cb6900e47ea0371 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Thu, 14 May 2020 16:33:13 -0500 Subject: [PATCH 27/29] Removed stray use of .andAllSync from QueryBuilder --- .../FluentBenchmark/SolarSystem/SolarSystem.swift | 12 ++++++------ Sources/FluentKit/Query/Builder/QueryBuilder.swift | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift b/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift index 35b71cbd..c69ea5f8 100644 --- a/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift +++ b/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift @@ -1,3 +1,5 @@ +import AsyncKit + private let migrations: [Migration] = [ GalaxyMigration(), StarMigration(), @@ -29,9 +31,8 @@ public struct SolarSystem: Migration { } else { all = migrations } - return .andAllSync(all.map { migration in - { migration.prepare(on: database) } - }, on: database.eventLoop) + + return EventLoopFutureQueue(eventLoop: database.eventLoop).append(each: all) { $0.prepare(on: database) } } public func revert(on database: Database) -> EventLoopFuture { @@ -41,8 +42,7 @@ public struct SolarSystem: Migration { } else { all = migrations } - return .andAllSync(all.reversed().map { migration in - { migration.revert(on: database) } - }, on: database.eventLoop) + + return EventLoopFutureQueue(eventLoop: database.eventLoop).append(each: all) { $0.revert(on: database) } } } diff --git a/Sources/FluentKit/Query/Builder/QueryBuilder.swift b/Sources/FluentKit/Query/Builder/QueryBuilder.swift index dce60d96..b2bd3230 100644 --- a/Sources/FluentKit/Query/Builder/QueryBuilder.swift +++ b/Sources/FluentKit/Query/Builder/QueryBuilder.swift @@ -1,3 +1,4 @@ +import AsyncKit import NIO public final class QueryBuilder @@ -206,9 +207,9 @@ public final class QueryBuilder return self.database.eventLoop.makeSucceededFuture(()) } // run eager loads - return .andAllSync(self.eagerLoaders.map { eagerLoad in - { eagerLoad.anyRun(models: all, on: self.database) } - }, on: self.database.eventLoop) + return EventLoopFutureQueue(eventLoop: self.database.eventLoop).append(each: self.eagerLoaders) { loader in + return loader.anyRun(models: all, on: self.database) + } } } else { return done From e4e6760718d64c58e6f95f8fb3dbe6f70f5df151 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Fri, 15 May 2020 01:46:31 -0500 Subject: [PATCH 28/29] Add missing AsyncKit dependency requirement (breaks the build, why was this working before...?). Fix Migration reversion in FluentBenchmarker (migrations have to be reverted in reverse order). Fix batch numbers (you have to actually change the previous batch number if you expect it to, you know, change). --- Package.swift | 2 ++ Sources/FluentBenchmark/SolarSystem/SolarSystem.swift | 2 +- Sources/FluentBenchmark/Tests/MigratorTests.swift | 2 +- Sources/FluentKit/Migration/Migrator.swift | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index f6493e28..85cd400b 100644 --- a/Package.swift +++ b/Package.swift @@ -16,11 +16,13 @@ let package = Package( .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0-rc.1"), + .package(url:" https://github.com/vapor/async-kit.git", from: "1.0.0-rc.1"), ], targets: [ .target(name: "FluentKit", dependencies: [ .product(name: "NIO", package: "swift-nio"), .product(name: "Logging", package: "swift-log"), + .product(name: "AsyncKit", package: "async-kit"), ]), .target(name: "FluentBenchmark", dependencies: [ .target(name: "FluentKit"), diff --git a/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift b/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift index c69ea5f8..a5b299d2 100644 --- a/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift +++ b/Sources/FluentBenchmark/SolarSystem/SolarSystem.swift @@ -43,6 +43,6 @@ public struct SolarSystem: Migration { all = migrations } - return EventLoopFutureQueue(eventLoop: database.eventLoop).append(each: all) { $0.revert(on: database) } + return EventLoopFutureQueue(eventLoop: database.eventLoop).append(each: all.reversed()) { $0.revert(on: database) } } } diff --git a/Sources/FluentBenchmark/Tests/MigratorTests.swift b/Sources/FluentBenchmark/Tests/MigratorTests.swift index 79076751..e3e2c240 100644 --- a/Sources/FluentBenchmark/Tests/MigratorTests.swift +++ b/Sources/FluentBenchmark/Tests/MigratorTests.swift @@ -102,7 +102,7 @@ extension FluentBenchmarker { do { let count = try MigrationLog.query(on: database2).count().wait() - // This is a valid state to enter. Unlike database in the SQL family, + // This is a valid state to enter. Unlike databases in the SQL family, // some databases such as MongoDB won't throw an error if the table doesn't exist. XCTAssertEqual(count, 0) } catch { diff --git a/Sources/FluentKit/Migration/Migrator.swift b/Sources/FluentKit/Migration/Migrator.swift index 8629aecd..0c4d17e4 100644 --- a/Sources/FluentKit/Migration/Migrator.swift +++ b/Sources/FluentKit/Migration/Migrator.swift @@ -144,7 +144,7 @@ private final class DatabaseMigrator { return self.lastBatchNumber().and(value: migrations) }.flatMap { batch, migrations in return EventLoopFutureQueue(eventLoop: self.database.eventLoop).append(each: migrations) { migration in - self.prepare(migration, batch: batch) + self.prepare(migration, batch: batch + 1) } } } From 8de1431ab2cbe82bb57813f8b429b51537557f1e Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Fri, 15 May 2020 02:40:31 -0500 Subject: [PATCH 29/29] Fix typo in Package.swift. Now I have to wonder not only why was this working before without having the dependency at all, but why did it work for me locally in this broken form that definitely fails on Linux? --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 85cd400b..ffffbd48 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), .package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0-rc.1"), - .package(url:" https://github.com/vapor/async-kit.git", from: "1.0.0-rc.1"), + .package(url: "https://github.com/vapor/async-kit.git", from: "1.0.0-rc.1"), ], targets: [ .target(name: "FluentKit", dependencies: [