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

FileSystem.copyItem can parallelise directory copy #2806

Merged
merged 16 commits into from
Jul 29, 2024

Conversation

UncleMattHope
Copy link
Contributor

FileSystem.copyItem defaults to perform directory copies in parallel

Motivation:

Using some limited parallelism can improved the speed of the copy for very limited increase in cpu use.

In circumstances where the source and/or destination are on a high latency device, such as a network file system,
the resulting speed up can be even more significant.
Providing conservative defaults, while allowing users the ability to control it directly if desired, provides for a faster default and access to user targeted gains in performance.
The use of parallel by default is considered in keeping with the swift-nio model of concurrent capabilities.

The parallelism used is restricted because the underlying limited resource for this is highly likely to be limited system file descriptors, rather than the limits of the underlying executor itself, especially on embedded devices with significantly more limited concurrent descriptors.

Modifications:

All copyItem overloads except copyItem(sourcePath:destinationPath:shouldProceedAfterError:shouldCopyFile)
can, and likely will, use concurrent copies when copying directories.

A Newly type CopyStrategy controls the behaviour, allowing:

  • parallel(maxDescriptors: Int)
    • Parallel limited to a maximum number of concurrently open file descriptors.
  • sequential
    • The previous behaviour, still asynchronous but only one operation at a time
  • platformDefault
    • Whatever the API determines the default should be from the prior options

The cancellation semantics of copyItem were previously unclear/undocumented.
These are now stated and cancellation is tested.

There were some aspects of naming/types on this API which were inconsistent/unclear. Those have been altered to aid clarity.

  • Less duplication with the protocol docs
  • Consistent use of item not file
  • Use DirectoryEntry for the source in both shouldXxx callbacks

NIOFileSystem package status remains unchanged, it is still _NIOFileSystem with the semantics that entails.

Result:

For any use of the following functions on FileSystemProtocol, when backed by FileSystem:

copyItem(*):

The shouldCopyItem check happens before the internal FileSystem "do I know how to copy this sort of FileType" check. This makes the code a little simpler, but the desire is to allow consuming code to filter out anything unexpected if they like without needing to trap errors.

copyItem(sourcePath:destinationPath):

Depending on the platform this will become parallel, users on MacOS, Linux and Windows will have up to 4 concurrent file system operations at once.
Empirical testing on a MacBook pro with a significant dense directory hierarchy of 12 GiB had performance improvements of ~50% reduction in elapsed time for CPU usage increases below the noise floor of the testing

copyItem(sourcePath:destinationPath:shouldProceedAfterError:shouldCopyFile):

When migrating from this to copyItem(sourcePath:destinationPath:copyStrategy:shouldProceedAfterError:shouldCopyItem)care should be taken to ensure the should callbacks are safe to use in different concurrency domains, or that CopyStrategy.sequential is explicitly stated.
The backward compatibility shim provided does this by default.

Configurable via CopyStrategy.
- Default on known platforms is to be parallel.
Cancellation semantics documented and tested.
Backward compatibility shim for old copyItem.
Fixed inconsistency with use of DirectoryEntry for the source
on shouldXxx callbacks
Some comment improvements:
- Less duplication with the protocol
- Consistent use of `item`  not 'file'
- Tweak a comment on default threads in thread pool that was
  wrong and confused me

This has defaults for concurrency based on
limited empirical testing on MacOS assumed to
likely apply to other similar targets.
Embedded platforms have more conservative limits.
Unanticipated platforms remain sequential.

Semantic Changes

copyItem(*):

shouldCopyItem check happens before the FileSystem type check.
This makes the code a little simpler, and also allows consuming code
to filter out anything unexpected if they like without needing to trap
errors.

copyItem(sourcePath:destinationPath):
Depending on the platform it will become parallel

When migrating from shouldProceedAfterError/shouldCopyFile to
shouldProceedAfterError/shouldCopyItem care should be taken to ensure
they are safe to use in different concurrency domains, or that
CopyStrategy.sequential is explicitly stated.
The backward compatibility shim provided does this be default.
//===----------------------------------------------------------------------===//

