Skip to content

Commit 8ff43cb

Browse files
committed
cleanup identity cache mapping impl; assure transformation mode is .swizzle
1 parent 1e5474d commit 8ff43cb

9 files changed

Lines changed: 138 additions & 96 deletions

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ public protocol ManifestLoaderProtocol {
126126
fileSystem: FileSystem,
127127
observabilityScope: ObservabilityScope,
128128
delegateQueue: DispatchQueue,
129-
identityLookupCache: ThreadSafeKeyValueStore<
130-
SourceControlURL,
131-
(result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
132-
>
133129
) async throws -> Manifest
134130

135131
/// Reset any internal cache held by the manifest loader.
@@ -203,10 +199,6 @@ extension ManifestLoaderProtocol {
203199
fileSystem: FileSystem,
204200
observabilityScope: ObservabilityScope,
205201
delegateQueue: DispatchQueue,
206-
identityLookupCache: ThreadSafeKeyValueStore<
207-
SourceControlURL,
208-
(result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
209-
>
210202
) async throws -> Manifest {
211203
// find the manifest path and parse it's tools-version
212204
let manifestPath = try ManifestLoader.findManifest(
@@ -233,8 +225,7 @@ extension ManifestLoaderProtocol {
233225
dependencyMapper: dependencyMapper,
234226
fileSystem: fileSystem,
235227
observabilityScope: observabilityScope,
236-
delegateQueue: delegateQueue,
237-
identityLookupCache: identityLookupCache
228+
delegateQueue: delegateQueue
238229
)
239230
}
240231
}
@@ -320,10 +311,6 @@ public final class ManifestLoader: ManifestLoaderProtocol {
320311
fileSystem: FileSystem,
321312
observabilityScope: ObservabilityScope,
322313
delegateQueue: DispatchQueue,
323-
identityLookupCache: ThreadSafeKeyValueStore<
324-
SourceControlURL,
325-
(result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
326-
>
327314
) async throws -> Manifest {
328315
// Inform the delegate.
329316
let start = DispatchTime.now()

Sources/Workspace/PackageContainer/FileSystemPackageContainer.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ public struct FileSystemPackageContainer: PackageContainer {
8989
dependencyMapper: self.dependencyMapper,
9090
fileSystem: self.fileSystem,
9191
observabilityScope: self.observabilityScope,
92-
delegateQueue: .sharedConcurrent,
93-
identityLookupCache: .init()
92+
delegateQueue: .sharedConcurrent
9493
)
9594
}
9695
}

Sources/Workspace/PackageContainer/RegistryPackageContainer.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class RegistryPackageContainer: PackageContainer {
3030
private let manifestLoader: ManifestLoaderProtocol
3131
private let currentToolsVersion: ToolsVersion
3232
private let observabilityScope: ObservabilityScope
33-
private let identityLookupCache: Workspace.SCMToRegistryMap
33+
private let identityLookupCache: Workspace.IdentityLookupCache
3434

3535
private var knownVersionsCache = AsyncThrowingValueMemoizer<[Version]>()
3636
private var toolsVersionsCache = ThrowingAsyncKeyValueMemoizer<Version, ToolsVersion>()
@@ -46,7 +46,7 @@ public class RegistryPackageContainer: PackageContainer {
4646
manifestLoader: ManifestLoaderProtocol,
4747
currentToolsVersion: ToolsVersion,
4848
observabilityScope: ObservabilityScope,
49-
identityLookupCache: Workspace.SCMToRegistryMap
49+
identityLookupCache: Workspace.IdentityLookupCache
5050
) {
5151
self.package = package
5252
self.identityResolver = identityResolver
@@ -152,7 +152,6 @@ public class RegistryPackageContainer: PackageContainer {
152152
fileSystem: result.fileSystem,
153153
observabilityScope: self.observabilityScope,
154154
delegateQueue: .sharedConcurrent,
155-
identityLookupCache: self.identityLookupCache
156155
)
157156
}
158157

Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri
7272
private var knownVersionsCache = ThreadSafeBox<[Version: String]?>()
7373
private var manifestsCache = ThrowingAsyncKeyValueMemoizer<String, Manifest>()
7474
private var toolsVersionsCache = ThreadSafeKeyValueStore<Version, ToolsVersion>()
75-
private var identityLookupCache: Workspace.SCMToRegistryMap
75+
private var identityLookupCache: Workspace.IdentityLookupCache
7676

7777
/// This is used to remember if tools version of a particular version is
7878
/// valid or not.
@@ -89,7 +89,7 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri
8989
fingerprintStorage: PackageFingerprintStorage?,
9090
fingerprintCheckingMode: FingerprintCheckingMode,
9191
observabilityScope: ObservabilityScope,
92-
identityLookupCache: Workspace.SCMToRegistryMap
92+
identityLookupCache: Workspace.IdentityLookupCache
9393
) throws {
9494
self.package = package
9595
self.identityResolver = identityResolver
@@ -412,7 +412,6 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri
412412
fileSystem: fileSystem,
413413
observabilityScope: self.observabilityScope,
414414
delegateQueue: .sharedConcurrent,
415-
identityLookupCache: self.identityLookupCache
416415
)
417416
}
418417

Sources/Workspace/Workspace+Dependencies.swift

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -373,28 +373,41 @@ extension Workspace {
373373
)
374374
}
375375

