Skip to content

Commit 8836d7e

Browse files
authored
Merge pull request #99 from graphql-java/cache_calls_in_batch_load
ValueCache calls are made inside the batch load call
2 parents 682c652 + af28c04 commit 8836d7e

15 files changed

+648
-144
lines changed

README.md

+23-16
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ It can serve as integral part of your application's data layer to provide a
1010
consistent API over various back-ends and reduce message communication overhead through batching and caching.
1111

1212
An important use case for `java-dataloader` is improving the efficiency of GraphQL query execution. Graphql fields
13-
are resolved in a independent manner and with a true graph of objects, you may be fetching the same object many times.
13+
are resolved independently and, with a true graph of objects, you may be fetching the same object many times.
1414

1515
A naive implementation of graphql data fetchers can easily lead to the dreaded "n+1" fetch problem.
1616

1717
Most of the code is ported directly from Facebook's reference implementation, with one IMPORTANT adaptation to make
1818
it work for Java 8. ([more on this below](#manual-dispatching)).
1919

20-
But before reading on, be sure to take a short dive into the
20+
Before reading on, be sure to take a short dive into the
2121
[original documentation](https://github.com/facebook/dataloader/blob/master/README.md) provided by Lee Byron (@leebyron)
2222
and Nicholas Schrock (@schrockn) from [Facebook](https://www.facebook.com/), the creators of the original data loader.
2323

@@ -51,7 +51,8 @@ and Nicholas Schrock (@schrockn) from [Facebook](https://www.facebook.com/), the
5151
- Results are ordered according to insertion order of load requests
5252
- Deals with partial errors when a batch future fails
5353
- Can disable batching and/or caching in configuration
54-
- Can supply your own [`CacheMap<K, V>`](https://github.com/graphql-java/java-dataloader/blob/master/src/main/java/io/engagingspaces/vertx/dataloader/CacheMap.java) implementations
54+
- Can supply your own `CacheMap<K, V>` implementations
55+
- Can supply your own `ValueCache<K, V>` implementations
5556
- Has very high test coverage
5657

5758
## Examples
@@ -110,7 +111,7 @@ In this version of data loader, this does not happen automatically. More on thi
110111

111112
As stated on the original Facebook project :
112113

113-
>A naive application may have issued four round-trips to a backend for the required information,
114+
> A naive application may have issued four round-trips to a backend for the required information,
114115
but with DataLoader this application will make at most two.
115116

116117
> DataLoader allows you to decouple unrelated parts of your application without sacrificing the
@@ -270,9 +271,9 @@ This is not quite as loose in a Java implementation as Java is a type safe langu
270271

271272
A batch loader function is defined as `BatchLoader<K, V>` meaning for a key of type `K` it returns a value of type `V`.
272273

273-
It cant just return some `Exception` as an object of type `V`. Type safety matters.
274+
It can't just return some `Exception` as an object of type `V`. Type safety matters.
274275

275-
However you can use the `Try` data type which can encapsulate a computation that succeeded or returned an exception.
276+
However, you can use the `Try` data type which can encapsulate a computation that succeeded or returned an exception.
276277

277278
```java
278279
Try<String> tryS = Try.tryCall(() -> {
@@ -291,7 +292,7 @@ However you can use the `Try` data type which can encapsulate a computation that
291292
}
292293
```
293294

294-
DataLoader supports this type and you can use this form to create a batch loader that returns a list of `Try` objects, some of which may have succeeded
295+
DataLoader supports this type, and you can use this form to create a batch loader that returns a list of `Try` objects, some of which may have succeeded,
295296
and some of which may have failed. From that data loader can infer the right behavior in terms of the `load(x)` promise.
296297

297298
```java
@@ -331,7 +332,7 @@ The value cache uses an async API pattern to encapsulate the idea that the value
331332
The default future cache behind `DataLoader` is an in memory `HashMap`. There is no expiry on this, and it lives for as long as the data loader
332333
lives.
333334

334-
However, you can create your own custom cache and supply it to the data loader on construction via the `org.dataloader.CacheMap` interface.
335+
However, you can create your own custom future cache and supply it to the data loader on construction via the `org.dataloader.CacheMap` interface.
335336

336337
```java
337338
MyCustomCache customCache = new MyCustomCache();
@@ -342,21 +343,27 @@ However, you can create your own custom cache and supply it to the data loader o
342343
You could choose to use one of the fancy cache implementations from Guava or Caffeine and wrap it in a `CacheMap` wrapper ready
343344
for data loader. They can do fancy things like time eviction and efficient LRU caching.
344345

345-
As stated above, a custom `org.dataloader.CacheMap` is a local cache of futures with values, not values per se.
346+
As stated above, a custom `org.dataloader.CacheMap` is a local cache of `CompleteFuture`s to values, not values per se.
347+
348+
If you want to externally cache values then you need to use the `org.dataloader.ValueCache` interface.
346349

347350
## Custom value caches
348351

349-
You will need to create your own implementations of the `org.dataloader.ValueCache` if your want to use an external cache.
352+
The `org.dataloader.ValueCache` allows you to use an external cache.
353+
354+
The API of `ValueCache` has been designed to be asynchronous because it is expected that the value cache could be outside
355+
your JVM. It uses `CompleteableFuture`s to get and set values into cache, which may involve a network call and hence exceptional failures to get
356+
or set values.
357+
358+
The `ValueCache` API is batch oriented, if you have a backing cache that can do batch cache fetches (such a REDIS) then you can use the `ValueCache.getValues*(`
359+
call directly. However, if you don't have such a backing cache, then the default implementation will break apart the batch of cache value into individual requests
360+
to `ValueCache.getValue()` for you.
350361

351362
This library does not ship with any implementations of `ValueCache` because it does not want to have
352363
production dependencies on external cache libraries, but you can easily write your own.
353364

354365
The tests have an example based on [Caffeine](https://github.com/ben-manes/caffeine).
355366

356-
The API of `ValueCache` has been designed to be asynchronous because it is expected that the value cache could be outside
357-
your JVM. It uses `CompleteableFuture`s to get and set values into cache, which may involve a network call and hence exceptional failures to get
358-
or set values.
359-
360367

361368
## Disabling caching
362369

@@ -369,7 +376,7 @@ In certain uncommon cases, a DataLoader which does not cache may be desirable.
369376
Calling the above will ensure that every call to `.load()` will produce a new promise, and requested keys will not be saved in memory.
370377

371378
However, when the memoization cache is disabled, your batch function will receive an array of keys which may contain duplicates! Each key will
372-
be associated with each call to `.load()`. Your batch loader should provide a value for each instance of the requested key as per the contract
379+
be associated with each call to `.load()`. Your batch loader MUST provide a value for each instance of the requested key as per the contract
373380

374381
```java
375382
userDataLoader.load("A");
@@ -445,7 +452,7 @@ then you will not want to cache data meant for user A to then later give it user
445452
The scope of your `DataLoader` instances is important. You will want to create them per web request to ensure data is only cached within that
446453
web request and no more.
447454

448-
If your data can be shared across web requests then use a custom cache to keep values in a common place.
455+
If your data can be shared across web requests then use a custom `org.dataloader.ValueCache` to keep values in a common place.
449456

450457
Data loaders are stateful components that contain promises (with context) that are likely share the same affinity as the request.
451458

build.gradle

+13
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ version = releaseVersion ? releaseVersion : getDevelopmentVersion()
4040
group = 'com.graphql-java'
4141
description = 'A pure Java 8 port of Facebook Dataloader'
4242

43+
gradle.buildFinished { buildResult ->
44+
println "*******************************"
45+
println "*"
46+
if (buildResult.failure != null) {
47+
println "* FAILURE - ${buildResult.failure}"
48+
} else {
49+
println "* SUCCESS"
50+
}
51+
println "* Version: $version"
52+
println "*"
53+
println "*******************************"
54+
}
55+
4356
repositories {
4457
mavenCentral()
4558
mavenLocal()

src/main/java/org/dataloader/DataLoader.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class DataLoader<K, V> {
6767
private final DataLoaderHelper<K, V> helper;
6868
private final StatisticsCollector stats;
6969
private final CacheMap<Object, V> futureCache;
70-
private final ValueCache<Object, V> valueCache;
70+
private final ValueCache<K, V> valueCache;
7171

7272
/**
7373
* Creates new DataLoader with the specified batch loader function and default options
@@ -430,8 +430,8 @@ private CacheMap<Object, V> determineFutureCache(DataLoaderOptions loaderOptions
430430
}
431431

432432
@SuppressWarnings("unchecked")
433-
private ValueCache<Object, V> determineValueCache(DataLoaderOptions loaderOptions) {
434-
return (ValueCache<Object, V>) loaderOptions.valueCache().orElseGet(ValueCache::defaultValueCache);
433+
private ValueCache<K, V> determineValueCache(DataLoaderOptions loaderOptions) {
434+
return (ValueCache<K, V>) loaderOptions.valueCache().orElseGet(ValueCache::defaultValueCache);
435435
}
436436

437437
/**

0 commit comments

Comments
 (0)