Skip to content

Commit 0b5ca8b

Browse files
authored
[5.5][Collections] populateTargetTrie changes (#3637)
* [Collections] Disable target trie in some of the tests Motivation: Some of the package collection tests don't require search and finish quickly, so when background thread tries to `populateTargetTrie` it results in signal 4. For [example](#3623 (comment)): ``` 10:23:08 Test Suite 'PackageCollectionsTests' started at 2021-07-20 10:21:53.935 10:23:08 Test Case '-[PackageCollectionsTests.PackageCollectionsTests testCollectionPendingTrustConfirmIsKeptOnAdd]' started. 10:23:08 Test Case '-[PackageCollectionsTests.PackageCollectionsTests testCollectionPendingTrustConfirmIsKeptOnAdd]' passed (0.105 seconds). 10:23:08 Exited with signal code 4 ``` Modifications: Set `initializeTargetTrie` to `false` for these tests such that `populateTargetTrie` is not called. * [Collections] Don't queue populateTargetTrie if there is no data Motivation: `populateTargetTrie`, potentially because it's done in a background queue, causes a lot of random crashes in tests (not just in SwiftPM but other projects as well). The exception happens inside SQLite C library and it's not yet clear what the real cause is. However, since most tests do not use the package collections feature at all, `populateTargetTrie` is basically a no-op and therefore doesn't need to be queued. Modification: Queue `populateTargetTrie` only if there is package collection data. This code change doesn't actually fix anything but may perhaps reduce the chance of crashing. rdar://80840989
1 parent 14ff208 commit 0b5ca8b

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

Sources/PackageCollections/Storage/SQLitePackageCollectionsStorage.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,27 @@ final class SQLitePackageCollectionsStorage: PackageCollectionsStorage, Closable
801801
}
802802

803803
internal func populateTargetTrie(callback: @escaping (Result<Void, Error>) -> Void = { _ in }) {
804+
// Check to see if there is any data before submitting task to queue because otherwise it's no-op anyway
805+
do {
806+
let numberOfCollections: Int = try self.executeStatement("SELECT COUNT(*) FROM \(Self.packageCollectionsTableName);") { statement in
807+
let row = try statement.step()
808+
guard let count = row?.int(at: 0) else {
809+
throw StringError("Failed to get count of \(Self.packageCollectionsTableName) table")
810+
}
811+
return count
812+
}
813+
// No collections means no data, so no need to populate target trie
814+
guard numberOfCollections > 0 else {
815+
self.populateTargetTrieLock.withLock {
816+
self.targetTrieReady = true
817+
}
818+
return callback(.success(()))
819+
}
820+
} catch {
821+
self.diagnosticsEngine?.emit(warning: "Failed to determine if database is empty or not: \(error)")
822+
// Try again in background
823+
}
824+
804825
DispatchQueue.sharedConcurrent.async(group: nil, qos: .background, flags: .assignCurrentContext) {
805826
do {
806827
try self.populateTargetTrieLock.withLock { // Prevent race to populate targetTrie

Tests/PackageCollectionsTests/PackageCollectionsTests.swift

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class PackageCollectionsTests: XCTestCase {
2323
let authTokens = ThreadSafeKeyValueStore<AuthTokenType, String>()
2424
let configuration = PackageCollections.Configuration(authTokens: { authTokens.get() })
2525

26-
// This test doesn't use storage at all and finishes quickly so disable target trie to prevent race
26+
// This test doesn't use search at all and finishes quickly so disable target trie to prevent race
2727
let storageConfig = SQLitePackageCollectionsStorage.Configuration(initializeTargetTrie: false)
2828
let storage = makeMockStorage(storageConfig)
2929
defer { XCTAssertNoThrow(try storage.close()) }
@@ -156,7 +156,9 @@ final class PackageCollectionsTests: XCTestCase {
156156
try skipIfUnsupportedPlatform()
157157

158158
let configuration = PackageCollections.Configuration()
159-
let storage = makeMockStorage()
159+
// This test doesn't use search at all and finishes quickly so disable target trie to prevent race
160+
let storageConfig = SQLitePackageCollectionsStorage.Configuration(initializeTargetTrie: false)
161+
let storage = makeMockStorage(storageConfig)
160162
defer { XCTAssertNoThrow(try storage.close()) }
161163

162164
let mockCollection = makeMockCollections(count: 1).first!
@@ -174,7 +176,8 @@ final class PackageCollectionsTests: XCTestCase {
174176
}
175177

176178
// add fails because collection is not found
177-
guard case .failure = tsc_await({ callback in packageCollections.addCollection(mockCollection.source, order: nil, callback: callback) }) else {
179+
guard case .failure(let error) = tsc_await({ callback in packageCollections.addCollection(mockCollection.source, order: nil, callback: callback) }),
180+
error is NotFoundError else {
178181
return XCTFail("expected error")
179182
}
180183

@@ -191,7 +194,9 @@ final class PackageCollectionsTests: XCTestCase {
191194
try skipIfUnsupportedPlatform()
192195

193196
let configuration = PackageCollections.Configuration()
194-
let storage = makeMockStorage()
197+
// This test doesn't use search at all and finishes quickly so disable target trie to prevent race
198+
let storageConfig = SQLitePackageCollectionsStorage.Configuration(initializeTargetTrie: false)
199+
let storage = makeMockStorage(storageConfig)
195200
defer { XCTAssertNoThrow(try storage.close()) }
196201

197202
let mockCollection = makeMockCollections(count: 1, signed: false).first!
@@ -208,7 +213,9 @@ final class PackageCollectionsTests: XCTestCase {
208213
XCTAssertEqual(sources.count, 0, "sources should be empty")
209214
}
210215

211-
guard case .failure = tsc_await({ callback in packageCollections.addCollection(mockCollection.source, order: nil, callback: callback) }) else {
216+
// add fails because collection requires trust confirmation
217+
guard case .failure(let error) = tsc_await({ callback in packageCollections.addCollection(mockCollection.source, order: nil, callback: callback) }),
218+
case PackageCollectionError.trustConfirmationRequired = error else {
212219
return XCTFail("expected error")
213220
}
214221

@@ -225,7 +232,9 @@ final class PackageCollectionsTests: XCTestCase {
225232
try skipIfUnsupportedPlatform()
226233

227234
let configuration = PackageCollections.Configuration()
228-
let storage = makeMockStorage()
235+
// This test doesn't use search at all and finishes quickly so disable target trie to prevent race
236+
let storageConfig = SQLitePackageCollectionsStorage.Configuration(initializeTargetTrie: false)
237+
let storage = makeMockStorage(storageConfig)
229238
defer { XCTAssertNoThrow(try storage.close()) }
230239

231240
let mockCollection = makeMockCollections(count: 1).first!
@@ -244,7 +253,7 @@ final class PackageCollectionsTests: XCTestCase {
244253

245254
// add fails because collection's signature is invalid
246255
guard case .failure(let error) = tsc_await({ callback in packageCollections.addCollection(mockCollection.source, order: nil, callback: callback) }),
247-
PackageCollectionError.invalidSignature == error as? PackageCollectionError else {
256+
case PackageCollectionError.invalidSignature = error else {
248257
return XCTFail("expected PackageCollectionError.invalidSignature")
249258
}
250259

0 commit comments

Comments
 (0)