376+
// Populate the identity lookup cache via the Package.resolved.
377+
// This will only ever be done if the user has passed in `--force-resolved-versions`
378+
self.identityLookupCache.deriveCache(
379+
from: resolvedPackagesStore.resolvedPackages,
380+
self.configuration.sourceControlToRegistryDependencyTransformation
381+
)
376382
// Create the identity mapping between the registry and scm, if applicable.
377-
for resolvedPackage in resolvedPackagesStore.resolvedPackages.values {
378-
if case let .registry(id, .some(url)) = resolvedPackage.packageRef.kind {
379-
self.identityLookupCache[url] = (
380-
result: .success(id),
381-
expirationTime: .now().advanced(by: .seconds(300))
382-
)
383-
}
384-
}
383+
// for resolvedPackage in resolvedPackagesStore.resolvedPackages.values {
384+
// if case let .registry(id, .some(url)) = resolvedPackage.packageRef.kind {
385+
// self.identityLookupCache[storing: url] = .success(id)
386+
// }
387+
// }
385388

386-
// Track SCM packages that don't have a mapped registry equivalent;
387-
// this will avoid having to make any unnecessary calls to registry
388-
// endpoints.
389-
for resolvedPackage in resolvedPackagesStore.resolvedPackages.values {
390-
if case .remoteSourceControl(let url) = resolvedPackage.packageRef.kind,
391-
self.identityLookupCache[url] == nil {
392-
self.identityLookupCache[url] = (
393-
result: .success(nil),
394-
expirationTime: .now().advanced(by: .seconds(300))
395-
)
396-
}
397-
}
389+
// let transformationMode = self.configuration.sourceControlToRegistryDependencyTransformation
390+
391+
// Track SCM packages that don't have a mapped registry equivalent,
392+
// only if the transformation mode is `.swizzle`; this will avoid
393+
// having to make any unnecessary calls to registry endpoints.
394+
//
395+
// Additionally for cases where `--use-registry-identity-for-scm` is used,
396+
// the package kind remains that of a remote source control while using a
397+
// registry identity to de-duplicate references to this package elsewhere in
398+
// the package graph. Here, we do not want to keep track of any mapping since
399+
// it still may have a registry equivalent.
400+
// if transformationMode == .swizzle {
401+
// for resolvedPackage in resolvedPackagesStore.resolvedPackages.values {
402+
// if case .remoteSourceControl(let url) = resolvedPackage.packageRef.kind,
403+
// self.identityLookupCache[url] == nil {
404+
// self.identityLookupCache[url] = (
405+
// result: .success(nil),
406+
// expirationTime: .now().advanced(by: .seconds(300))
407+
// )
408+
// }
409+
// }
410+
// }
398411

