Skip to content

Commit 36e2b31

Browse files
committed
Add timeout to queries
1 parent 774e14b commit 36e2b31

File tree

1 file changed

+58
-12
lines changed

1 file changed

+58
-12
lines changed

Tests/AppTests/Helpers/DatabasePool.swift

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,17 @@ actor DatabasePool {
5555
print("ℹ️ availableDatabases", availableDatabases.count)
5656
for db in availableDatabases {
5757
print("ℹ️ setting up db \(db.port)")
58-
// try await db.setup(for: .testing)
58+
try await db.setup(for: .testing)
5959
print("ℹ️ DONE setting up db \(db.port)")
6060
}
6161
}
6262

6363
func tearDown() async throws {
64-
try await tearDown(databases: runningDatabases())
64+
if isRunningInCI() {
65+
// Let the CI system deal with the databases, there's nothing we can or should do here.
66+
} else {
67+
try await tearDown(databases: runningDatabases())
68+
}
6569
}
6670

6771
func tearDown(databases: any Collection<Database>) async throws {
@@ -182,8 +186,11 @@ extension DatabasePool.Database {
182186
}
183187

184188
func createSchema(_ environment: Environment, details: ConnectionDetails) async throws {
189+
let start = Date()
190+
print("ℹ️ \(#function) start")
191+
defer { print("ℹ️ \(#function) end", Date().timeIntervalSince(start)) }
185192
do {
186-
try await _withDatabase("postgres", details: details) { // Connect to `postgres` db in order to reset the test db
193+
try await _withDatabase("postgres", details: details, timeout: .seconds(1)) { // Connect to `postgres` db in order to reset the test db
187194
let databaseName = Environment.get("DATABASE_NAME")!
188195
try await $0.query(PostgresQuery(unsafeSQL: "DROP DATABASE IF EXISTS \(databaseName) WITH (FORCE)"))
189196
try await $0.query(PostgresQuery(unsafeSQL: "CREATE DATABASE \(databaseName)"))
@@ -203,10 +210,13 @@ extension DatabasePool.Database {
203210
}
204211

205212
func createSnapshot(details: ConnectionDetails) async throws {
213+
let start = Date()
214+
print("ℹ️ \(#function) start")
215+
defer { print("ℹ️ \(#function) end", Date().timeIntervalSince(start)) }
206216
let original = Environment.get("DATABASE_NAME")!
207217
let snapshot = original + "_snapshot"
208218
do {
209-
try await _withDatabase("postgres", details: details) { client in
219+
try await _withDatabase("postgres", details: details, timeout: .seconds(1)) { client in
210220
try await client.query(PostgresQuery(unsafeSQL: "DROP DATABASE IF EXISTS \(snapshot) WITH (FORCE)"))
211221
try await client.query(PostgresQuery(unsafeSQL: "CREATE DATABASE \(snapshot) TEMPLATE \(original)"))
212222
}
@@ -221,7 +231,7 @@ extension DatabasePool.Database {
221231
let snapshot = original + "_snapshot"
222232
// delete db and re-create from snapshot
223233
do {
224-
try await _withDatabase("postgres", details: details) { client in
234+
try await _withDatabase("postgres", details: details, timeout: .seconds(1)) { client in
225235
try await client.query(PostgresQuery(unsafeSQL: "DROP DATABASE IF EXISTS \(original) WITH (FORCE)"))
226236
try await client.query(PostgresQuery(unsafeSQL: "CREATE DATABASE \(original) TEMPLATE \(snapshot)"))
227237
}
@@ -250,14 +260,18 @@ private func connect(to databaseName: String, details: DatabasePool.Database.Con
250260

251261
private func _withDatabase(_ databaseName: String,
252262
details: DatabasePool.Database.ConnectionDetails,
253-
_ query: @escaping (PostgresClient) async throws -> Void) async throws {
263+
timeout: Duration,
264+
_ query: @Sendable @escaping (PostgresClient) async throws -> Void) async throws {
254265
let client = connect(to: databaseName, details: details)
255-
try await withThrowingTaskGroup { taskGroup in
256-
taskGroup.addTask { await client.run() }
257-
258-
try await query(client)
259-
260-
taskGroup.cancelAll()
266+
try await run(timeout: timeout) {
267+
try await withThrowingTaskGroup { taskGroup in
268+
taskGroup.addTask { await client.run() }
269+
taskGroup.addTask {
270+
try await query(client)
271+
}
272+
try await taskGroup.next()
273+
taskGroup.cancelAll()
274+
}
261275
}
262276
}
263277

@@ -275,3 +289,35 @@ extension Environment {
275289

276290
#warning("remove later")
277291
extension String: Swift.Error { }
292+
293+
294+
private enum TimeoutError: Error {
295+
case timeout
296+
case noResult
297+
}
298+
299+
300+
private func run(timeout: Duration, operation: @escaping @Sendable () async throws -> Void) async throws {
301+
try await withThrowingTaskGroup(of: Bool.self) { group in
302+
group.addTask {
303+
try? await Task.sleep(for: timeout)
304+
return false
305+
}
306+
group.addTask {
307+
try await operation()
308+
return true
309+
}
310+
let res = await group.nextResult()
311+
group.cancelAll()
312+
switch res {
313+
case .success(false):
314+
throw TimeoutError.timeout
315+
case .success(true):
316+
break
317+
case .failure(let error):
318+
throw error
319+
case .none:
320+
throw TimeoutError.noResult
321+
}
322+
}
323+
}

0 commit comments

Comments
 (0)