Skip to content

Commit

Permalink
Track Migrations Per Database (#271)
Browse files Browse the repository at this point in the history
* Pass ID of database to migrate into Migrator

* Migrate each registered DatabaseID unless a specific database is specified

* Removed Migrator.run(on:_:) method that passes Database instance into 'query' closure

* Removed MigrationError enum

* Skip subsequent queries if a previous error has occured in Migrator preview methods

* Pass in default database ID (nil) to Migrator.run 'query' closure argument

* Changed Migrations.databases type from an Array to a Set

* Use .whenAllSucceed instead of .andAllSync to complete Migrator.run 'query' closure results

* Defined MigratorTests test cases

* Use 'guard' statements to check failed errors in Migrator preview methods

* Use .recover instead of .flatMapErrorThrowing to capture failures in Migrator preview methods

* Use !contains instead of count check in Migrator.unpreparedMigrations(on:) method

* Cleanup newlines in Migrator.run(on:_:) method

* Give database configurations access to database ID in DatabaseConfigurationFactory closure

* Run MigrationLog queries on Migration.Item database

* Require DatabaseIDs for FluentBenchmarker to access separate databases

* Created FluentBenchmarker.testMigrator_sequence test case

* Removed FluentKit MigratorTests

* Assert database2 log count is 0 if no errors are throw from query

* Fixed typo in MigrationLog database2 query catch block comment

* Removed DatabaseID injection from DatabaseConfigurationFactory

* Change Migrations.databases to a computed property

* Added .ids() method to Databases type

* Get database IDs for FluentBenchmark.testMigrator_sequence from Databases.ids() method

* Allow nil in Migrations.databases set

* Use underlying DatabaseMigrator type to run migrations in Migrator

* Removed stray use of .andAllSync from QueryBuilder

* 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).

* 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?

Co-authored-by: Gwynne Raskind <gwynne@darkrainfall.org>
  • Loading branch information
calebkleveter and gwynne authored May 18, 2020
1 parent c73570c commit 27288c2
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 127 deletions.
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
1 change: 1 addition & 0 deletions Sources/FluentBenchmark/FluentBenchmarker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class FluentBenchmarker {
}

public init(databases: Databases) {
precondition(databases.ids().count >= 2, "FluentBenchmarker Databases instance must have 2 or more registered databases")
self.databases = databases
}

Expand Down
12 changes: 6 additions & 6 deletions Sources/FluentBenchmark/SolarSystem/SolarSystem.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import AsyncKit

private let migrations: [Migration] = [
GalaxyMigration(),
StarMigration(),
Expand Down Expand Up @@ -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<Void> {
Expand All @@ -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.reversed()) { $0.revert(on: database) }
}
}
78 changes: 78 additions & 0 deletions Sources/FluentBenchmark/Tests/MigratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -53,6 +54,83 @@ extension FluentBenchmarker {
try migrator.revertAllBatches().wait()
}
}

private func testMigrator_sequence() throws {
try self.runTest(#function, []) {

// Setup
let ids = Array(self.databases.ids())
let databaseID = (ids[0], ids[1])

let database1 = try XCTUnwrap(
self.databases.database(
databaseID.0,
logger: Logger(label: "codes.vapor.tests"),
on: self.databases.eventLoopGroup.next()
)
)
let database2 = try XCTUnwrap(
self.databases.database(
databaseID.1,
logger: Logger(label: "codes.vapor.tests"),
on: self.databases.eventLoopGroup.next()
)
)

let migrations = Migrations()


// Migration #1
migrations.add(GalaxyMigration(), to: databaseID.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)")

do {
let count = try MigrationLog.query(on: database2).count().wait()

// 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 {
// 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.
}


// Migration #2
migrations.add(GalaxyMigration(), to: databaseID.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 {
Expand Down
4 changes: 4 additions & 0 deletions Sources/FluentKit/Database/Databases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ public final class Databases {
}
}

public func ids() -> Set<DatabaseID> {
return self.lock.withLock { Set(self.configurations.keys) }
}

public func shutdown() {
self.lock.lock()
defer { self.lock.unlock() }
Expand Down
1 change: 1 addition & 0 deletions Sources/FluentKit/Migration/Migrations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public final class Migrations {
}

var storage: [Item]
var databases: Set<DatabaseID?> { Set(self.storage.map(\.id)) }

public init() {
self.storage = []
Expand Down
Loading

0 comments on commit 27288c2

Please sign in to comment.