/// How to perform copies. Currently only relevant to directory level copies when using
/// ``FileSystemProtocol.copyItem``
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to be (the docs CI will likely complain at you anyway):

Suggested change
/// ``FileSystemProtocol.copyItem``
/// ``FileSystemProtocol/copyItem``

Sources/NIOFileSystem/CopyStrategy.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/FileSystem.swift Show resolved Hide resolved
Sources/NIOFileSystem/FileSystem.swift Outdated Show resolved Hide resolved
///
/// #### Errors
///
/// No errors should be throw by implementors without first calling ``shouldProceedAfterError``,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// No errors should be throw by implementors without first calling ``shouldProceedAfterError``,
/// No errors should be thrown by implementors without first calling ``shouldProceedAfterError``,

Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
///
/// This is primarily exposed for testing to ensure use of the parallel paths (which are more complex) while keeping actual
/// parallelism to minimal levels to make debugging simpler.
public static let minimalParallel: Self = .init(.parallel(minDescriptorsAllowed))
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this useful outside of our own tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found writing tests using .parallel(maxDescriptors:2) to be painful since that call declares throws, necessitating lots of cascading the throwing capability or wrapping it in an ugly try/catch.

It seemed reasonable that other people testing code using this might want to make their tests follow the parallel path via .platformDefault, but have the option to switch to the limited parallelism easily without having to mess about at the CopyStrategy declaration point in their tests.

Happy to hide it and just push it into our tests if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't feel too strongly here but my lean is to hide it from the public API, as you note using it outside of testing is an anti-pattern, and I think it's unlikely that users will also need this in their tests. If they do they can try! parallel(maxDescriptors: 2) (which should be safe per our docs).

Copy link
Member

Choose a reason for hiding this comment

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

I agree with this. I don't see why any user would want to set the minimum parallelism

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cool, I'll move it to the testing

///
/// This is primarily exposed for testing to ensure use of the parallel paths (which are more complex) while keeping actual
/// parallelism to minimal levels to make debugging simpler.
public static let minimalParallel: Self = .init(.parallel(minDescriptorsAllowed))
Copy link
Member

Choose a reason for hiding this comment

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

I agree with this. I don't see why any user would want to set the minimum parallelism

