Skip to content

Commit d651b94

Browse files
committed
Readme updates as well as a rename of the default
value store to NoOp
1 parent 50e4179 commit d651b94

File tree

4 files changed

+56
-26
lines changed

4 files changed

+56
-26
lines changed

README.md

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ a list of user ids in one call.
144144
This is important consideration. By using `dataloader` you have batched up the requests for N keys in a list of keys that can be
145145
retrieved at one time.
146146

147-
If you don't have batched backing services, then you cant be as efficient as possible as you will have to make N calls for each key.
147+
If you don't have batched backing services, then you can't be as efficient as possible as you will have to make N calls for each key.
148148

149149
```java
150150
BatchLoader<Long, User> lessEfficientUserBatchLoader = new BatchLoader<Long, User>() {
@@ -313,6 +313,47 @@ and some of which may have failed. From that data loader can infer the right be
313313
On the above example if one of the `Try` objects represents a failure, then its `load()` promise will complete exceptionally and you can
314314
react to that, in a type safe manner.
315315

316+
## Caching
317+
318+
`DataLoader` has a two tiered caching system in place.
319+
320+
The first cache is represented by the interface `org.dataloader.CacheMap`. It will cache `CompletableFuture`s by key and hence future `load(key)` calls
321+
will be given the same future and hence the same value.
322+
323+
This cache can only work local to the JVM, since its caches `CompletableFuture`s which cannot be serialised across a network say.
324+
325+
The second level cache is a value cache represented by the interface `org.dataloader.CachedValueStore`. By default, this is not enabled and is a no-op.
326+
327+
The value cache uses an async API pattern to encapsulate the idea that the value cache could be in a remote place such as REDIS or Memcached.
328+
329+
## Custom future caches
330+
331+
The default cache behind `DataLoader` is an in memory `HashMap`. There is no expiry on this, and it lives for as long as the data loader
332+
lives.
333+
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+
336+
```java
337+
MyCustomCache customCache = new MyCustomCache();
338+
DataLoaderOptions options = DataLoaderOptions.newOptions().setCacheMap(customCache);
339+
DataLoaderFactory.newDataLoader(userBatchLoader, options);
340+
```
341+
342+
You could choose to use one of the fancy cache implementations from Guava or Caffeine and wrap it in a `CacheMap` wrapper ready
343+
for data loader. They can do fancy things like time eviction and efficient LRU caching.
344+
345+
As stated above, a custom `org.dataloader.CacheMap` is a local cache of futures with values, not values per se.
346+
347+
## Custom value caches
348+
349+
You will need to create your own implementations of the `org.dataloader.CachedValueStore` if your want to use an external cache.
350+
351+
This library does not ship with any implementations of `CachedValueStore` because it does not want to have
352+
production dependencies on external cache libraries.
353+
354+
The API of `CachedValueStore` has been designed to be asynchronous because it is expected that the value cache could be outside
355+
your JVM. It uses `Future`s to get and set values into cache, which may involve a network call and hence exceptional failures to get
356+
or set values.
316357

317358

318359
## Disabling caching
@@ -346,7 +387,7 @@ More complex cache behavior can be achieved by calling `.clear()` or `.clearAll(
346387
## Caching errors
347388

348389
If a batch load fails (that is, a batch function returns a rejected CompletionStage), then the requested values will not be cached.
349-
However if a batch function returns a `Try` or `Throwable` instance for an individual value, then that will be cached to avoid frequently loading
390+
However, if a batch function returns a `Try` or `Throwable` instance for an individual value, then that will be cached to avoid frequently loading
350391
the same problem object.
351392

352393
In some circumstances you may wish to clear the cache for these individual problems:
@@ -406,33 +447,18 @@ If your data can be shared across web requests then use a custom cache to keep v
406447

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

409-
## Custom caches
410-
411-
The default cache behind `DataLoader` is an in memory `HashMap`. There is no expiry on this, and it lives for as long as the data loader
412-
lives.
413-
414-
However, you can create your own custom cache and supply it to the data loader on construction via the `org.dataloader.CacheMap` interface.
415-
416-
```java
417-
MyCustomCache customCache = new MyCustomCache();
418-
DataLoaderOptions options = DataLoaderOptions.newOptions().setCacheMap(customCache);
419-
DataLoaderFactory.newDataLoader(userBatchLoader, options);
420-
```
421-
422-
You could choose to use one of the fancy cache implementations from Guava or Kaffeine and wrap it in a `CacheMap` wrapper ready
423-
for data loader. They can do fancy things like time eviction and efficient LRU caching.
424-
425450
## Manual dispatching
426451

427-
The original [Facebook DataLoader](https://github.com/facebook/dataloader) was written in Javascript for NodeJS. NodeJS is single-threaded in nature, but simulates
428-
asynchronous logic by invoking functions on separate threads in an event loop, as explained
452+
The original [Facebook DataLoader](https://github.com/facebook/dataloader) was written in Javascript for NodeJS.
453+
454+
NodeJS is single-threaded in nature, but simulates asynchronous logic by invoking functions on separate threads in an event loop, as explained
429455
[in this post](http://stackoverflow.com/a/19823583/3455094) on StackOverflow.
430456

431457
NodeJS generates so-call 'ticks' in which queued functions are dispatched for execution, and Facebook `DataLoader` uses
432458
the `nextTick()` function in NodeJS to _automatically_ dequeue load requests and send them to the batch execution function
433459
for processing.
434460

435-
And here there is an **IMPORTANT DIFFERENCE** compared to how `java-dataloader` operates!!
461+
Here there is an **IMPORTANT DIFFERENCE** compared to how `java-dataloader` operates!!
436462

437463
In NodeJS the batch preparation will not affect the asynchronous processing behaviour in any way. It will just prepare
438464
batches in 'spare time' as it were.

src/main/java/org/dataloader/CachedValueStore.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.dataloader;
22

33
import org.dataloader.annotations.PublicSpi;
4-
import org.dataloader.impl.DefaultCachedValueStore;
4+
import org.dataloader.impl.NoOpCachedValueStore;
55

66
import java.util.List;
77
import java.util.concurrent.CompletableFuture;
@@ -24,6 +24,7 @@
2424
@PublicSpi
2525
public interface CachedValueStore<K, V> {
2626

27+
2728
/**
2829
* Creates a new store, using the default no-op implementation.
2930
*
@@ -33,7 +34,8 @@ public interface CachedValueStore<K, V> {
3334
* @return the cache store
3435
*/
3536
static <K, V> CachedValueStore<K, V> defaultStore() {
36-
return new DefaultCachedValueStore<>();
37+
//noinspection unchecked
38+
return (CachedValueStore<K, V>) NoOpCachedValueStore.NOOP;
3739
}
3840

3941
/**

src/main/java/org/dataloader/impl/DefaultCacheMap.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.concurrent.CompletableFuture;
2525

2626
/**
27-
* Default implementation of {@link CacheMap} that is based on a regular {@link java.util.LinkedHashMap}.
27+
* Default implementation of {@link CacheMap} that is based on a regular {@link java.util.HashMap}.
2828
*
2929
* @param <K> type parameter indicating the type of the cache keys
3030
* @param <V> type parameter indicating the type of the data that is cached

src/main/java/org/dataloader/impl/DefaultCachedValueStore.java renamed to src/main/java/org/dataloader/impl/NoOpCachedValueStore.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import java.util.concurrent.CompletableFuture;
88

99
/**
10-
* Default implementation of {@link CachedValueStore} that does nothing.
10+
* Implementation of {@link CachedValueStore} that does nothing.
1111
* <p>
1212
* We don't want to store values in memory twice, so when using the default store we just
1313
* say we never have the key and complete the other methods by doing nothing.
@@ -18,7 +18,9 @@
1818
* @author <a href="https://github.com/craig-day">Craig Day</a>
1919
*/
2020
@Internal
21-
public class DefaultCachedValueStore<K, V> implements CachedValueStore<K, V> {
21+
public class NoOpCachedValueStore<K, V> implements CachedValueStore<K, V> {
22+
23+
public static NoOpCachedValueStore<?, ?> NOOP = new NoOpCachedValueStore<>();
2224

2325
/**
2426
* {@inheritDoc}

0 commit comments

Comments
 (0)