399412
// Request all the containers to fetch them in parallel.
400413
//

Sources/Workspace/Workspace+Manifests.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,8 +859,7 @@ extension Workspace {
859859
dependencyMapper: self.dependencyMapper,
860860
fileSystem: fileSystem,
861861
observabilityScope: manifestLoadingScope,
862-
delegateQueue: .sharedConcurrent,
863-
identityLookupCache: self.identityLookupCache
862+
delegateQueue: .sharedConcurrent
864863
)
865864
} catch {
866865
let duration = start.distance(to: .now())

Sources/Workspace/Workspace+Registry.swift

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,18 @@ extension Workspace {
5656
private let underlying: ManifestLoaderProtocol
5757
private let registryClient: RegistryClient
5858
private let transformationMode: TransformationMode
59-
60-
private let cacheTTL = DispatchTimeInterval.seconds(300) // 5m
61-
// private let identityLookupCache = ThreadSafeKeyValueStore<
62-
// SourceControlURL,
63-
// (result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
64-
// >()
59+
private let identityLookupCache: Workspace.IdentityLookupCache
6560

6661
init(
6762
underlying: ManifestLoaderProtocol,
6863
registryClient: RegistryClient,
69-
transformationMode: TransformationMode
64+
transformationMode: TransformationMode,
65+
identityLookupCache: Workspace.IdentityLookupCache
7066
) {
7167
self.underlying = underlying
7268
self.registryClient = registryClient
7369
self.transformationMode = transformationMode
70+
self.identityLookupCache = identityLookupCache
7471
}
7572

7673
func load(
@@ -85,10 +82,6 @@ extension Workspace {
8582
fileSystem: any FileSystem,
8683
observabilityScope: ObservabilityScope,
8784
delegateQueue: DispatchQueue,
88-
identityLookupCache: ThreadSafeKeyValueStore<
89-
SourceControlURL,
90-
(result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
91-
>
9285
) async throws -> Manifest {
9386
let manifest = try await self.underlying.load(
9487
manifestPath: manifestPath,
@@ -101,13 +94,11 @@ extension Workspace {
10194
dependencyMapper: dependencyMapper,
10295
fileSystem: fileSystem,
10396
observabilityScope: observabilityScope,
104-
delegateQueue: delegateQueue,
105-
identityLookupCache: .init()
97+
delegateQueue: delegateQueue
10698
)
10799
return try await self.transformSourceControlDependenciesToRegistry(
108100
manifest: manifest,
109101
transformationMode: transformationMode,
110-
identityLookupCache: identityLookupCache,
111102
observabilityScope: observabilityScope
112103
)
113104
}
@@ -123,10 +114,6 @@ extension Workspace {
123114
private func transformSourceControlDependenciesToRegistry(
124115
manifest: Manifest,
125116
transformationMode: TransformationMode,
126-
identityLookupCache: ThreadSafeKeyValueStore<
127-
SourceControlURL,
128-
(result: Result<PackageIdentity?, Error>, expirationTime: DispatchTime)
129-
>,
130117
observabilityScope: ObservabilityScope
131118
) async throws -> Manifest {
132119
var transformations = [PackageDependency: PackageIdentity]()
@@ -138,7 +125,6 @@ extension Workspace {
138125
do {
139126
let identity = try await self.mapRegistryIdentity(
140127
url: url,
141-
identityLookupCache: identityLookupCache,
142128
observabilityScope: observabilityScope
143129
)
144130
return (dependency, identity)
@@ -343,37 +329,31 @@ extension Workspace {
343329

344330
private func mapRegistryIdentity(
345331
url: SourceControlURL,
346-
identityLookupCache: ThreadSafeKeyValueStore<
347-
SourceControlURL,
348-
(result:
349-
Result<PackageIdentity?, Error>,
350-
expirationTime: DispatchTime
351-
)>,
352332
observabilityScope: ObservabilityScope
353333
) async throws -> PackageIdentity? {
354334
if let cached = identityLookupCache[url], cached.expirationTime > .now() {
355335
switch cached.result {
356336
case .success(let identity):
357-
return identity;
337+
return identity
338+
case .notApplicable:
339+
// scm package does not have a valid registry mapping
340+
return nil
358341
case .failure:
359342
// server error, do not try again
360343
return nil
361344
}
362345
}
363346

364347
do {
365-
// TODO bp :
366-
// - possibly propogate the resolvedFileStrategy here?
367-
// - ensure that the cache is up to date
368348
let identities = try await self.registryClient.lookupIdentities(
369349
scmURL: url,
370350
observabilityScope: observabilityScope
371351
)
372352
let identity = identities.sorted().first
373-
identityLookupCache[url] = (result: .success(identity), expirationTime: .now() + self.cacheTTL)
353+
identityLookupCache[storing: url] = .success(identity)
374354
return identity
375355
} catch {
376-
identityLookupCache[url] = (result: .failure(error), expirationTime: .now() + self.cacheTTL)
356+
identityLookupCache[storing: url] = .failure(error)
377357
throw error
378358
}
379359
}
@@ -469,3 +449,71 @@ extension Workspace {
469449
try registryDownloadsManager.remove(package: dependency.packageRef.identity)
470450
}
471451
}
452+
453+
extension Workspace {
454+
public class IdentityLookupCache {
455+
public typealias Key = SourceControlURL
456+
public typealias Value = CacheResult
457+
public typealias CacheResult = (result: IdentityMapResult<PackageIdentity?, Error>, expirationTime: DispatchTime)
458+
459+
460+
public enum IdentityMapResult<Success, Failure> {
461+
/// Represents a successful mapping of a source control URL to its registry identity
462+
case success(Success)
463+
/// Represents a failure to retrieve an identity from the registry
464+
case failure(Failure)
465+
/// Represents a scenario wherein a scm package has no valid registry identity mapping
466+
case notApplicable
467+
}
468+
469+
/// Tracks the mapping of scm packages to their registry identities
470+
private let cache = ThreadSafeKeyValueStore<SourceControlURL, CacheResult>()
471+
private let cacheTTL = DispatchTimeInterval.seconds(300) // 5m
472+
473+
public subscript(key: Key) -> CacheResult? {
474+
get { self.cache[key] }
475+
set { self.cache[key] = newValue }
476+
}
477+
478+
public subscript(storing key: Key) -> IdentityMapResult<PackageIdentity?, Error>? {
479+
get { self.cache[key]?.result }
480+
set {
481+
guard let newValue else {
482+
cache.removeValue(forKey: key)
483+
return
484+
}
485+
self.cache[key] = (
486+
result: newValue,
487+
expirationTime: .now() + self.cacheTTL
488+
)
489+
}
490+
}
491+
492+
/// Derives an identity lookup cache from the Package.resolved file if applicable.
493+
/// Asserts against the transformation mode set in the Workspace to determine how to store
494+
/// source control packages and their identity mappings.
495+
public func deriveCache(from resolvedPackages: ResolvedPackagesStore.ResolvedPackages, _ transformationMode: WorkspaceConfiguration.SourceControlToRegistryDependencyTransformation) {
496+
guard transformationMode != .disabled else {
497+
return
498+
}
499+
500+
// First run, create a mapping between registry packages and their scm location, if applicable.
501+
for resolvedPackage in resolvedPackages.values {
502+
if case let .registry(id, .some(url)) = resolvedPackage.packageRef.kind {
503+
self[storing: url] = .success(id)
504+
}
505+
}
506+
507+
// Second pass; identify source control packages that may have had their
508+
// identifiers modified to that of a registry id
509+
if transformationMode == .swizzle {
510+
for resolvedPackage in resolvedPackages.values {
511+
if case .remoteSourceControl(let url) = resolvedPackage.packageRef.kind,
512+
self.cache[url] == nil {
513+
self[storing: url] = .notApplicable
514+
}
515+
}
516+
}
517+
}
518+
}
519+
}

0 commit comments

Comments
 (0)