-
Notifications
You must be signed in to change notification settings - Fork 536
Add --max-concurrent-downloads flag for parallel layer downloads #716
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
53e6c45
903beb8
2c97436
21ac59f
9659c53
b2e13d9
efa7b87
341c918
ae30d88
c905f84
2029f72
e1c03a2
3374833
b9e673a
7359006
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -208,7 +208,15 @@ public struct Flags { | |
| self.disableProgressUpdates = disableProgressUpdates | ||
| } | ||
|
|
||
| public init(disableProgressUpdates: Bool, maxConcurrentDownloads: Int) { | ||
| self.disableProgressUpdates = disableProgressUpdates | ||
| self.maxConcurrentDownloads = maxConcurrentDownloads | ||
|
||
| } | ||
|
|
||
| @Flag(name: .long, help: "Disable progress bar updates") | ||
| public var disableProgressUpdates = false | ||
|
|
||
| @Option(name: .long, help: "Maximum number of concurrent layer downloads (default: 3)") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @adityaramani Should we use the word "layer" or "blob" here? |
||
| public var maxConcurrentDownloads: Int = 3 | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| #!/usr/bin/env swift | ||
|
|
||
| import Foundation | ||
|
|
||
| func testConcurrentDownloads() async throws { | ||
| print("Testing concurrent download behavior...\n") | ||
|
|
||
| // Track concurrent task count | ||
| actor ConcurrencyTracker { | ||
| var currentCount = 0 | ||
| var maxObservedCount = 0 | ||
| var completedTasks = 0 | ||
|
|
||
| func taskStarted() { | ||
| currentCount += 1 | ||
| maxObservedCount = max(maxObservedCount, currentCount) | ||
| } | ||
|
|
||
| func taskCompleted() { | ||
| currentCount -= 1 | ||
| completedTasks += 1 | ||
| } | ||
|
|
||
| func getStats() -> (max: Int, completed: Int) { | ||
| return (maxObservedCount, completedTasks) | ||
| } | ||
|
|
||
| func reset() { | ||
| currentCount = 0 | ||
| maxObservedCount = 0 | ||
| completedTasks = 0 | ||
| } | ||
| } | ||
|
|
||
| let tracker = ConcurrencyTracker() | ||
|
|
||
| // Test with different concurrency limits | ||
| for maxConcurrent in [1, 3, 6] { | ||
| await tracker.reset() | ||
|
|
||
| // Simulate downloading 20 layers | ||
| let layerCount = 20 | ||
| let layers = Array(0..<layerCount) | ||
|
|
||
| print("Testing maxConcurrent=\(maxConcurrent) with \(layerCount) layers...") | ||
|
|
||
| let startTime = Date() | ||
|
|
||
| try await withThrowingTaskGroup(of: Void.self) { group in | ||
| var iterator = layers.makeIterator() | ||
|
|
||
| // Start initial batch based on maxConcurrent | ||
| for _ in 0..<maxConcurrent { | ||
| if iterator.next() != nil { | ||
| group.addTask { | ||
| await tracker.taskStarted() | ||
| try await Task.sleep(nanoseconds: 10_000_000) | ||
| await tracker.taskCompleted() | ||
| } | ||
| } | ||
| } | ||
| for try await _ in group { | ||
| if iterator.next() != nil { | ||
| group.addTask { | ||
| await tracker.taskStarted() | ||
| try await Task.sleep(nanoseconds: 10_000_000) | ||
| await tracker.taskCompleted() | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let duration = Date().timeIntervalSince(startTime) | ||
| let stats = await tracker.getStats() | ||
|
|
||
| print(" ✓ Completed: \(stats.completed)/\(layerCount)") | ||
| print(" ✓ Max concurrent: \(stats.max)") | ||
| print(" ✓ Duration: \(String(format: "%.3f", duration))s") | ||
|
|
||
| guard stats.max <= maxConcurrent + 1 else { | ||
| throw TestError.concurrencyLimitExceeded | ||
| } | ||
|
|
||
| guard stats.completed == layerCount else { | ||
| throw TestError.incompleteTasks | ||
| } | ||
|
|
||
| print(" ✅ PASSED\n") | ||
| } | ||
|
|
||
| print("All tests passed!") | ||
| } | ||
|
|
||
| enum TestError: Error { | ||
| case concurrencyLimitExceeded | ||
| case incompleteTasks | ||
| } | ||
|
|
||
| Task { | ||
| do { | ||
| try await testConcurrentDownloads() | ||
| exit(0) | ||
| } catch { | ||
| print("Test failed: \(error)") | ||
| exit(1) | ||
| } | ||
| } | ||
|
|
||
| RunLoop.main.run() |
Uh oh!
There was an error while loading. Please reload this page.