@@ -6,8 +6,9 @@ public enum DataLoaderFutureValue<T> {
6
6
case failure( Error )
7
7
}
8
8
9
- public typealias BatchLoadFunction < Key, Value> = ( _ keys: [ Key ] ) throws -> EventLoopFuture < [ DataLoaderFutureValue < Value > ] >
10
- private typealias LoaderQueue < Key, Value> = Array < ( key: Key , promise: EventLoopPromise < Value > ) >
9
+ public typealias BatchLoadFunction < Key, Value> = ( _ keys: [ Key ] ) throws
10
+ -> EventLoopFuture < [ DataLoaderFutureValue < Value > ] >
11
+ private typealias LoaderQueue < Key, Value> = [ ( key: Key , promise: EventLoopPromise < Value > ) ]
11
12
12
13
/// DataLoader creates a public API for loading data from a particular
13
14
/// data back-end with unique keys such as the id column of a SQL table
@@ -17,14 +18,13 @@ private typealias LoaderQueue<Key, Value> = Array<(key: Key, promise: EventLoopP
17
18
/// when used in long-lived applications or those which serve many users
18
19
/// with different access permissions and consider creating a new instance
19
20
/// per data request.
20
- final public class DataLoader < Key: Hashable , Value> {
21
-
21
+ public final class DataLoader < Key: Hashable , Value> {
22
22
private let batchLoadFunction : BatchLoadFunction < Key , Value >
23
23
private let options : DataLoaderOptions < Key , Value >
24
24
25
25
private var cache = [ Key : EventLoopFuture < Value > ] ( )
26
26
private var queue = LoaderQueue < Key , Value > ( )
27
-
27
+
28
28
private var dispatchScheduled = false
29
29
private let lock = Lock ( )
30
30
@@ -39,7 +39,7 @@ final public class DataLoader<Key: Hashable, Value> {
39
39
/// Loads a key, returning an `EventLoopFuture` for the value represented by that key.
40
40
public func load( key: Key , on eventLoopGroup: EventLoopGroup ) throws -> EventLoopFuture < Value > {
41
41
let cacheKey = options. cacheKeyFunction ? ( key) ?? key
42
-
42
+
43
43
return lock. withLock {
44
44
if options. cachingEnabled, let cachedFuture = cache [ cacheKey] {
45
45
return cachedFuture
@@ -57,14 +57,18 @@ final public class DataLoader<Key: Hashable, Value> {
57
57
}
58
58
} else {
59
59
do {
60
- _ = try batchLoadFunction ( [ key] ) . map { results in
60
+ _ = try batchLoadFunction ( [ key] ) . map { results in
61
61
if results. isEmpty {
62
- promise. fail ( DataLoaderError . noValueForKey ( " Did not return value for key: \( key) " ) )
62
+ promise
63
+ . fail (
64
+ DataLoaderError
65
+ . noValueForKey ( " Did not return value for key: \( key) " )
66
+ )
63
67
} else {
64
68
let result = results [ 0 ]
65
69
switch result {
66
- case . success( let value) : promise. succeed ( value)
67
- case . failure( let error) : promise. fail ( error)
70
+ case let . success( value) : promise. succeed ( value)
71
+ case let . failure( error) : promise. fail ( error)
68
72
}
69
73
}
70
74
}
@@ -82,7 +86,7 @@ final public class DataLoader<Key: Hashable, Value> {
82
86
return future
83
87
}
84
88
}
85
-
89
+
86
90
/// Loads multiple keys, promising an array of values:
87
91
///
88
92
/// ```
@@ -97,14 +101,17 @@ final public class DataLoader<Key: Hashable, Value> {
97
101
/// myLoader.load(key: "b", on: eventLoopGroup)
98
102
/// ].flatten(on: eventLoopGroup).wait()
99
103
/// ```
100
- public func loadMany( keys: [ Key ] , on eventLoopGroup: EventLoopGroup ) throws -> EventLoopFuture < [ Value ] > {
104
+ public func loadMany(
105
+ keys: [ Key ] ,
106
+ on eventLoopGroup: EventLoopGroup
107
+ ) throws -> EventLoopFuture < [ Value ] > {
101
108
guard !keys. isEmpty else {
102
109
return eventLoopGroup. next ( ) . makeSucceededFuture ( [ ] )
103
110
}
104
111
let futures = try keys. map { try load ( key: $0, on: eventLoopGroup) }
105
112
return EventLoopFuture . whenAllSucceed ( futures, on: eventLoopGroup. next ( ) )
106
113
}
107
-
114
+
108
115
/// Clears the value at `key` from the cache, if it exists. Returns itself for
109
116
/// method chaining.
110
117
@discardableResult
@@ -115,7 +122,7 @@ final public class DataLoader<Key: Hashable, Value> {
115
122
}
116
123
return self
117
124
}
118
-
125
+
119
126
/// Clears the entire cache. To be used when some event results in unknown
120
127
/// invalidations across this particular `DataLoader`. Returns itself for
121
128
/// method chaining.
@@ -130,9 +137,13 @@ final public class DataLoader<Key: Hashable, Value> {
130
137
/// Adds the provied key and value to the cache. If the key already exists, no
131
138
/// change is made. Returns itself for method chaining.
132
139
@discardableResult
133
- public func prime( key: Key , value: Value , on eventLoop: EventLoopGroup ) -> DataLoader < Key , Value > {
140
+ public func prime(
141
+ key: Key ,
142
+ value: Value ,
143
+ on eventLoop: EventLoopGroup
144
+ ) -> DataLoader < Key , Value > {
134
145
let cacheKey = options. cacheKeyFunction ? ( key) ?? key
135
-
146
+
136
147
lock. withLockVoid {
137
148
if cache [ cacheKey] == nil {
138
149
let promise : EventLoopPromise < Value > = eventLoop. next ( ) . makePromise ( )
@@ -160,27 +171,27 @@ final public class DataLoader<Key: Hashable, Value> {
160
171
dispatchScheduled = false
161
172
}
162
173
}
163
-
174
+
164
175
guard batch. count > 0 else {
165
176
return ( )
166
177
}
167
178
168
179
// If a maxBatchSize was provided and the queue is longer, then segment the
169
180
// queue into multiple batches, otherwise treat the queue as a single batch.
170
- if let maxBatchSize = options. maxBatchSize, maxBatchSize > 0 && maxBatchSize < batch. count {
171
- for i in 0 ... ( batch. count / maxBatchSize) {
181
+ if let maxBatchSize = options. maxBatchSize, maxBatchSize > 0 , maxBatchSize < batch. count {
182
+ for i in 0 ... ( batch. count / maxBatchSize) {
172
183
let startIndex = i * maxBatchSize
173
184
let endIndex = ( i + 1 ) * maxBatchSize
174
- let slicedBatch = batch [ startIndex..< min ( endIndex, batch. count) ]
185
+ let slicedBatch = batch [ startIndex ..< min ( endIndex, batch. count) ]
175
186
try executeBatch ( batch: Array ( slicedBatch) )
176
187
}
177
188
} else {
178
- try executeBatch ( batch: batch)
189
+ try executeBatch ( batch: batch)
179
190
}
180
191
}
181
-
192
+
182
193
private func executeBatch( batch: LoaderQueue < Key , Value > ) throws {
183
- let keys = batch. map { $0 . key }
194
+ let keys = batch. map ( \ . key)
184
195
185
196
if keys. isEmpty {
186
197
return
@@ -191,22 +202,25 @@ final public class DataLoader<Key: Hashable, Value> {
191
202
do {
192
203
_ = try batchLoadFunction ( keys) . flatMapThrowing { values in
193
204
if values. count != keys. count {
194
- throw DataLoaderError . typeError ( " The function did not return an array of the same length as the array of keys. \n Keys count: \( keys. count) \n Values count: \( values. count) " )
205
+ throw DataLoaderError
206
+ . typeError (
207
+ " The function did not return an array of the same length as the array of keys. \n Keys count: \( keys. count) \n Values count: \( values. count) "
208
+ )
195
209
}
196
210
197
211
for entry in batch. enumerated ( ) {
198
212
let result = values [ entry. offset]
199
213
200
214
switch result {
201
- case . failure( let error) : entry. element. promise. fail ( error)
202
- case . success( let value) : entry. element. promise. succeed ( value)
215
+ case let . failure( error) : entry. element. promise. fail ( error)
216
+ case let . success( value) : entry. element. promise. succeed ( value)
203
217
}
204
218
}
205
219
} . recover { error in
206
220
self . failedExecution ( batch: batch, error: error)
207
221
}
208
222
} catch {
209
- self . failedExecution ( batch: batch, error: error)
223
+ failedExecution ( batch: batch, error: error)
210
224
}
211
225
}
212
226
@@ -220,48 +234,49 @@ final public class DataLoader<Key: Hashable, Value> {
220
234
221
235
#if compiler(>=5.5) && canImport(_Concurrency)
222
236
223
- /// Batch load function using async await
224
- public typealias ConcurrentBatchLoadFunction < Key, Value> = @Sendable ( _ keys: [ Key ] ) async throws -> [ DataLoaderFutureValue < Value > ]
237
+ /// Batch load function using async await
238
+ public typealias ConcurrentBatchLoadFunction < Key, Value> =
239
+ @Sendable ( _ keys: [ Key ] ) async throws -> [ DataLoaderFutureValue < Value > ]
225
240
226
- public extension DataLoader {
227
- @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
228
- convenience init (
229
- on eventLoop: EventLoop ,
230
- options: DataLoaderOptions < Key , Value > = DataLoaderOptions ( ) ,
231
- throwing asyncThrowingLoadFunction: @escaping ConcurrentBatchLoadFunction < Key , Value >
232
- ) {
233
- self . init ( options: options, batchLoadFunction: { keys in
234
- let promise = eventLoop. next ( ) . makePromise ( of: [ DataLoaderFutureValue < Value > ] . self)
235
- promise. completeWithTask {
236
- try await asyncThrowingLoadFunction ( keys)
237
- }
238
- return promise. futureResult
239
- } )
240
- }
241
+ public extension DataLoader {
242
+ @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
243
+ convenience init (
244
+ on eventLoop: EventLoop ,
245
+ options: DataLoaderOptions < Key , Value > = DataLoaderOptions ( ) ,
246
+ throwing asyncThrowingLoadFunction: @escaping ConcurrentBatchLoadFunction < Key , Value >
247
+ ) {
248
+ self . init ( options: options, batchLoadFunction: { keys in
249
+ let promise = eventLoop. next ( ) . makePromise ( of: [ DataLoaderFutureValue < Value > ] . self)
250
+ promise. completeWithTask {
251
+ try await asyncThrowingLoadFunction ( keys)
252
+ }
253
+ return promise. futureResult
254
+ } )
255
+ }
241
256
242
- /// Asynchronously loads a key, returning the value represented by that key.
243
- @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
244
- func load( key: Key , on eventLoopGroup: EventLoopGroup ) async throws -> Value {
245
- try await load ( key: key, on: eventLoopGroup) . get ( )
246
- }
257
+ /// Asynchronously loads a key, returning the value represented by that key.
258
+ @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
259
+ func load( key: Key , on eventLoopGroup: EventLoopGroup ) async throws -> Value {
260
+ try await load ( key: key, on: eventLoopGroup) . get ( )
261
+ }
247
262
248
- /// Asynchronously loads multiple keys, promising an array of values:
249
- ///
250
- /// ```
251
- /// let aAndB = try await myLoader.loadMany(keys: [ "a", "b" ], on: eventLoopGroup)
252
- /// ```
253
- ///
254
- /// This is equivalent to the more verbose:
255
- ///
256
- /// ```
257
- /// async let a = myLoader.load(key: "a", on: eventLoopGroup)
258
- /// async let b = myLoader.load(key: "b", on: eventLoopGroup)
259
- /// let aAndB = try await a + b
260
- /// ```
261
- @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
262
- func loadMany( keys: [ Key ] , on eventLoopGroup: EventLoopGroup ) async throws -> [ Value ] {
263
- try await loadMany ( keys: keys, on: eventLoopGroup) . get ( )
263
+ /// Asynchronously loads multiple keys, promising an array of values:
264
+ ///
265
+ /// ```
266
+ /// let aAndB = try await myLoader.loadMany(keys: [ "a", "b" ], on: eventLoopGroup)
267
+ /// ```
268
+ ///
269
+ /// This is equivalent to the more verbose:
270
+ ///
271
+ /// ```
272
+ /// async let a = myLoader.load(key: "a", on: eventLoopGroup)
273
+ /// async let b = myLoader.load(key: "b", on: eventLoopGroup)
274
+ /// let aAndB = try await a + b
275
+ /// ```
276
+ @available ( macOS 12 , iOS 15 , watchOS 8 , tvOS 15 , * )
277
+ func loadMany( keys: [ Key ] , on eventLoopGroup: EventLoopGroup ) async throws -> [ Value ] {
278
+ try await loadMany ( keys: keys, on: eventLoopGroup) . get ( )
279
+ }
264
280
}
265
- }
266
281
267
- #endif
282
+ #endif
0 commit comments