-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add multiple cache storages feature #156
Conversation
- `Runner.Options.CacheMode.storages` is added - When saving, cache frameworks into all storages with `CacheActorKind.producer` - When restoring, storages with `CacheActorKind.consumer` are used in order - If first storage failed to restore some caches, the failed ones are tried to be fetched from second storage
9f7018b
to
5953d6c
Compare
106fc1b
to
0f1c98a
Compare
@@ -225,12 +229,6 @@ struct CacheSystem: Sendable { | |||
} | |||
} | |||
|
|||
private func fetchArtifacts(target: CacheTarget, to destination: URL) async throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was unused.
guard cacheMode.isConsumingCacheEnabled else { | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is checked within restoreAllAvailableCachesIfNeeded
so should not be needed.
ad5a600
to
8c7c16e
Compare
ae56332
to
f6cbef1
Compare
@@ -108,7 +108,6 @@ struct CacheSystem: Sendable { | |||
static let defaultParalellNumber = 8 | |||
private let pinsStore: PinsStore | |||
private let outputDirectory: URL | |||
private let storage: (any CacheStorage)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the introduction of cache policies, storages may be different on when saving caches and when restoring caches. So removed the property and passes by arguments instead.
case .project: | ||
// For `.project`, just checking whether the valid caches (already built frameworks under the project) | ||
// exist or not (not restoring anything from external locations). | ||
return await restoreCachesForTargets( | ||
availableTargets, | ||
cacheSystem: cacheSystem, | ||
cacheStorage: nil | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here:
Scipio/Sources/ScipioKit/Producer/FrameworkProducer.swift
Lines 245 to 259 in f6cbef1
if isValidCache && exists { | |
logger.info( | |
"✅ Valid \(product.target.name).xcframework (\(expectedCacheKeyHash)) is exists. Skip building.", metadata: .color(.green) | |
) | |
return true | |
} else { | |
if exists { | |
logger.warning("⚠️ Existing \(frameworkName) is outdated.", metadata: .color(.yellow)) | |
logger.info("🗑️ Delete \(frameworkName)", metadata: .color(.red)) | |
try fileSystem.removeFileTree(outputPath.absolutePath) | |
} | |
guard let cacheStorage else { | |
return false | |
} |
remainingTargets.subtract(restoredPerStorage) | ||
if remainingTargets.isEmpty { | ||
break | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if all frameworks are successfully restored, we don't need to proceed to next cache storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[not mandatory] it's kindly to comment this
// Making the cache key compatible with 0.24.0 temporarily for easier debugging. | ||
// | ||
// TODO: revert this before merging | ||
scipioVersion: "578ce98d236e79dad3e473cb11153e867be07174" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the feature, we're better to add --disable-scipio-version-management
option for this purpose.
It's useful for the development phase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling when developing is not a solution, because if excluding scipioVersion
from cache keys, those are not compatible with the cache keys generated by release-versioned Scipio.
Ideally we should introduce some kind of Scipio's cache versioning and increment it when there are some incompatible changes to built frameworks (but it'll make Scipio's development and review process a bit complicated).
# Conflicts: # Sources/ScipioKit/Producer/FrameworkProducer.swift
45b6089
to
fa589b1
Compare
fa589b1
to
0006fbb
Compare
import ScipioS3Storage | ||
|
||
let s3Storage: some CacheStorage = ScipioS3Storage.S3Storage(config: ...) | ||
let localCacheStorage: some CacheStorage = LocalCacheStorage() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't instantiate LocalCacheStorage
from external. Use CachePolicy.localDisk
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LocalDiskCacheStorage is public type and can be initialized with custom cache directory. Do you mean that you'd like to make the type internal and expose the functionality through CachePolicy.localDisk
only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
Because we want to restrict their actors. e.g. for ProjectCache
, users can only use [.producer]
combination.
To conceal the storage constructed, we can prevent mistakes in usage.
let storagesWithProducer = cachePolicies.storages(for: .producer) | ||
if !storagesWithProducer.isEmpty { | ||
await cacheSystem.cacheFrameworks(targets, to: storagesWithProducer) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Q] I'm concerned that the way storages are handled is different for consumers and producers.
For consumers, storages are evaluated in order and the top ones are used.
On the other hand, producers try to write to all declared storages at the same time.
Is this difference in behavior intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's intentional. If we don't cache built frameworks to 2nd storages and later, the storages will not be able to restore the frameworks so saving to all storages will be required I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost great!
remainingTargets.subtract(restoredPerStorage) | ||
if remainingTargets.isEmpty { | ||
break | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[not mandatory] it's kindly to comment this
case custom(URL) | ||
} | ||
|
||
private let cacheDirectroy: CacheDirectory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀 Directroy
19be7d0
to
2720cde
Compare
Resolves #148.
Runner.Options.CacheMode
is replaced with[Runner.Options.CachePolicy]
cacheMode: .disabled
->cachePolicies: []
cacheMode: .project
->cachePolicies: [.project]
cacheMode: .storage(storage, actors)
->cachePolicies: [.init(storage: storage, actors: actors)]
CacheActorKind.producer
CacheActorKind.consumer
are used in order