Skip to content

BREAKING: Converts to Swift Concurrency #166

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

NeedleInAJayStack
Copy link
Member

This removes the NIO dependency. It is breaking because it removes all Swift NIO-isms that were present in the public APIs (like EventLoopFuture and EventLoopGroup argument/return types).

paulofaria
paulofaria previously approved these changes Jun 24, 2025
.package(url: "https://github.com/apple/swift-collections", .upToNextMajor(from: "1.0.0")),
],
targets: [
.target(
name: "GraphQL",
dependencies: [
.product(name: "NIO", package: "swift-nio"),
.product(name: "OrderedCollections", package: "swift-collections"),
]
),
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe enable strict concurrency as well

    swiftLanguageVersions: [.v5, .version("6")]

let fieldASTs = field.value
let fieldPath = path.appending(field.key)
// TODO: Probably need to lock around results access here.
results[field.key] = try await resolveField(
Copy link
Contributor

@adam-fowler adam-fowler Jun 24, 2025

Choose a reason for hiding this comment

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

Yeah this is going to cause issues. Best way to do this is have the child task return an index and result and fill out the ordered dictionary when parsing the results of the group.

group.addTask {
    ...
    return (key: field.key, result: try await resolveField(...))
}
for try await result in group {
    results[result.key] = result.result
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah perfect, thanks for the insight on how best to do this. I've adjusted to this approach.


completedResults.append(completedItem)
// TODO: Probably need to block around completedResults access here
completedResults[index] = completedItem
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment above, about returning index with result to group

Copy link
Member Author

Choose a reason for hiding this comment

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

I fixed this also

@@ -3,7 +3,7 @@ open class EventStream<Element> {
public init() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Unrelated to concurrency, but should this not be a protocol

@@ -30,13 +30,13 @@ public class ConcurrentEventStream<Element>: EventStream<Element> {

@available(macOS 10.15, iOS 15, watchOS 8, tvOS 15, *)
extension AsyncThrowingStream {
func mapStream<To>(_ closure: @escaping (Element) throws -> To)
func mapStream<To>(_ closure: @escaping (Element) async throws -> To)
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason you have this when AsyncSequence.map exists?

Copy link
Contributor

Choose a reason for hiding this comment

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

Unstructured Tasks aren't great. They make cancellation hard.
In actual fact you could just replace the whole of this file with AsyncStream<Element>. This opens up all the functions that AsyncSequence has eg map, filter, compactMap, first, etc and cancellation will come for free.

Copy link
Member Author

@NeedleInAJayStack NeedleInAJayStack Jun 24, 2025

Choose a reason for hiding this comment

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

We had created this a few years ago before Swift Concurrency existed to abstract different AsyncSequence/PubSub backends (at the time, options included RxSwift and Combine). Since we're switching over to concurrency, I agree, we should switch to having AsyncThrowingStream as a first class member in our API. I've done that work here: 9ec313a

Copy link
Member

Choose a reason for hiding this comment

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

I guess @adam-fowler’s point is to use some async sequence in the public APIs instead of the concrete stream, is that correct @adam-fowler ?

This removes the NIO dependency. It is breaking because it removes all Swift NIO-isms that were present in the public APIs (like EventLoopFuture and EventLoopGroup argument/return types).
@NeedleInAJayStack NeedleInAJayStack changed the title Draft: feat!: Uses swift concurrency under the hood BREAKING: Converts to Swift Concurrency Jun 25, 2025
The intent is to replace it with swift-distributed-tracing integration.
This resolves the race condition caused by the inbox counts and the event delivery. If event delivery happens before the subsequent publish increments the inbox counts, then the counts will be lower than expected. Resolved by just not asking for inbox counts, since they aren't relevant to the test.
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.

3 participants