Files
container/Sources/NativeBuilder/ContainerBuildCache/BuildCache.swift
T
2025-08-13 10:46:14 -07:00

149 lines
5.2 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerBuildIR
import ContainerBuildSnapshotter
import ContainerizationOCI
import Foundation
/// Protocol for build cache implementations.
///
/// The cache stores operation results to avoid redundant execution.
///
/// PERFORMANCE OPTIMIZATION OPPORTUNITIES (from Design.md):
///
/// 1. Graph-Level Cache Analysis:
/// - Add a method to check multiple cache keys in parallel before execution starts
/// - Return a map of cache hits/misses for the entire build graph
/// - Example: func checkBatch(_ keys: [CacheKey]) async -> [CacheKey: Bool]
///
/// 2. Cache-Aware Graph Transformation:
/// - Implement a CachedScheduler that pre-processes the build graph
/// - Replace cached operations with lightweight cache retrieval operations
/// - Skip entire dependency chains if final results are cached
///
/// 3. Speculative Cache Warming:
/// - Add methods to prefetch likely cache entries based on build patterns
/// - Support background cache population for common operations
///
/// 4. Parallel Cache Operations:
/// - Batch get/put operations for multiple artifacts
/// - Use Swift concurrency for non-blocking cache access
/// - Example: func getBatch(_ keys: [CacheKey]) async -> [CacheKey: CachedResult?]
///
/// 5. Cache Metadata for Scheduling:
/// - Expose cache hit probability estimates
/// - Provide operation cost estimates based on historical data
/// - Enable scheduler to make smarter execution decisions
///
/// Current implementation is functionally correct but checks cache sequentially
/// during execution rather than optimizing the execution graph based on cache state.
public protocol BuildCache: Sendable {
/// Look up a cached result for an operation.
///
/// - Parameters:
/// - key: The cache key for the operation
/// - operation: The operation being cached
/// - Returns: The cached result if found, nil otherwise
func get(_ key: CacheKey, for operation: ContainerBuildIR.Operation) async -> CachedResult?
/// Store a result in the cache.
///
/// - Parameters:
/// - result: The result to cache
/// - key: The cache key
/// - operation: The operation that produced the result
func put(_ result: CachedResult, key: CacheKey, for operation: ContainerBuildIR.Operation) async
/// Get cache statistics.
///
/// - Returns: Statistics about cache usage and performance
func statistics() async -> CacheStatistics
}
/// A key for cache lookups.
public struct CacheKey: Hashable, Sendable {
/// The digest of the operation.
public let operationDigest: ContainerBuildIR.Digest
/// Input digests from dependencies.
public let inputDigests: [ContainerBuildIR.Digest]
/// Platform identifier.
public let platform: Platform
public init(
operationDigest: ContainerBuildIR.Digest,
inputDigests: [ContainerBuildIR.Digest] = [],
platform: Platform
) {
self.operationDigest = operationDigest
self.inputDigests = inputDigests
self.platform = platform
}
}
/// A cached execution result.
public struct CachedResult: Sendable {
/// The snapshot produced by the operation.
public let snapshot: Snapshot
/// Environment changes made by the operation.
public let environmentChanges: [String: EnvironmentValue]
/// Metadata changes.
public let metadataChanges: [String: String]
public init(
snapshot: Snapshot,
environmentChanges: [String: EnvironmentValue] = [:],
metadataChanges: [String: String] = [:]
) {
self.snapshot = snapshot
self.environmentChanges = environmentChanges
self.metadataChanges = metadataChanges
}
}
/// A no-op cache implementation that never caches.
public struct NoOpBuildCache: BuildCache {
public init() {}
public func get(_ key: CacheKey, for operation: ContainerBuildIR.Operation) async -> CachedResult? {
nil
}
public func put(_ result: CachedResult, key: CacheKey, for operation: ContainerBuildIR.Operation) async {
// No-op
}
public func statistics() async -> CacheStatistics {
CacheStatistics(
entryCount: 0,
totalSize: 0,
hitRate: 0,
oldestEntryAge: 0,
mostRecentEntryAge: 0,
evictionPolicy: "none",
averageEntrySize: 0,
operationMetrics: .empty,
errorCount: 0,
lastGCTime: nil,
shardInfo: nil
)
}
}