Skip to content

Commit dfcbeba

Browse files
authored
Concurrency updates and database logging APIs (#775)
* Bump dependency versions and clean up language flags * Make it easier to override the logger on a database when requesting it (since `.logging(to:)` doesn't usually work as intended) * Use the `willBootAsync` lifecycle callback to avoid calling `.wait()` when auto-migration is requested on the commandline. * Code style, improve the console output in the migration command * Make all the tests fully async and update them to use Application.make() etc. * Remove pointless Dependabot config
1 parent d831ac5 commit dfcbeba

12 files changed

+270
-288
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
version: 2
2-
enable-beta-ecosystems: true
32
updates:
43
- package-ecosystem: "github-actions"
54
directory: "/"
@@ -11,14 +10,3 @@ updates:
1110
dependencies:
1211
patterns:
1312
- "*"
14-
- package-ecosystem: "swift"
15-
directory: "/"
16-
schedule:
17-
interval: "daily"
18-
open-pull-requests-limit: 6
19-
allow:
20-
- dependency-type: all
21-
groups:
22-
all-dependencies:
23-
patterns:
24-
- "*"

Package.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ let package = Package(
1313
.library(name: "Fluent", targets: ["Fluent"]),
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.48.0"),
17-
.package(url: "https://github.com/vapor/vapor.git", from: "4.94.1"),
16+
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.48.4"),
17+
.package(url: "https://github.com/vapor/vapor.git", from: "4.101.0"),
1818
],
1919
targets: [
2020
.target(
@@ -40,10 +40,4 @@ let package = Package(
4040
var swiftSettings: [SwiftSetting] { [
4141
.enableUpcomingFeature("ConciseMagicFile"),
4242
.enableUpcomingFeature("ForwardTrailingClosures"),
43-
.enableUpcomingFeature("ImportObjcForwardDeclarations"),
44-
.enableUpcomingFeature("DisableOutwardActorInference"),
45-
.enableUpcomingFeature("IsolatedDefaultValues"),
46-
.enableUpcomingFeature("GlobalConcurrency"),
47-
.enableUpcomingFeature("StrictConcurrency"),
48-
.enableExperimentalFeature("StrictConcurrency=complete"),
4943
] }

[email protected]

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ let package = Package(
1313
.library(name: "Fluent", targets: ["Fluent"]),
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.48.0"),
17-
.package(url: "https://github.com/vapor/vapor.git", from: "4.94.1"),
16+
.package(url: "https://github.com/vapor/fluent-kit.git", from: "1.48.4"),
17+
.package(url: "https://github.com/vapor/vapor.git", from: "4.101.0"),
1818
],
1919
targets: [
2020
.target(
@@ -43,8 +43,5 @@ var swiftSettings: [SwiftSetting] { [
4343
.enableUpcomingFeature("ForwardTrailingClosures"),
4444
.enableUpcomingFeature("ImportObjcForwardDeclarations"),
4545
.enableUpcomingFeature("DisableOutwardActorInference"),
46-
.enableUpcomingFeature("IsolatedDefaultValues"),
47-
.enableUpcomingFeature("GlobalConcurrency"),
48-
.enableUpcomingFeature("StrictConcurrency"),
4946
.enableExperimentalFeature("StrictConcurrency=complete"),
5047
] }

Sources/Fluent/FluentProvider.swift

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ extension Request {
1212
}
1313

1414
public func db(_ id: DatabaseID?) -> any Database {
15-
self.application
16-
.databases
17-
.database(
18-
id,
19-
logger: self.logger,
20-
on: self.eventLoop,
21-
history: self.fluent.history.historyEnabled ? self.fluent.history.history : nil,
22-
pageSizeLimit: self.fluent.pagination.pageSizeLimit != nil ? self.fluent.pagination.pageSizeLimit?.value : self.application.fluent.pagination.pageSizeLimit
23-
)!
15+
self.db(id, logger: self.logger)
16+
}
17+
18+
public func db(_ id: DatabaseID?, logger: Logger) -> any Database {
19+
self.application.databases.database(
20+
id,
21+
logger: logger,
22+
on: self.eventLoop,
23+
history: self.fluent.history.historyEnabled ? self.fluent.history.history : nil,
24+
// Use map() (not flatMap()) so if pageSizeLimit is non-nil but the value is nil
25+
// the request's "no limit" setting overrides the app's setting.
26+
pageSizeLimit: self.fluent.pagination.pageSizeLimit.map(\.value) ??
27+
self.application.fluent.pagination.pageSizeLimit
28+
)!
2429
}
2530

2631
public var fluent: Fluent {
@@ -34,14 +39,17 @@ extension Application {
3439
}
3540

3641
public func db(_ id: DatabaseID?) -> any Database {
37-
self.databases
38-
.database(
39-
id,
40-
logger: self.logger,
41-
on: self.eventLoopGroup.any(),
42-
history: self.fluent.history.historyEnabled ? self.fluent.history.history : nil,
43-
pageSizeLimit: self.fluent.pagination.pageSizeLimit
44-
)!
42+
self.db(id, logger: self.logger)
43+
}
44+
45+
public func db(_ id: DatabaseID?, logger: Logger) -> any Database {
46+
self.databases.database(
47+
id,
48+
logger: logger,
49+
on: self.eventLoopGroup.any(),
50+
history: self.fluent.history.historyEnabled ? self.fluent.history.history : nil,
51+
pageSizeLimit: self.fluent.pagination.pageSizeLimit
52+
)!
4553
}
4654

4755
public var databases: Databases {
@@ -53,7 +61,7 @@ extension Application {
5361
}
5462

5563
public var migrator: Migrator {
56-
Migrator(
64+
.init(
5765
databases: self.databases,
5866
migrations: self.migrations,
5967
logger: self.logger,
@@ -85,10 +93,7 @@ extension Application {
8593
let migrationLogLevel: NIOLockedValueBox<Logger.Level>
8694

8795
init(threadPool: NIOThreadPool, on eventLoopGroup: any EventLoopGroup, migrationLogLevel: Logger.Level) {
88-
self.databases = Databases(
89-
threadPool: threadPool,
90-
on: eventLoopGroup
91-
)
96+
self.databases = Databases(threadPool: threadPool, on: eventLoopGroup)
9297
self.migrations = .init()
9398
self.migrationLogLevel = .init(migrationLogLevel)
9499
}
@@ -99,25 +104,35 @@ extension Application {
99104
}
100105

101106
struct Lifecycle: LifecycleHandler {
102-
func willBoot(_ application: Application) throws {
103-
struct Signature: CommandSignature {
104-
@Flag(name: "auto-migrate", help: "If true, Fluent will automatically migrate your database on boot")
105-
var autoMigrate: Bool
106-
107-
@Flag(name: "auto-revert", help: "If true, Fluent will automatically revert your database on boot")
108-
var autoRevert: Bool
107+
struct Signature: CommandSignature {
108+
@Flag(name: "auto-migrate", help: "If true, Fluent will automatically migrate your database on boot")
109+
var autoMigrate: Bool
109110

110-
init() {}
111-
}
111+
@Flag(name: "auto-revert", help: "If true, Fluent will automatically revert your database on boot")
112+
var autoRevert: Bool
113+
}
112114

115+
func willBoot(_ application: Application) throws {
113116
let signature = try Signature(from: &application.environment.commandInput)
117+
114118
if signature.autoRevert {
115119
try application.autoRevert().wait()
116120
}
117121
if signature.autoMigrate {
118122
try application.autoMigrate().wait()
119123
}
120124
}
125+
126+
func willBootAsync(_ application: Application) async throws {
127+
let signature = try Signature(from: &application.environment.commandInput)
128+
129+
if signature.autoRevert {
130+
try await application.autoRevert()
131+
}
132+
if signature.autoMigrate {
133+
try await application.autoMigrate()
134+
}
135+
}
121136

122137
func shutdown(_ application: Application) {
123138
application.databases.shutdown()
@@ -148,21 +163,11 @@ extension Application {
148163
nonmutating set { self.storage.migrationLogLevel.withLockedValue { $0 = newValue } }
149164
}
150165

151-
public var history: History {
152-
.init(fluent: self)
153-
}
154-
155-
public struct History {
156-
let fluent: Fluent
157-
}
166+
public struct History { let fluent: Fluent }
167+
public var history: History { .init(fluent: self) }
158168

159-
public var pagination: Pagination {
160-
.init(fluent: self)
161-
}
162-
163-
public struct Pagination {
164-
let fluent: Fluent
165-
}
169+
public struct Pagination { let fluent: Fluent }
170+
public var pagination: Pagination { .init(fluent: self) }
166171
}
167172

168173
public var fluent: Fluent {

Sources/Fluent/MigrateCommand.swift

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,12 @@ public final class MigrateCommand: AsyncCommand {
3030

3131
private func revert(using context: CommandContext) async throws {
3232
let migrations = try await context.application.migrator.previewRevertLastBatch().get()
33-
guard migrations.count > 0 else {
34-
context.console.print("No migrations to revert.")
35-
return
33+
guard !migrations.isEmpty else {
34+
return context.console.print("No migrations to revert.")
3635
}
3736
context.console.print("The following migration(s) will be reverted:")
3837
for (migration, dbid) in migrations {
39-
context.console.print("- ", newLine: false)
40-
context.console.error(migration.name, newLine: false)
41-
context.console.print(" on ", newLine: false)
42-
context.console.print(dbid?.string ?? "default")
38+
context.console.output("- \(migration.name, color: .red) on \(dbid?.string ?? "<default>", style: .info)")
4339
}
4440
if context.console.confirm("Would you like to continue?".consoleText(.warning)) {
4541
try await context.application.migrator.revertLastBatch().get()
@@ -51,16 +47,12 @@ public final class MigrateCommand: AsyncCommand {
5147

5248
private func prepare(using context: CommandContext) async throws {
5349
let migrations = try await context.application.migrator.previewPrepareBatch().get()
54-
guard migrations.count > 0 else {
55-
context.console.print("No new migrations.")
56-
return
50+
guard !migrations.isEmpty else {
51+
return context.console.print("No new migrations.")
5752
}
5853
context.console.print("The following migration(s) will be prepared:")
5954
for (migration, dbid) in migrations {
60-
context.console.print("+ ", newLine: false)
61-
context.console.success(migration.name, newLine: false)
62-
context.console.print(" on ", newLine: false)
63-
context.console.print(dbid?.string ?? "default")
55+
context.console.output("+ \(migration.name, color: .green) on \(dbid?.string ?? "<default>", style: .info)")
6456
}
6557
if context.console.confirm("Would you like to continue?".consoleText(.warning)) {
6658
try await context.application.migrator.prepareBatch().get()

Tests/FluentTests/CacheTests.swift

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,34 @@ import Fluent
44
import Vapor
55

66
final class CacheTests: XCTestCase {
7+
var app: Application!
8+
9+
override func setUp() async throws {
10+
self.app = try await Application.make(.testing)
11+
}
12+
13+
override func tearDown() async throws {
14+
try await self.app.asyncShutdown()
15+
self.app = nil
16+
}
17+
718
func testCacheMigrationName() {
819
XCTAssertEqual(CacheEntry.migration.name, "Fluent.CacheEntry.Create")
920
}
1021

1122
func testCacheGet() async throws {
12-
let app = Application(.testing)
13-
defer { app.shutdown() }
14-
1523
// Setup test db.
1624
let test = ArrayTestDatabase()
17-
app.databases.use(test.configuration, as: .test)
18-
app.migrations.add(CacheEntry.migration)
25+
self.app.databases.use(test.configuration, as: .test)
26+
self.app.migrations.add(CacheEntry.migration)
1927

2028
// Configure cache.
21-
app.caches.use(.fluent)
29+
self.app.caches.use(.fluent)
2230

2331
// simulate cache miss
2432
test.append([])
2533
do {
26-
let foo = try await app.cache.get("foo", as: String.self)
34+
let foo = try await self.app.cache.get("foo", as: String.self)
2735
XCTAssertNil(foo)
2836
}
2937

@@ -33,15 +41,12 @@ final class CacheTests: XCTestCase {
3341
"value": "\"bar\""
3442
])])
3543
do {
36-
let foo = try await app.cache.get("foo", as: String.self)
44+
let foo = try await self.app.cache.get("foo", as: String.self)
3745
XCTAssertEqual(foo, "bar")
3846
}
3947
}
4048

4149
func testCacheSet() async throws {
42-
let app = Application(.testing)
43-
defer { app.shutdown() }
44-
4550
// Setup test db.
4651
let test = CallbackTestDatabase { query in
4752
switch query.input[0] {
@@ -51,19 +56,16 @@ final class CacheTests: XCTestCase {
5156
XCTAssertEqual(value, "\"bar\"")
5257
default: XCTFail("unexpected value")
5358
}
54-
5559
default: XCTFail("unexpected input")
5660
}
57-
return [
58-
TestOutput(["id": UUID()])
59-
]
61+
return [TestOutput(["id": UUID()])]
6062
}
61-
app.databases.use(test.configuration, as: .test)
62-
app.migrations.add(CacheEntry.migration)
63+
self.app.databases.use(test.configuration, as: .test)
64+
self.app.migrations.add(CacheEntry.migration)
6365

6466
// Configure cache.
65-
app.caches.use(.fluent)
67+
self.app.caches.use(.fluent)
6668

67-
try await app.cache.set("foo", to: "bar")
69+
try await self.app.cache.set("foo", to: "bar")
6870
}
6971
}

0 commit comments

Comments
 (0)