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

Async NonBlockingFileIO #704

Merged
merged 16 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/disabled-workflows/gen-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
chunk: [0,1,2,3,4,5,6,7,8]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install
Expand All @@ -38,7 +38,7 @@ jobs:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 1
- run: git fetch origin gh-pages:gh-pages
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/api-breakage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ jobs:
linux:
runs-on: ubuntu-latest
container:
image: swift:5.8
image: swift:5.10
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
# https://github.com/actions/checkout/issues/766
Expand Down
17 changes: 7 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,25 @@ on:
paths:
- '**.swift'
pull_request:
branches:
- main
- 7.x.x
paths:
- '**.swift'
- '**.swift'
- '**.yml'
workflow_dispatch:

env:
AWS_ACCESS_KEY_ID : ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY : ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DISABLE_LOCALSTACK : "false"
AWS_ENABLE_LOGGING : "true"
AWS_LOG_LEVEL: "trace"
AWS_LOG_LEVEL: "debug"
STRICT_CONCURRENCY: "true"

jobs:
macos:
runs-on: macOS-latest
runs-on: macOS-13
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: SPM build
run: swift build

Expand All @@ -37,10 +35,9 @@ jobs:
strategy:
matrix:
image:
- swift:5.7
- swift:5.8
- swift:5.9
- swiftlang/swift:nightly-5.10-jammy
- swift:5.10
services:
localstack:
image: localstack/localstack
Expand All @@ -50,7 +47,7 @@ jobs:
LOCALSTACK_ENDPOINT : "http://localstack:4566"
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Test Resource Prefix
# set AWS_TEST_RESOURCE_PREFIX environment variable to a random series of 8 letters
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-models-v5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: 5.x.x
fetch-depth: 1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-smithy-models.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install Dependencies
Expand Down
69 changes: 59 additions & 10 deletions Package.swift

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ You can find out more about credential providers [here](https://soto.codes/user-

To use Soto you need to create an `AWSClient` and a service object for the AWS service you want to work with. The `AWSClient` provides all the communication with AWS and the service object provides the configuration and APIs for communicating with a specific AWS service. More can be found out about `AWSClient` [here](https://soto.codes/user-guides/awsclient.html) and the AWS service objects [here](https://soto.codes/user-guides/service-objects.html).

Each Soto command returns a [Swift NIO](https://github.com/apple/swift-nio) `EventLoopFuture`. An `EventLoopFuture` _is not_ the response of the command, but rather a container object that will be populated with the response at a later point. In this manner calls to AWS do not block the main thread. It is recommended you familiarise yourself with the Swift NIO [documentation](https://apple.github.io/swift-nio/docs/current/NIO/), specifically [EventLoopFuture](https://apple.github.io/swift-nio/docs/current/NIO/Classes/EventLoopFuture.html) if you want to take full advantage of Soto.

The recommended manner to interact with `EventLoopFutures` is chaining. The following function returns an `EventLoopFuture` that creates an S3 bucket, puts a file in the bucket, reads the file back from the bucket and finally prints the contents of the file. Each of these operations are chained together. The output of one being the input of the next.

```swift
import SotoS3 //ensure this module is specified as a dependency in your package.swift

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ extension CognitoIdentity {
identityPoolId: String,
identityProvider: IdentityProviderFactory,
region: Region,
httpClient: HTTPClient,
httpClient: any AWSHTTPClient,
logger: Logger = AWSClient.loggingDisabled
) {
self.client = AWSClient(credentialProvider: .empty, httpClientProvider: .shared(httpClient), logger: logger)
self.cognitoIdentity = CognitoIdentity(client: self.client, region: region)
self.identityPoolId = identityPoolId
let context = IdentityProviderFactory.Context(cognitoIdentity: cognitoIdentity, identityPoolId: identityPoolId, logger: logger)
let context = IdentityProviderFactory.Context(cognitoIdentity: self.cognitoIdentity, identityPoolId: identityPoolId, logger: logger)
self.identityProvider = identityProvider.createProvider(context: context)
}

Expand All @@ -59,7 +59,7 @@ extension CognitoIdentity {
}

func shutdown() async throws {
try await identityProvider.shutdown()
try await self.identityProvider.shutdown()
try await self.client.shutdown()
}
}
Expand Down Expand Up @@ -96,7 +96,7 @@ extension CredentialProviderFactory {
/// For the `identityProvider` parameter construct a struct conforming to `IdentityProvider` as follows
/// ```
/// struct MyIdentityProvider: IdentityProvider {
/// func getIdentity(on eventLoop: EventLoop, logger: Logger) -> EventLoopFuture<CognitoIdentity.IdentityParams> {
/// func getIdentity(logger: Logger) async throws -> CognitoIdentity.IdentityParams {
/// // code to call backend to return the identity id and token. When backend call completes fill out a
/// // `CognitoIdentity.IdentityParams` struct with the details.
/// }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ extension IdentityProviderFactory {
/// authParameters: ["USERNAME": "my-username", "PASSWORD": "my-password"],
/// clientId: "my-client-id"
/// )
/// return cognitoIdentityProvider.initiateAuth(request, logger: context.logger, on: context.eventLoop)
/// return cognitoIdentityProvider.initiateAuth(request, logger: context.logger)
/// .flatMapThrowing { response in
/// guard let idToken = response.authenticationResult?.idToken else { throw CredentialProviderError.noProvider }
/// return [userPoolIdentityProvider: idToken]
Expand Down
6 changes: 3 additions & 3 deletions Sources/Soto/Extensions/DynamoDB/DynamoDBDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private struct _DecoderStorage {
}

/// return the attribute at the top of the storage
var topAttribute: DynamoDB.AttributeValue { return attributes.last! }
var topAttribute: DynamoDB.AttributeValue { return self.attributes.last! }

/// push a new attribute onto the storage
mutating func pushAttribute(_ attribute: DynamoDB.AttributeValue) {
Expand Down Expand Up @@ -183,7 +183,7 @@ private class _DynamoDBDecoder: Decoder {
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }

return UKDC(attribute: try self.getValue(forKey: key), decoder: self.decoder)
return try UKDC(attribute: self.getValue(forKey: key), decoder: self.decoder)
}

func _superDecoder(forKey key: CodingKey) throws -> Decoder {
Expand Down Expand Up @@ -438,7 +438,7 @@ private class _DynamoDBDecoder: Decoder {
self.decoder.codingPath.append(DynamoDBCodingKey(index: self.currentIndex))
defer { self.decoder.codingPath.removeLast() }

return UKDC(attribute: try self.getAttributeValue(), decoder: self.decoder)
return try UKDC(attribute: self.getAttributeValue(), decoder: self.decoder)
}

mutating func superDecoder() throws -> Decoder {
Expand Down
14 changes: 4 additions & 10 deletions Sources/Soto/Extensions/S3/FileByteBufferAsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,17 @@ public struct FileByteBufferAsyncSequence: AsyncSequence {
let fileIO: NonBlockingFileIO
let chunkSize: Int
let byteBufferAllocator: ByteBufferAllocator
let eventLoop: EventLoop

public init(
_ fileHandle: NIOFileHandle,
fileIO: NonBlockingFileIO,
chunkSize: Int,
byteBufferAllocator: ByteBufferAllocator = ByteBufferAllocator(),
eventLoop: EventLoop
byteBufferAllocator: ByteBufferAllocator = ByteBufferAllocator()
) {
self.fileHandle = fileHandle
self.fileIO = fileIO
self.chunkSize = chunkSize
self.byteBufferAllocator = byteBufferAllocator
self.eventLoop = eventLoop
}

public struct AsyncIterator: AsyncIteratorProtocol {
Expand All @@ -46,16 +43,14 @@ public struct FileByteBufferAsyncSequence: AsyncSequence {
let chunkSize: Int
let fileIO: NonBlockingFileIO
let byteBufferAllocator: ByteBufferAllocator
let eventLoop: EventLoop

public mutating func next() async throws -> ByteBuffer? {
let byteBuffer = try await self.fileIO.read(
fileHandle: self.fileHandle,
fromOffset: Int64(self.offset),
byteCount: 64 * 1024,
allocator: self.byteBufferAllocator,
eventLoop: self.eventLoop
).get()
allocator: self.byteBufferAllocator
)
let size = byteBuffer.readableBytes
guard size > 0 else {
return nil
Expand All @@ -70,8 +65,7 @@ public struct FileByteBufferAsyncSequence: AsyncSequence {
fileHandle: self.fileHandle,
chunkSize: self.chunkSize,
fileIO: self.fileIO,
byteBufferAllocator: self.byteBufferAllocator,
eventLoop: self.eventLoop
byteBufferAllocator: self.byteBufferAllocator
)
}
}
Expand Down
Loading