Skip to content

Commit 1ce6154

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

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

Sources/GraphQL/Execution/Execute.swift

+31
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,36 @@ 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, SubscriptionFieldExecutionStrategy {
232+
public init() {}
233+
234+
public func executeFields(
235+
exeContext: ExecutionContext,
236+
parentType: GraphQLObjectType,
237+
sourceValue: Any,
238+
path: IndexPath,
239+
fields: OrderedDictionary<String, [Field]>
240+
) throws -> Future<OrderedDictionary<String, Any>> {
241+
var results = OrderedDictionary<String, Future<Any>?>.init(minimumCapacity: fields.count)
242+
for field in fields {
243+
let fieldASTs = field.value
244+
let fieldKey = field.key
245+
let fieldPath = path.appending(fieldKey)
246+
results[fieldKey] = try resolveField(
247+
exeContext: exeContext,
248+
parentType: parentType,
249+
source: sourceValue,
250+
fieldASTs: fieldASTs,
251+
path: fieldPath
252+
).map { $0 ?? Map.null }
253+
}
254+
return results.compactMapValues { $0 }.flatten(on: exeContext.eventLoopGroup)
255+
}
256+
}
257+
227258
/**
228259
* Implements the "Evaluating requests" section of the GraphQL specification.
229260
*

Tests/GraphQLTests/FieldExecutionStrategyTests/FieldExecutionStrategyTests.swift

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

0 commit comments

Comments
 (0)