Skip to content

Conversation

rharriso
Copy link

@rharriso rharriso commented Apr 9, 2020

I'd like to propose that the DataLoaderHelper handle situations where cacheMap.get completes exceptionally. Currently this class assumes that if the cache contains a key, then the value associated with that key is the appropriate type.

Note these changes have not been tested

Sorry if my use of CompletableFuture is crude, I'd appreciate any composition advice.

Motivation

I'm planning on using Redis as an expiring cache. The data has to be serialized and de-serialized on read and write. This cache is an external service and is shared between graphql server instances. Problems with network activity and differing application versions could cause issues with smooth serde.

Changes

The new flow for load is as follows.

  1. Try to load from cache:
    a. Returns Optional.empty if: cache disabled, key not in cache, Cache.get fails or returns null
    b. Returns Optional<v> otherwise
  2. If cache load is present, return result
  3. If cache load not present, load from source
  4. If caching enabled, store loadedValue in cache
  5. Result is supplied by completable future.

return CompletableFuture.completedFuture(Optional.empty());
}

return futureCache.get(key).handleAsync((v, exception) -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleAsync use the standard ForkJoin pool - we make it a policy to never introduce threading into the library. Only the consuming code of this lib can really know what threading strategy to use.

If we accepted this change, this would have to be the version that takes and executor and it would have to come from DataLoaderOptions say

if (futureCache.containsKey(cacheKey)) {
stats.incrementCacheHitCount();
return futureCache.get(cacheKey);
return loadFromCache(key).thenApplyAsync((cachedValue) -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem : thenApplyAsync uses the standard FookJoinPoll - see above

var sourceResult = loadFromSource(key, loadContext);
sourceResult.thenAcceptAsync((sourceValue) -> {
if (cachingEnabled) {
futureCache.set(cacheKey, java.util.concurrent.CompletableFuture.supplyAsync(() -> sourceValue));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would be no need for

java.util.concurrent.CompletableFuture.supplyAsync(() -> sourceValue)

if you have a value then you can just do CompleteableFuture.completedValue(v)

That is a CF thats is finished successfully with value v

return sourceResult.get();
} catch (InterruptedException | java.util.concurrent.ExecutionException e) {
return null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a blocking call - sourceResult.get();

DataLoader MUST always be async - ALWAYS. We cant have this.

@rharriso
Copy link
Author

@bbakerman thanks for the feedback. I'll see if I can resolve these problems.

@bbakerman bbakerman closed this Jun 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants