Skip to content
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

Merged
merged 32 commits into from
Nov 18, 2024

Conversation

ikesyo
Copy link
Collaborator

@ikesyo ikesyo commented Nov 8, 2024

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)]
  • 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 (next) storage

- `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
@ikesyo ikesyo force-pushed the multiple-cache-storages branch from 9f7018b to 5953d6c Compare November 8, 2024 06:03
@ikesyo ikesyo force-pushed the multiple-cache-storages branch from 106fc1b to 0f1c98a Compare November 8, 2024 08:38
@@ -225,12 +229,6 @@ struct CacheSystem: Sendable {
}
}

private func fetchArtifacts(target: CacheTarget, to destination: URL) async throws {
Copy link
Collaborator Author

@ikesyo ikesyo Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was unused.

Comment on lines 191 to 193
guard cacheMode.isConsumingCacheEnabled else {
return false
}
Copy link
Collaborator Author

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.

@ikesyo ikesyo force-pushed the multiple-cache-storages branch from ad5a600 to 8c7c16e Compare November 11, 2024 06:06
@ikesyo ikesyo force-pushed the multiple-cache-storages branch from ae56332 to f6cbef1 Compare November 11, 2024 06:45
@@ -108,7 +108,6 @@ struct CacheSystem: Sendable {
static let defaultParalellNumber = 8
private let pinsStore: PinsStore
private let outputDirectory: URL
private let storage: (any CacheStorage)?
Copy link
Collaborator Author

@ikesyo ikesyo Nov 11, 2024

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.

Comment on lines 132 to 139
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
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here:

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
}

Comment on lines 182 to 185
remainingTargets.subtract(restoredPerStorage)
if remainingTargets.isEmpty {
break
}
Copy link
Collaborator Author

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.

Copy link
Owner

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

@ikesyo ikesyo marked this pull request as ready for review November 11, 2024 07:08
Sources/ScipioKit/Producer/Cache/CacheSystem.swift Outdated Show resolved Hide resolved
Sources/ScipioKit/Producer/Cache/CacheSystem.swift Outdated Show resolved Hide resolved
Comment on lines 248 to 251
// Making the cache key compatible with 0.24.0 temporarily for easier debugging.
//
// TODO: revert this before merging
scipioVersion: "578ce98d236e79dad3e473cb11153e867be07174"
Copy link
Owner

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.

Copy link
Collaborator Author

@ikesyo ikesyo Nov 14, 2024

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).

Sources/ScipioKit/Producer/FrameworkProducer.swift Outdated Show resolved Hide resolved
Sources/scipio/scipio.docc/build-pipeline.md Outdated Show resolved Hide resolved
Sources/ScipioKit/Runner.swift Outdated Show resolved Hide resolved
Sources/ScipioKit/Producer/FrameworkProducer.swift Outdated Show resolved Hide resolved
Sources/ScipioKit/Runner.swift Outdated Show resolved Hide resolved
@ikesyo ikesyo force-pushed the multiple-cache-storages branch from 45b6089 to fa589b1 Compare November 12, 2024 08:28
@ikesyo ikesyo force-pushed the multiple-cache-storages branch from fa589b1 to 0006fbb Compare November 12, 2024 08:45
Sources/ScipioKit/Runner.swift Outdated Show resolved Hide resolved
import ScipioS3Storage

let s3Storage: some CacheStorage = ScipioS3Storage.S3Storage(config: ...)
let localCacheStorage: some CacheStorage = LocalCacheStorage()
Copy link
Owner

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

Copy link
Collaborator Author

@ikesyo ikesyo Nov 12, 2024

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?

Copy link
Owner

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.

Comment on lines +343 to +346
let storagesWithProducer = cachePolicies.storages(for: .producer)
if !storagesWithProducer.isEmpty {
await cacheSystem.cacheFrameworks(targets, to: storagesWithProducer)
}
Copy link
Owner

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?

Copy link
Collaborator Author

@ikesyo ikesyo Nov 14, 2024

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.

Copy link
Owner

@giginet giginet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost great!

Sources/ScipioKit/Runner.swift Outdated Show resolved Hide resolved
Sources/ScipioKit/Producer/Cache/CacheSystem.swift Outdated Show resolved Hide resolved
Sources/ScipioKit/Producer/Cache/CacheSystem.swift Outdated Show resolved Hide resolved
Comment on lines 182 to 185
remainingTargets.subtract(restoredPerStorage)
if remainingTargets.isEmpty {
break
}
Copy link
Owner

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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 Directroy

@ikesyo ikesyo force-pushed the multiple-cache-storages branch from 19be7d0 to 2720cde Compare November 15, 2024 08:11
@ikesyo ikesyo merged commit 6c1e30a into giginet:main Nov 18, 2024
2 checks passed
@ikesyo ikesyo deleted the multiple-cache-storages branch November 21, 2024 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support cache redundancy (cache hierarchy)
2 participants