Skip to content

Commit 237dfc0

Browse files
refactor(perf): Adds fieldCache and interfaceCache
Improves benchmarked `graphql` time by 22%
1 parent f1745db commit 237dfc0

1 file changed

Lines changed: 92 additions & 22 deletions

File tree

Sources/GraphQL/Type/Definition.swift

Lines changed: 92 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,35 @@ extension GraphQLScalarType: Hashable {
275275
public final class GraphQLObjectType: @unchecked Sendable {
276276
public let name: String
277277
public let description: String?
278+
278279
// While technically not sendable, fields and interfaces should not be mutated after schema
279280
// creation.
280-
public var fields: () throws -> GraphQLFieldMap
281-
public var interfaces: () throws -> [GraphQLInterfaceType]
281+
public var fields: () throws -> GraphQLFieldMap {
282+
get {
283+
fieldFunc
284+
}
285+
set {
286+
fieldFunc = newValue
287+
// Clear the cache when setting a new function
288+
fieldCache = nil
289+
}
290+
}
291+
private var fieldFunc: () throws -> GraphQLFieldMap
292+
private var fieldCache: GraphQLFieldDefinitionMap? = nil
293+
294+
public var interfaces: () throws -> [GraphQLInterfaceType] {
295+
get {
296+
interfaceFunc
297+
}
298+
set {
299+
interfaceFunc = newValue
300+
// Clear the cache when setting a new function
301+
interfaceCache = nil
302+
}
303+
}
304+
private var interfaceFunc: () throws -> [GraphQLInterfaceType]
305+
private var interfaceCache: [GraphQLInterfaceType]? = nil
306+
282307
public let isTypeOf: GraphQLIsTypeOf?
283308
public let astNode: ObjectTypeDefinition?
284309
public let extensionASTNodes: [TypeExtensionDefinition]
@@ -296,8 +321,8 @@ public final class GraphQLObjectType: @unchecked Sendable {
296321
try assertValid(name: name)
297322
self.name = name
298323
self.description = description
299-
self.fields = { fields }
300-
self.interfaces = { interfaces }
324+
self.fieldFunc = { fields }
325+
self.interfaceFunc = { interfaces }
301326
self.isTypeOf = isTypeOf
302327
self.astNode = astNode
303328
self.extensionASTNodes = extensionASTNodes
@@ -315,22 +340,32 @@ public final class GraphQLObjectType: @unchecked Sendable {
315340
try assertValid(name: name)
316341
self.name = name
317342
self.description = description
318-
self.fields = fields
319-
self.interfaces = interfaces
343+
self.fieldFunc = fields
344+
self.interfaceFunc = interfaces
320345
self.isTypeOf = isTypeOf
321346
self.astNode = astNode
322347
self.extensionASTNodes = extensionASTNodes
323348
}
324349

325350
func getFields() throws -> GraphQLFieldDefinitionMap {
326-
try defineFieldMap(
327-
name: name,
328-
fields: fields()
329-
)
351+
// Cache on the first call
352+
return try self.fieldCache ?? {
353+
let fields = try defineFieldMap(
354+
name: name,
355+
fields: fields()
356+
)
357+
self.fieldCache = fields
358+
return fields
359+
}()
330360
}
331361

332362
func getInterfaces() throws -> [GraphQLInterfaceType] {
333-
return try interfaces()
363+
// Cache on the first call
364+
return try self.interfaceCache ?? {
365+
let interfaces = try interfaces()
366+
self.interfaceCache = interfaces
367+
return interfaces
368+
}()
334369
}
335370
}
336371

@@ -657,10 +692,35 @@ public final class GraphQLInterfaceType: @unchecked Sendable {
657692
public let name: String
658693
public let description: String?
659694
public let resolveType: GraphQLTypeResolve?
695+
660696
// While technically not sendable, fields and interfaces should not be mutated after schema
661697
// creation.
662-
public var fields: () throws -> GraphQLFieldMap
663-
public var interfaces: () throws -> [GraphQLInterfaceType]
698+
public var fields: () throws -> GraphQLFieldMap {
699+
get {
700+
fieldFunc
701+
}
702+
set {
703+
fieldFunc = newValue
704+
// Clear the cache when setting a new function
705+
fieldCache = nil
706+
}
707+
}
708+
private var fieldFunc: () throws -> GraphQLFieldMap
709+
private var fieldCache: GraphQLFieldDefinitionMap? = nil
710+
711+
public var interfaces: () throws -> [GraphQLInterfaceType] {
712+
get {
713+
interfaceFunc
714+
}
715+
set {
716+
interfaceFunc = newValue
717+
// Clear the cache when setting a new function
718+
interfaceCache = nil
719+
}
720+
}
721+
private var interfaceFunc: () throws -> [GraphQLInterfaceType]
722+
private var interfaceCache: [GraphQLInterfaceType]? = nil
723+
664724
public let astNode: InterfaceTypeDefinition?
665725
public let extensionASTNodes: [InterfaceExtensionDefinition]
666726
public let kind: TypeKind = .interface
@@ -677,8 +737,8 @@ public final class GraphQLInterfaceType: @unchecked Sendable {
677737
try assertValid(name: name)
678738
self.name = name
679739
self.description = description
680-
self.fields = { fields }
681-
self.interfaces = { interfaces }
740+
self.fieldFunc = { fields }
741+
self.interfaceFunc = { interfaces }
682742
self.resolveType = resolveType
683743
self.astNode = astNode
684744
self.extensionASTNodes = extensionASTNodes
@@ -696,22 +756,32 @@ public final class GraphQLInterfaceType: @unchecked Sendable {
696756
try assertValid(name: name)
697757
self.name = name
698758
self.description = description
699-
self.fields = fields
700-
self.interfaces = interfaces
759+
self.fieldFunc = fields
760+
self.interfaceFunc = interfaces
701761
self.resolveType = resolveType
702762
self.astNode = astNode
703763
self.extensionASTNodes = extensionASTNodes
704764
}
705765

706766
func getFields() throws -> GraphQLFieldDefinitionMap {
707-
try defineFieldMap(
708-
name: name,
709-
fields: fields()
710-
)
767+
// Cache on the first call
768+
return try self.fieldCache ?? {
769+
let fields = try defineFieldMap(
770+
name: name,
771+
fields: fields()
772+
)
773+
self.fieldCache = fields
774+
return fields
775+
}()
711776
}
712777

713778
func getInterfaces() throws -> [GraphQLInterfaceType] {
714-
return try interfaces()
779+
// Cache on the first call
780+
return try self.interfaceCache ?? {
781+
let interfaces = try interfaces()
782+
self.interfaceCache = interfaces
783+
return interfaces
784+
}()
715785
}
716786
}
717787

0 commit comments

Comments
 (0)