1
1
import NIO
2
+ import NIOConcurrencyHelpers
2
3
3
4
public enum DataLoaderFutureValue < T> {
4
5
case success( T )
@@ -23,6 +24,8 @@ final public class DataLoader<Key: Hashable, Value> {
23
24
24
25
private var cache = [ Key : EventLoopFuture < Value > ] ( )
25
26
private var queue = LoaderQueue < Key , Value > ( )
27
+
28
+ private let lock = Lock ( )
26
29
27
30
public init ( options: DataLoaderOptions < Key , Value > = DataLoaderOptions ( ) , batchLoadFunction: @escaping BatchLoadFunction < Key , Value > ) {
28
31
self . options = options
@@ -32,36 +35,38 @@ final public class DataLoader<Key: Hashable, Value> {
32
35
/// Loads a key, returning an `EventLoopFuture` for the value represented by that key.
33
36
public func load( key: Key , on eventLoopGroup: EventLoopGroup ) throws -> EventLoopFuture < Value > {
34
37
let cacheKey = options. cacheKeyFunction ? ( key) ?? key
38
+
39
+ return try lock. withLock {
40
+ if options. cachingEnabled, let cachedFuture = cache [ cacheKey] {
41
+ return cachedFuture
42
+ }
35
43
36
- if options. cachingEnabled, let cachedFuture = cache [ cacheKey] {
37
- return cachedFuture
38
- }
39
-
40
- let promise : EventLoopPromise < Value > = eventLoopGroup. next ( ) . makePromise ( )
41
-
42
- if options. batchingEnabled {
43
- queue. append ( ( key: key, promise: promise) )
44
- } else {
45
- _ = try batchLoadFunction ( [ key] ) . map { results in
46
- if results. isEmpty {
47
- promise. fail ( DataLoaderError . noValueForKey ( " Did not return value for key: \( key) " ) )
48
- } else {
49
- let result = results [ 0 ]
50
- switch result {
51
- case . success( let value) : promise. succeed ( value)
52
- case . failure( let error) : promise. fail ( error)
44
+ let promise : EventLoopPromise < Value > = eventLoopGroup. next ( ) . makePromise ( )
45
+
46
+ if options. batchingEnabled {
47
+ queue. append ( ( key: key, promise: promise) )
48
+ } else {
49
+ _ = try batchLoadFunction ( [ key] ) . map { results in
50
+ if results. isEmpty {
51
+ promise. fail ( DataLoaderError . noValueForKey ( " Did not return value for key: \( key) " ) )
52
+ } else {
53
+ let result = results [ 0 ]
54
+ switch result {
55
+ case . success( let value) : promise. succeed ( value)
56
+ case . failure( let error) : promise. fail ( error)
57
+ }
53
58
}
54
59
}
55
60
}
56
- }
57
61
58
- let future = promise. futureResult
62
+ let future = promise. futureResult
59
63
60
- if options. cachingEnabled {
61
- cache [ cacheKey] = future
62
- }
64
+ if options. cachingEnabled {
65
+ cache [ cacheKey] = future
66
+ }
63
67
64
- return future
68
+ return future
69
+ }
65
70
}
66
71
67
72
/// Loads multiple keys, promising an array of values:
@@ -107,7 +112,9 @@ final public class DataLoader<Key: Hashable, Value> {
107
112
@discardableResult
108
113
func clear( key: Key ) -> DataLoader < Key , Value > {
109
114
let cacheKey = options. cacheKeyFunction ? ( key) ?? key
110
- cache. removeValue ( forKey: cacheKey)
115
+ lock. withLockVoid {
116
+ cache. removeValue ( forKey: cacheKey)
117
+ }
111
118
return self
112
119
}
113
120
@@ -116,7 +123,9 @@ final public class DataLoader<Key: Hashable, Value> {
116
123
/// method chaining.
117
124
@discardableResult
118
125
func clearAll( ) -> DataLoader < Key , Value > {
119
- cache. removeAll ( )
126
+ lock. withLockVoid {
127
+ cache. removeAll ( )
128
+ }
120
129
return self
121
130
}
122
131
@@ -125,12 +134,14 @@ final public class DataLoader<Key: Hashable, Value> {
125
134
@discardableResult
126
135
func prime( key: Key , value: Value , on eventLoop: EventLoopGroup ) -> DataLoader < Key , Value > {
127
136
let cacheKey = options. cacheKeyFunction ? ( key) ?? key
137
+
138
+ lock. withLockVoid {
139
+ if cache [ cacheKey] == nil {
140
+ let promise : EventLoopPromise < Value > = eventLoop. next ( ) . makePromise ( )
141
+ promise. succeed ( value)
128
142
129
- if cache [ cacheKey] == nil {
130
- let promise : EventLoopPromise < Value > = eventLoop. next ( ) . makePromise ( )
131
- promise. succeed ( value)
132
-
133
- cache [ cacheKey] = promise. futureResult
143
+ cache [ cacheKey] = promise. futureResult
144
+ }
134
145
}
135
146
136
147
return self
@@ -140,9 +151,11 @@ final public class DataLoader<Key: Hashable, Value> {
140
151
///
141
152
/// The client must run this manually to compete the `EventLoopFutures` of the keys.
142
153
public func execute( ) throws {
143
- // Take the current loader queue, replacing it with an empty queue.
144
- let batch = self . queue
145
- self . queue = [ ]
154
+ var batch = LoaderQueue < Key , Value > ( )
155
+ lock. withLockVoid {
156
+ batch = self . queue
157
+ self . queue = [ ]
158
+ }
146
159
147
160
// If a maxBatchSize was provided and the queue is longer, then segment the
148
161
// queue into multiple batches, otherwise treat the queue as a single batch.
0 commit comments