You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To get started, create a DataLoader. Each DataLoader instance represents a unique cache. Typically instances are created per request when used
20
-
within a web-server if different users can see different things.
19
+
The `AsyncDataLoader` library is preferred. The `DataLoader` uses NIO for concurrency and is provided for backwards compatibility.
20
+
21
+
To get started, create a DataLoader. Each DataLoader instance represents a unique cache. Typically instances are created per request when used within a web-server if different users can see different things.
21
22
22
23
## Batching 🍪
23
-
Batching is not an advanced feature, it's DataLoader's primary feature.
24
+
Batching is not an advanced feature, it's DataLoader's primary feature. Create a DataLoader by providing a batch loading function:
24
25
25
-
Create a DataLoader by providing a batch loading function:
26
26
```swift
27
-
let userLoader = Dataloader<Int, User>(batchLoadFunction: { keys in
27
+
importAsyncDataLoader
28
+
29
+
let userLoader = DataLoader<Int, User>(batchLoadFunction: { keys in
28
30
try User.query(on: req).filter(\User.id~~ keys).all().map { users in
@@ -33,86 +35,65 @@ let userLoader = Dataloader<Int, User>(batchLoadFunction: { keys in
33
35
})
34
36
```
35
37
36
-
The order of the returned DataLoaderFutureValues must match the order of the keys.
38
+
The order of the returned DataLoaderFutureValues must match the order of the input keys.
37
39
38
40
### Load individual keys
39
41
```swift
40
-
letfuture1=tryuserLoader.load(key: 1, on: eventLoopGroup)
41
-
letfuture2=tryuserLoader.load(key: 2, on: eventLoopGroup)
42
-
letfuture3=tryuserLoader.load(key: 1, on: eventLoopGroup)
42
+
asyncletresult1= userLoader.load(key: 1)
43
+
asyncletresult2= userLoader.load(key: 2)
44
+
asyncletresult3= userLoader.load(key: 1)
43
45
```
44
46
45
47
The example above will only fetch two users, because the user with key `1` is present twice in the list.
46
48
47
49
### Load multiple keys
48
50
There is also a method to load multiple keys at once
49
51
```swift
50
-
try userLoader.loadMany(keys: [1, 2, 3], on: eventLoopGroup)
52
+
tryawaituserLoader.loadMany(keys: [1, 2, 3])
51
53
```
52
54
53
55
### Execution
54
-
By default, a DataLoader will wait for a short time from the moment `load` is called to collect keys prior
55
-
to running the `batchLoadFunction` and completing the `load` futures. This is to let keys accumulate and
56
-
batch into a smaller number of total requests. This amount of time is configurable using the `executionPeriod`
57
-
option:
56
+
By default, a DataLoader will wait for a short time from the moment `load` is called to collect keys prior to running the `batchLoadFunction` and completing the `load` results. This allows keys to accumulate and batch into a smaller number of total requests. This amount of time is configurable using the `executionPeriod` option:
Longer execution periods reduce the number of total data requests, but also reduce the responsiveness of the
69
-
`load` futures.
67
+
Longer execution periods reduce the number of total data requests, but also reduce the responsiveness of the `load` futures.
70
68
71
-
If desired, you can manually execute the `batchLoadFunction` and complete the futures at any time, using the
72
-
`.execute()` method.
69
+
If desired, you can manually execute the `batchLoadFunction` and complete the futures at any time, using the `.execute()` method.
73
70
74
-
Scheduled execution can be disabled by setting `executionPeriod` to `nil`, but be careful - you *must* call `.execute()`
75
-
manually in this case. Otherwise, the futures will never complete!
71
+
Scheduled execution can be disabled by setting `executionPeriod` to `nil`, but be careful - you *must* call `.execute()` manually in this case. Otherwise, the futures will never complete!
76
72
77
73
### Disable batching
78
-
It is possible to disable batching by setting the `batchingEnabled` option to `false`.
79
-
In this case, the `batchLoadFunction` will be invoked immediately when a key is loaded.
80
-
74
+
It is possible to disable batching by setting `batchingEnabled` to `false`. In this case, the `batchLoadFunction` will be invoked immediately when a key is loaded.
81
75
82
76
## Caching 💰
83
-
DataLoader provides a memoization cache. After `.load()` is called with a key, the resulting value is cached
84
-
for the lifetime of the DataLoader object. This eliminates redundant loads.
77
+
DataLoader provides a memoization cache. After `.load()` is called with a key, the resulting value is cached for the lifetime of the DataLoader object. This eliminates redundant loads.
85
78
86
-
In addition to relieving pressure on your data storage, caching results also creates fewer objects which may
87
-
relieve memory pressure on your application:
79
+
In addition to relieving pressure on your data storage, caching results also creates fewer objects which may relieve memory pressure on your application:
88
80
89
81
```swift
90
82
let userLoader = DataLoader<Int, Int>(...)
91
-
letfuture1= userLoader.load(key: 1, on: eventLoopGroup)
92
-
letfuture2= userLoader.load(key: 1, on: eventLoopGroup)
93
-
print(future1==future2) // true
83
+
asyncletresult1= userLoader.load(key: 1)
84
+
asyncletresult2= userLoader.load(key: 1)
85
+
awaitprint(result1==result2) // true
94
86
```
95
87
96
88
### Caching per-Request
89
+
DataLoader caching *does not* replace Redis, Memcache, or any other shared application-level cache. DataLoader is first and foremost a data loading mechanism, and its cache only serves the purpose of not repeatedly loading the same data in the context of a single request to your Application. To do this, it maintains a simple in-memory memoization cache (more accurately: `.load()` is a memoized function).
97
90
98
-
DataLoader caching *does not* replace Redis, Memcache, or any other shared
99
-
application-level cache. DataLoader is first and foremost a data loading mechanism,
100
-
and its cache only serves the purpose of not repeatedly loading the same data in
101
-
the context of a single request to your Application. To do this, it maintains a
102
-
simple in-memory memoization cache (more accurately: `.load()` is a memoized function).
103
-
104
-
Avoid multiple requests from different users using the DataLoader instance, which
105
-
could result in cached data incorrectly appearing in each request. Typically,
106
-
DataLoader instances are created when a Request begins, and are not used once the
107
-
Request ends.
91
+
Avoid multiple requests from different users using the DataLoader instance, which could result in cached data incorrectly appearing in each request. Typically, DataLoader instances are created when a Request begins, and are not used once the Request ends.
108
92
109
93
### Clearing Cache
110
-
111
94
In certain uncommon cases, clearing the request cache may be necessary.
112
95
113
-
The most common example when clearing the loader's cache is necessary is after
114
-
a mutation or update within the same request, when a cached value could be out of
115
-
date and future loads should not use any possibly cached value.
96
+
The most common example when clearing the loader's cache is necessary is after a mutation or update within the same request, when a cached value could be out of date and future loads should not use any possibly cached value.
116
97
117
98
Here's a simple example using SQL UPDATE to illustrate.
118
99
@@ -121,84 +102,68 @@ Here's a simple example using SQL UPDATE to illustrate.
121
102
let userLoader = DataLoader<Int, Int>(...)
122
103
123
104
// And a value happens to be loaded (and cached).
124
-
userLoader.load(key: 4, on: eventLoopGroup)
105
+
tryawaituserLoader.load(key: 4)
125
106
126
107
// A mutation occurs, invalidating what might be in cache.
127
-
sqlRun('UPDATE users WHERE id=4 SET username="zuck"').whenComplete { userLoader.clear(key: 4) }
108
+
tryawaitsqlRun('UPDATE users WHERE id=4 SET username="zuck"')
109
+
await userLoader.clear(key: 4)
128
110
129
111
// Later the value load is loaded again so the mutated data appears.
130
-
userLoader.load(key: 4, on: eventLoopGroup)
112
+
tryawaituserLoader.load(key: 4)
131
113
132
114
// Request completes.
133
115
```
134
116
135
117
### Caching Errors
136
-
137
-
If a batch load fails (that is, a batch function throws or returns a DataLoaderFutureValue.failure(Error)),
138
-
then the requested values will not be cached. However if a batch
139
-
function returns an `Error` instance for an individual value, that `Error` will
140
-
be cached to avoid frequently loading the same `Error`.
118
+
If a batch load fails (that is, a batch function throws or returns a DataLoaderFutureValue.failure(Error)), then the requested values will not be cached. However if a batch function returns an `Error` instance for an individual value, that `Error` will be cached to avoid frequently loading the same `Error`.
141
119
142
120
In some circumstances you may wish to clear the cache for these individual Errors:
143
121
144
122
```swift
145
-
userLoader.load(key: 1, on: eventLoopGroup).whenFailure { error in
123
+
do {
124
+
tryawait userLoader.load(key: 1)
125
+
} catch {
146
126
if (/* determine if should clear error */) {
147
-
userLoader.clear(key: 1);
127
+
awaituserLoader.clear(key: 1);
148
128
}
149
129
throw error
150
130
}
151
131
```
152
132
153
133
### Disabling Cache
134
+
In certain uncommon cases, a DataLoader which *does not* cache may be desirable. Calling `DataLoader(options: DataLoaderOptions(cachingEnabled: false), batchLoadFunction: batchLoadFunction)` will ensure that every call to `.load()` will produce a *new* Future, and previously requested keys will not be saved in memory.
154
135
155
-
In certain uncommon cases, a DataLoader which *does not* cache may be desirable.
156
-
Calling `DataLoader(options: DataLoaderOptions(cachingEnabled: false), batchLoadFunction: batchLoadFunction)` will ensure that every
157
-
call to `.load()` will produce a *new* Future, and previously requested keys will not be
158
-
saved in memory.
159
-
160
-
However, when the memoization cache is disabled, your batch function will
161
-
receive an array of keys which may contain duplicates! Each key will be
162
-
associated with each call to `.load()`. Your batch loader should provide a value
163
-
for each instance of the requested key.
136
+
However, when the memoization cache is disabled, your batch function will receive an array of keys which may contain duplicates! Each key will be associated with each call to `.load()`. Your batch loader should provide a value for each instance of the requested key.
More complex cache behavior can be achieved by calling `.clear()` or `.clearAll()`
183
-
rather than disabling the cache completely. For example, this DataLoader will
184
-
provide unique keys to a batch function due to the memoization cache being
185
-
enabled, but will immediately clear its cache when the batch function is called
186
-
so later requests will load new values.
155
+
More complex cache behavior can be achieved by calling `.clear()` or `.clearAll()` rather than disabling the cache completely. For example, this DataLoader will provide unique keys to a batch function due to the memoization cache being enabled, but will immediately clear its cache when the batch function is called so later requests will load new values.
187
156
188
157
```swift
189
158
let myLoader = DataLoader<String, String>(batchLoadFunction: { keys in
190
-
identityLoader.clearAll()
159
+
awaitidentityLoader.clearAll()
191
160
returnsomeBatchLoad(keys: keys)
192
161
})
193
162
```
194
163
195
164
## Using with GraphQL 🎀
196
165
197
-
DataLoader pairs nicely well with [GraphQL](https://github.com/GraphQLSwift/GraphQL) and
198
-
[Graphiti](https://github.com/GraphQLSwift/Graphiti). GraphQL fields are designed to be
199
-
stand-alone functions. Without a caching or batching mechanism,
200
-
it's easy for a naive GraphQL server to issue new database requests each time a
201
-
field is resolved.
166
+
DataLoader pairs nicely well with [GraphQL](https://github.com/GraphQLSwift/GraphQL) and [Graphiti](https://github.com/GraphQLSwift/Graphiti). GraphQL fields are designed to be stand-alone functions. Without a caching or batching mechanism, it's easy for a naive GraphQL server to issue new database requests each time a field is resolved.
202
167
203
168
Consider the following GraphQL request:
204
169
@@ -219,12 +184,9 @@ Consider the following GraphQL request:
219
184
}
220
185
```
221
186
222
-
Naively, if `me`, `bestFriend` and `friends` each need to request the backend,
223
-
there could be at most 12 database requests!
187
+
Naively, if `me`, `bestFriend` and `friends` each need to request the backend, there could be at most 12 database requests!
224
188
225
-
By using DataLoader, we could batch our requests to a `User` type, and
226
-
only require at most 4 database requests, and possibly fewer if there are cache hits.
227
-
Here's a full example using Graphiti:
189
+
By using DataLoader, we could batch our requests to a `User` type, and only require at most 4 database requests, and possibly fewer if there are cache hits. Here's a full example using Graphiti:
let userLoader = DataLoader<Int, User>() { [weakself] keys in
257
219
guardletself=selfelse { throw ContextError }
258
-
return User.query(on: self.database).filter(\.$id ~~ keys).all().map { users in
259
-
keys.map { key in
260
-
users.first { $0.id== key }!
261
-
}
220
+
let users =tryawait User.query(on: self.database).filter(\.$id ~~ keys).all()
221
+
return keys.map { key in
222
+
users.first { $0.id== key }!
262
223
}
263
224
}
264
225
}
@@ -283,24 +244,18 @@ struct UserAPI : API {
283
244
284
245
## Contributing 🤘
285
246
286
-
All your feedback and help to improve this project is very welcome. Please create issues for your bugs, ideas and
287
-
enhancement requests, or better yet, contribute directly by creating a PR. 😎
247
+
All your feedback and help to improve this project is very welcome. Please create issues for your bugs, ideas and enhancement requests, or better yet, contribute directly by creating a PR. 😎
288
248
289
-
When reporting an issue, please add a detailed example, and if possible a code snippet or test
290
-
to reproduce your problem. 💥
249
+
When reporting an issue, please add a detailed example, and if possible a code snippet or test to reproduce your problem. 💥
291
250
292
-
When creating a pull request, please adhere to the current coding style where possible, and create tests with your
293
-
code so it keeps providing an awesome test coverage level 💪
251
+
When creating a pull request, please adhere to the current coding style where possible, and create tests with your code so it keeps providing an awesome test coverage level 💪
294
252
295
-
This repo uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat), and includes lint checks to enforce these formatting standards.
296
-
To format your code, install `swiftformat` and run:
253
+
This repo uses [SwiftFormat](https://github.com/nicklockwood/SwiftFormat), and includes lint checks to enforce these formatting standards. To format your code, install `swiftformat` and run:
297
254
298
255
```bash
299
256
swiftformat .
300
257
```
301
258
302
259
## Acknowledgements 👏
303
260
304
-
This library is entirely a Swift version of Facebooks [DataLoader](https://github.com/facebook/dataloader).
305
-
Developed by [Lee Byron](https://github.com/leebyron) and [Nicholas Schrock](https://github.com/schrockn)
306
-
from [Facebook](https://www.facebook.com/).
261
+
This library is entirely a Swift version of Facebook's [DataLoader](https://github.com/facebook/dataloader). Developed by [Lee Byron](https://github.com/leebyron) and [Nicholas Schrock](https://github.com/schrockn) from [Facebook](https://www.facebook.com/).
0 commit comments