Skip to content

Commit 25c6657

Browse files
feat: New nio-based concurrent executor
1 parent 7fc8b83 commit 25c6657

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

Sources/GraphQL/Execution/Execute.swift

+32
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public struct SerialFieldExecutionStrategy: QueryFieldExecutionStrategy,
149149
*
150150
* Each field is resolved as an individual task on a concurrent dispatch queue.
151151
*/
152+
@available(*, deprecated, message: "Use ConcurrentFieldExecutionStrategy instead")
152153
public struct ConcurrentDispatchFieldExecutionStrategy: QueryFieldExecutionStrategy,
153154
SubscriptionFieldExecutionStrategy
154155
{
@@ -224,6 +225,37 @@ public struct ConcurrentDispatchFieldExecutionStrategy: QueryFieldExecutionStrat
224225
}
225226
}
226227

228+
/**
229+
* Serial field execution strategy that's suitable for the "Evaluating selection sets" section of the spec for "read" mode.
230+
*/
231+
public struct ConcurrentFieldExecutionStrategy: QueryFieldExecutionStrategy,
232+
SubscriptionFieldExecutionStrategy {
233+
public init() {}
234+
235+
public func executeFields(
236+
exeContext: ExecutionContext,
237+
parentType: GraphQLObjectType,
238+
sourceValue: Any,
239+
path: IndexPath,
240+
fields: OrderedDictionary<String, [Field]>
241+
) throws -> Future<OrderedDictionary<String, Any>> {
242+
var results = OrderedDictionary<String, Future<Any>?>(minimumCapacity: fields.count)
243+
for field in fields {
244+
let fieldASTs = field.value
245+
let fieldKey = field.key
246+
let fieldPath = path.appending(fieldKey)
247+
results[fieldKey] = try resolveField(
248+
exeContext: exeContext,
249+
parentType: parentType,
250+
source: sourceValue,
251+
fieldASTs: fieldASTs,
252+
path: fieldPath
253+
).map { $0 ?? Map.null }
254+
}
255+
return results.compactMapValues { $0 }.flatten(on: exeContext.eventLoopGroup)
256+
}
257+
}
258+
227259
/**
228260
* Implements the "Evaluating requests" section of the GraphQL specification.
229261
*

Tests/GraphQLTests/FieldExecutionStrategyTests/FieldExecutionStrategyTests.swift

+45
Original file line numberDiff line numberDiff line change
@@ -313,4 +313,49 @@ class FieldExecutionStrategyTests: XCTestCase {
313313
}
314314
// XCTAssertEqualWithAccuracy(0.1, result.seconds, accuracy: 0.25)
315315
}
316+
317+
func testConcurrentFieldExecutionStrategyWithSingleField() throws {
318+
let result = try timing(graphql(
319+
queryStrategy: ConcurrentFieldExecutionStrategy(),
320+
schema: schema,
321+
request: singleQuery,
322+
eventLoopGroup: eventLoopGroup
323+
).wait())
324+
XCTAssertEqual(result.value, singleExpected)
325+
}
326+
327+
func testConcurrentFieldExecutionStrategyWithSingleFieldError() throws {
328+
let result = try timing(graphql(
329+
queryStrategy: ConcurrentFieldExecutionStrategy(),
330+
schema: schema,
331+
request: singleThrowsQuery,
332+
eventLoopGroup: eventLoopGroup
333+
).wait())
334+
XCTAssertEqual(result.value, singleThrowsExpected)
335+
}
336+
337+
func testConcurrentFieldExecutionStrategyWithMultipleFields() throws {
338+
let result = try timing(graphql(
339+
queryStrategy: ConcurrentFieldExecutionStrategy(),
340+
schema: schema,
341+
request: multiQuery,
342+
eventLoopGroup: eventLoopGroup
343+
).wait())
344+
XCTAssertEqual(result.value, multiExpected)
345+
}
346+
347+
func testConcurrentFieldExecutionStrategyWithMultipleFieldErrors() throws {
348+
let result = try timing(graphql(
349+
queryStrategy: ConcurrentFieldExecutionStrategy(),
350+
schema: schema,
351+
request: multiThrowsQuery,
352+
eventLoopGroup: eventLoopGroup
353+
).wait())
354+
XCTAssertEqual(result.value.data, multiThrowsExpectedData)
355+
let resultErrors = result.value.errors
356+
XCTAssertEqual(resultErrors.count, multiThrowsExpectedErrors.count)
357+
for m in multiThrowsExpectedErrors {
358+
XCTAssertTrue(resultErrors.contains(m), "Expecting result errors to contain \(m)")
359+
}
360+
}
316361
}

0 commit comments

Comments
 (0)