Sources/NIOFileSystem/FileSystem.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
Sources/NIOFileSystem/Internal/ParallelDirCopy.swift Outdated Show resolved Hide resolved
// We haven't started monitoring for task completion, so inProgress is 'worst case'.
var inProgress = 0
while inProgress <= maxConcurrentOperations {
try Task.checkCancellation()
Copy link
Member

Choose a reason for hiding this comment

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

We are calling iter.next() on the next line which will check for cancellation when needed

while let _ = try await taskGroup.next() {
var keepConsuming = true
while keepConsuming {
try Task.checkCancellation()
Copy link
Member

Choose a reason for hiding this comment

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

Same here

Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

This LGTM, thanks @UncleMattHope!

inProgress += 1
}
} else {
// We completed things before we hit the limit
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a bug, that made my cancellation test flaky if it happened to trigger during this phase.
I have a fix for that and will push shortly.

The cancellation propagates as the sequence cleanly terminating, so our task just completes early, rather than resulting an a cancellation.
simply inserting a try Task.checkCancellation() here is sufficient, but I have also streamlined the test side to be faster too

These made the cancellation test flaky
@UncleMattHope
Copy link
Contributor Author

Confirmation of acceptance of API break in _NIOFileSystem

5 breaking changes detected in _NIOFileSystem:
💔 API breakage: func FileSystem.copyItem(at:to:shouldProceedAfterError:shouldCopyFile:) has parameter 2 type change from (_NIOFileSystem.DirectoryEntry, any Swift.Error) async throws -> Swift.Void to _NIOFileSystem.CopyStrategy
💔 API breakage: func FileSystem.copyItem(at:to:shouldProceedAfterError:shouldCopyFile:) has parameter 3 type change from (SystemPackage.FilePath, SystemPackage.FilePath) async -> Swift.Bool to (_NIOFileSystem.DirectoryEntry, any Swift.Error) async throws -> Swift.Void
💔 API breakage: func FileSystemProtocol.copyItem(at:to:strategy:shouldProceedAfterError:shouldCopyItem:) has been added as a protocol requirement
💔 API breakage: func FileSystem.copyItem(at:to:shouldProceedAfterError:shouldCopyFile:) has been renamed to func copyItem(at:to:strategy:shouldProceedAfterError:shouldCopyItem:)
💔 API breakage: func FileSystemProtocol.copyItem(at:to:) has been renamed to func copyItem(at:to:strategy:)

Since NIOFileSystem is still in leading underscore mode this is deemed acceptable (confirmed in discussions offline)

  • In most cases the backward compatibility shim provided in FileSystemProtocol will cover it.
  • In cases where a user is actively using the callbacks or direct usage of FileSystem becoming aware of the semantic changes is desirable considering the cost to implement full backward compatibility

@glbrntt glbrntt merged commit 022ee13 into apple:main Jul 29, 2024
26 of 28 checks passed
UncleMattHope added a commit that referenced this pull request Aug 1, 2024
FilesystemProtocol/copyItem has an overload providing the default CopyStrategy

Motivation:

Based on user feedback from #2806
This makes it easier to use, especially when copying files such that it is irrelevant.

Modifications:

Add overload providing (at:to:shouldProceedAfterError:shouldCopyItem:) with CopyStrategy.platformDefault
as the strategy.
Fix a cancellation bug which resulted in a test being flaky.
In addition made the test cover that case reliably to prevent it coming back.

Result:

A simpler and more robust to change API will exist for copyItem()
@ayush1794 ayush1794 added the semver/minor Adds new public API. label Aug 5, 2024
cgrindel-self-hosted-renovate bot referenced this pull request in cgrindel/rules_swift_package_manager Aug 19, 2024
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [apple/swift-nio](https://togithub.com/apple/swift-nio) | minor |
`2.68.0` -> `2.70.0` |

---

### Release Notes

<details>
<summary>apple/swift-nio (apple/swift-nio)</summary>

###
[`v2.70.0`](https://togithub.com/apple/swift-nio/releases/tag/2.70.0):
SwiftNIO 2.70.0

[Compare
Source](https://togithub.com/apple/swift-nio/compare/2.69.0...2.70.0)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### SemVer Minor

- `FileSystem.copyItem` can parallelise directory copy by
[@&#8203;UncleMattHope](https://togithub.com/UncleMattHope) in
[https://github.com/apple/swift-nio/pull/2806](https://togithub.com/apple/swift-nio/pull/2806)
- `ChannelOption`: Allow types to be accessed with leading dot syntax by
[@&#8203;ayush1794](https://togithub.com/ayush1794) in
[https://github.com/apple/swift-nio/pull/2816](https://togithub.com/apple/swift-nio/pull/2816)
- Make `EventLoopPromise` conform to Equatable by
[@&#8203;gjcairo](https://togithub.com/gjcairo) in
[https://github.com/apple/swift-nio/pull/2714](https://togithub.com/apple/swift-nio/pull/2714)
- Provide a default `CopyStrategy` overload for copyItem. by
[@&#8203;UncleMattHope](https://togithub.com/UncleMattHope) in
[https://github.com/apple/swift-nio/pull/2818](https://togithub.com/apple/swift-nio/pull/2818)

##### SemVer Patch

- Better align shutdown semantics of testing event loops by
[@&#8203;simonjbeaumont](https://togithub.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2800](https://togithub.com/apple/swift-nio/pull/2800)
- Clone files on Darwin rather than copying them by
[@&#8203;rnro](https://togithub.com/rnro) in
[https://github.com/apple/swift-nio/pull/2823](https://togithub.com/apple/swift-nio/pull/2823)

##### Other Changes

- Fix compose file used in update-benchmark-thresholds script by
[@&#8203;simonjbeaumont](https://togithub.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2808](https://togithub.com/apple/swift-nio/pull/2808)
- Remove advice to generate linux tests. by
[@&#8203;PeterAdams-A](https://togithub.com/PeterAdams-A) in
[https://github.com/apple/swift-nio/pull/2807](https://togithub.com/apple/swift-nio/pull/2807)
- Make `testInstantTCPConnectionResetThrowsError` more reliable by
[@&#8203;hamzahrmalik](https://togithub.com/hamzahrmalik) in
[https://github.com/apple/swift-nio/pull/2810](https://togithub.com/apple/swift-nio/pull/2810)
- \[CI] Add `shellcheck` and fix up warnings by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2809](https://togithub.com/apple/swift-nio/pull/2809)
- \[CI] Fix docs check by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2811](https://togithub.com/apple/swift-nio/pull/2811)
- \[CI] Add Swift 6 language mode workflow by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2812](https://togithub.com/apple/swift-nio/pull/2812)
- Fix test compilation on non-macOS Darwin platforms by
[@&#8203;simonjbeaumont](https://togithub.com/simonjbeaumont) in
[https://github.com/apple/swift-nio/pull/2817](https://togithub.com/apple/swift-nio/pull/2817)
- Add `.index-build` to `.gitignore` by
[@&#8203;MaxDesiatov](https://togithub.com/MaxDesiatov) in
[https://github.com/apple/swift-nio/pull/2819](https://togithub.com/apple/swift-nio/pull/2819)
- \[CI] Add action and workflow to check for semver label by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2814](https://togithub.com/apple/swift-nio/pull/2814)
- Update repository docs for swift-version support and recent CI check
changes by [@&#8203;UncleMattHope](https://togithub.com/UncleMattHope)
in
[https://github.com/apple/swift-nio/pull/2815](https://togithub.com/apple/swift-nio/pull/2815)
- Fix failing build for test by
[@&#8203;ayush1794](https://togithub.com/ayush1794) in
[https://github.com/apple/swift-nio/pull/2824](https://togithub.com/apple/swift-nio/pull/2824)
- Fix typo in comment in `WebSocketErrorCodes.swift` by
[@&#8203;valeriyvan](https://togithub.com/valeriyvan) in
[https://github.com/apple/swift-nio/pull/2604](https://togithub.com/apple/swift-nio/pull/2604)
- \[CI] Add a scheduled workflow for tests and benchmarks by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2822](https://togithub.com/apple/swift-nio/pull/2822)
- \[CI] Fix label check by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2827](https://togithub.com/apple/swift-nio/pull/2827)

#### New Contributors

- [@&#8203;UncleMattHope](https://togithub.com/UncleMattHope) made their
first contribution in
[https://github.com/apple/swift-nio/pull/2806](https://togithub.com/apple/swift-nio/pull/2806)
- [@&#8203;ayush1794](https://togithub.com/ayush1794) made their first
contribution in
[https://github.com/apple/swift-nio/pull/2816](https://togithub.com/apple/swift-nio/pull/2816)
- [@&#8203;valeriyvan](https://togithub.com/valeriyvan) made their first
contribution in
[https://github.com/apple/swift-nio/pull/2604](https://togithub.com/apple/swift-nio/pull/2604)

**Full Changelog**:
apple/swift-nio@2.69.0...2.70.0

###
[`v2.69.0`](https://togithub.com/apple/swift-nio/releases/tag/2.69.0):
SwiftNIO 2.69.0

[Compare
Source](https://togithub.com/apple/swift-nio/compare/2.68.0...2.69.0)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### SemVer Minor

- Add manual control to `NIOLockedValueBox` by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2786](https://togithub.com/apple/swift-nio/pull/2786)
- ChannelHandler: provide static `(un)wrap(In|Out)bound(In|Out)` by
[@&#8203;weissi](https://togithub.com/weissi) in
[https://github.com/apple/swift-nio/pull/2791](https://togithub.com/apple/swift-nio/pull/2791)

##### SemVer Patch

- Pre-box some errors to reduce allocations by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2765](https://togithub.com/apple/swift-nio/pull/2765)
- Allow in-place mutation of `NIOLoopBoundBox.value` by
[@&#8203;dnadoba](https://togithub.com/dnadoba) in
[https://github.com/apple/swift-nio/pull/2771](https://togithub.com/apple/swift-nio/pull/2771)
- Avoid creating a yield ID counter per async writer by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2768](https://togithub.com/apple/swift-nio/pull/2768)
- Combine the two `NIOAsyncChannel` channel handlers by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2779](https://togithub.com/apple/swift-nio/pull/2779)
- Use the new Android overlay and Bionic module from Swift 6 by
[@&#8203;finagolfin](https://togithub.com/finagolfin) in
[https://github.com/apple/swift-nio/pull/2784](https://togithub.com/apple/swift-nio/pull/2784)
- Change `unsafeDownCast` to `as!` by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2802](https://togithub.com/apple/swift-nio/pull/2802)

##### Other Changes

- CI migration to GitHub Action by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
([https://github.com/apple/swift-nio/pull/2760](https://togithub.com/apple/swift-nio/pull/2760)
[https://github.com/apple/swift-nio/pull/2762](https://togithub.com/apple/swift-nio/pull/2762)
[https://github.com/apple/swift-nio/pull/2763](https://togithub.com/apple/swift-nio/pull/2763)
[https://github.com/apple/swift-nio/pull/2764](https://togithub.com/apple/swift-nio/pull/2764)
[https://github.com/apple/swift-nio/pull/2767](https://togithub.com/apple/swift-nio/pull/2767)
[https://github.com/apple/swift-nio/pull/2766](https://togithub.com/apple/swift-nio/pull/2766)
[https://github.com/apple/swift-nio/pull/2776](https://togithub.com/apple/swift-nio/pull/2776)
[https://github.com/apple/swift-nio/pull/2780](https://togithub.com/apple/swift-nio/pull/2780)
[https://github.com/apple/swift-nio/pull/2785](https://togithub.com/apple/swift-nio/pull/2785)
[https://github.com/apple/swift-nio/pull/2781](https://togithub.com/apple/swift-nio/pull/2781)
[https://github.com/apple/swift-nio/pull/2787](https://togithub.com/apple/swift-nio/pull/2787)
[https://github.com/apple/swift-nio/pull/2783](https://togithub.com/apple/swift-nio/pull/2783)
[https://github.com/apple/swift-nio/pull/2789](https://togithub.com/apple/swift-nio/pull/2789)
[https://github.com/apple/swift-nio/pull/2790](https://togithub.com/apple/swift-nio/pull/2790))
- Ignore format commit from git blame by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2796](https://togithub.com/apple/swift-nio/pull/2796)
[https://github.com/apple/swift-nio/pull/2797](https://togithub.com/apple/swift-nio/pull/2797)
[https://github.com/apple/swift-nio/pull/2801](https://togithub.com/apple/swift-nio/pull/2801)
[https://github.com/apple/swift-nio/pull/2803](https://togithub.com/apple/swift-nio/pull/2803)
- Adopt swift-format by
[@&#8203;FranzBusch](https://togithub.com/FranzBusch) in
[https://github.com/apple/swift-nio/pull/2794](https://togithub.com/apple/swift-nio/pull/2794)
- `HTTPPart` Documentation Clarification by
[@&#8203;dimitribouniol](https://togithub.com/dimitribouniol) in
[https://github.com/apple/swift-nio/pull/2775](https://togithub.com/apple/swift-nio/pull/2775)
- Add benchmark for creating `NIOAsyncChannel` by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2774](https://togithub.com/apple/swift-nio/pull/2774)
- Disable warnings as errors on Swift 6 and main by
[@&#8203;glbrntt](https://togithub.com/glbrntt) in
[https://github.com/apple/swift-nio/pull/2793](https://togithub.com/apple/swift-nio/pull/2793)

**Full Changelog**:
apple/swift-nio@2.68.0...2.69.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://togithub.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4xOC4xIiwidXBkYXRlZEluVmVyIjoiMzguMzkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: cgrindel-self-hosted-renovate[bot] <139595543+cgrindel-self-hosted-renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants