| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| Expand Up | @@ -28,11 +28,11 @@ | |
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.CompletionException; | ||
| import java.util.concurrent.CompletionStage; | ||
| import java.util.concurrent.ConcurrentLinkedQueue; | ||
| import java.util.concurrent.atomic.AtomicReference; | ||
|
|
||
| import static java.util.Collections.emptyList; | ||
| import static java.util.Collections.singletonList; | ||
| import static java.util.concurrent.CompletableFuture.allOf; | ||
| import static java.util.concurrent.CompletableFuture.completedFuture; | ||
| import static java.util.stream.Collectors.toList; | ||
| import static org.dataloader.impl.Assertions.assertState; | ||
| Expand Down Expand Up | @@ -244,7 +244,7 @@ DispatchResult<V> dispatch() { | |
| int queueSize = loaderQueueEntryHead.queueSize; | ||
| // we copy the pre-loaded set of futures ready for dispatch | ||
| Object[] keysArray = new Object[queueSize]; | ||
| CompletableFuture[] queuedFuturesArray = new CompletableFuture[queueSize]; | ||
| CompletableFuture<V>[] queuedFuturesArray = new CompletableFuture[queueSize]; | ||
| Object[] callContextsArray = new Object[queueSize]; | ||
| int index = queueSize - 1; | ||
| while (loaderQueueEntryHead != null) { | ||
| Expand Down Expand Up | @@ -313,54 +313,59 @@ private CompletableFuture<List<V>> sliceIntoBatchesOfBatches(List<K> keys, List< | |
| } | ||
| // | ||
| // now reassemble all the futures into one that is the complete set of results | ||
| return allOf(allBatches.toArray(new CompletableFuture[0])) | ||
| .thenApply(v -> allBatches.stream() | ||
| .map(CompletableFuture::join) | ||
| .flatMap(Collection::stream) | ||
| .collect(toList())); | ||
| return CompletableFutureKit.allOfFlatMap(allBatches); | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| private CompletableFuture<List<V>> dispatchQueueBatch(List<K> keys, List<Object> callContexts, List<CompletableFuture<V>> queuedFutures) { | ||
| stats.incrementBatchLoadCountBy(keys.size(), new IncrementBatchLoadCountByStatisticsContext<>(keys, callContexts)); | ||
| CompletableFuture<List<V>> batchLoad = invokeLoader(keys, callContexts, queuedFutures, loaderOptions.cachingEnabled()); | ||
| private CompletableFuture<List<V>> dispatchQueueBatch(List<K> keys, List<Object> keyContexts, List<CompletableFuture<V>> queuedFutures) { | ||
| stats.incrementBatchLoadCountBy(keys.size(), new IncrementBatchLoadCountByStatisticsContext<>(keys, keyContexts)); | ||
|
|
||
| BatchLoaderEnvironment environment = mkBatchLoaderEnv(keys, keyContexts); | ||
|
|
||
| CompletableFuture<List<V>> batchLoad = invokeLoader(environment, keys, keyContexts, queuedFutures, loaderOptions.cachingEnabled()); | ||
| return batchLoad | ||
| .thenApply(values -> { | ||
| .thenCompose(values -> { | ||
| assertResultSize(keys, values); | ||
| if (isPublisher() || isMappedPublisher()) { | ||
| // We have already completed the queued futures by the time the overall batchLoad future has completed. | ||
| return values; | ||
| return CompletableFutureKit.success(values); | ||
| } | ||
|
|
||
| List<K> clearCacheKeys = new ArrayList<>(); | ||
| Collection<K> clearCacheKeys = new ConcurrentLinkedQueue<>(); | ||
| List<Runnable> completeValueRunnables = new ArrayList<>(); | ||
| for (int idx = 0; idx < queuedFutures.size(); idx++) { | ||
| K key = keys.get(idx); | ||
| V value = values.get(idx); | ||
| Object callContext = callContexts.get(idx); | ||
| Object callContext = keyContexts.get(idx); | ||
| CompletableFuture<V> future = queuedFutures.get(idx); | ||
| if (value instanceof Throwable) { | ||
| stats.incrementLoadErrorCount(new IncrementLoadErrorCountStatisticsContext<>(key, callContext)); | ||
| future.completeExceptionally((Throwable) value); | ||
| clearCacheKeys.add(keys.get(idx)); | ||
| } else if (value instanceof Try) { | ||
| // we allow the batch loader to return a Try so we can better represent a computation | ||
| // that might have worked or not. | ||
| Try<V> tryValue = (Try<V>) value; | ||
| if (tryValue.isSuccess()) { | ||
| future.complete(tryValue.get()); | ||
| } else { | ||
| Runnable completeValueRunnable = () -> { | ||
| if (value instanceof Throwable) { | ||
| stats.incrementLoadErrorCount(new IncrementLoadErrorCountStatisticsContext<>(key, callContext)); | ||
| future.completeExceptionally(tryValue.getThrowable()); | ||
| clearCacheKeys.add(keys.get(idx)); | ||
| future.completeExceptionally((Throwable) value); | ||
| clearCacheKeys.add(key); | ||
| } else if (value instanceof Try) { | ||
| // we allow the batch loader to return a Try so we can better represent a computation | ||
| // that might have worked or not. | ||
| Try<V> tryValue = (Try<V>) value; | ||
| if (tryValue.isSuccess()) { | ||
| future.complete(tryValue.get()); | ||
| } else { | ||
| stats.incrementLoadErrorCount(new IncrementLoadErrorCountStatisticsContext<>(key, callContext)); | ||
| future.completeExceptionally(tryValue.getThrowable()); | ||
| clearCacheKeys.add(key); | ||
| } | ||
| } else { | ||
| future.complete(value); | ||
|
Comment thread
Copy link
Copy Markdown
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThis misses the Try path of completion
Sorry, something went wrong.
All reactions
|
||
| } | ||
| } else { | ||
| future.complete(value); | ||
| } | ||
| }; | ||
| completeValueRunnables.add(completeValueRunnable); | ||
| } | ||
| possiblyClearCacheEntriesOnExceptions(clearCacheKeys); | ||
| return values; | ||
| return scheduleCompletion(environment, keys, values, completeValueRunnables).thenApply(ignored -> { | ||
| possiblyClearCacheEntriesOnExceptions(clearCacheKeys); | ||
| return values; | ||
| }); | ||
| }).exceptionally(ex -> { | ||
| stats.incrementBatchLoadExceptionCount(new IncrementBatchLoadExceptionCountStatisticsContext<>(keys, callContexts)); | ||
| stats.incrementBatchLoadExceptionCount(new IncrementBatchLoadExceptionCountStatisticsContext<>(keys, keyContexts)); | ||
| if (ex instanceof CompletionException) { | ||
| ex = ex.getCause(); | ||
| } | ||
| Expand All | @@ -375,12 +380,32 @@ private CompletableFuture<List<V>> dispatchQueueBatch(List<K> keys, List<Object> | |
| }); | ||
| } | ||
|
|
||
| private CompletableFuture<List<V>> scheduleCompletion(BatchLoaderEnvironment environment, List<K> keys, List<V> values, List<Runnable> completeValueRunnables) { | ||
| BatchLoaderScheduler batchLoaderScheduler = loaderOptions.getBatchLoaderScheduler(); | ||
| CompletionStage<?> scheduledCompletion; | ||
| if (batchLoaderScheduler != null) { | ||
| scheduledCompletion = batchLoaderScheduler | ||
| .scheduleCompletion(completeValueRunnables, keys, environment); | ||
| } else { | ||
| scheduledCompletion = CompletableFutureKit.runAll(completeValueRunnables); | ||
| } | ||
| return scheduledCompletion | ||
| .thenApply(ignored -> values) | ||
| .toCompletableFuture(); | ||
| } | ||
|
|
||
| private BatchLoaderEnvironment mkBatchLoaderEnv(List<K> keys, List<Object> keyContexts) { | ||
| Object context = loaderOptions.getBatchLoaderContextProvider().getContext(); | ||
| return BatchLoaderEnvironment.newBatchLoaderEnvironment() | ||
| .context(context).keyContexts(keys, keyContexts).build(); | ||
| } | ||
|
|
||
|
|
||
| private void assertResultSize(List<K> keys, List<V> values) { | ||
| assertState(keys.size() == values.size(), () -> "The size of the promised values MUST be the same size as the key list"); | ||
| } | ||
|
|
||
| private void possiblyClearCacheEntriesOnExceptions(List<K> keys) { | ||
| private void possiblyClearCacheEntriesOnExceptions(Collection<K> keys) { | ||
| if (keys.isEmpty()) { | ||
| return; | ||
| } | ||
| Expand All | @@ -396,15 +421,17 @@ private void possiblyClearCacheEntriesOnExceptions(List<K> keys) { | |
| CompletableFuture<V> invokeLoaderImmediately(K key, Object keyContext, boolean cachingEnabled) { | ||
| List<K> keys = singletonList(key); | ||
| List<Object> keyContexts = singletonList(keyContext); | ||
| BatchLoaderEnvironment environment = mkBatchLoaderEnv(keys, keyContexts); | ||
|
|
||
| List<CompletableFuture<V>> queuedFutures = singletonList(new CompletableFuture<>()); | ||
| return invokeLoader(keys, keyContexts, queuedFutures, cachingEnabled) | ||
| return invokeLoader(environment, keys, keyContexts, queuedFutures, cachingEnabled) | ||
| .thenApply(list -> list.get(0)) | ||
| .toCompletableFuture(); | ||
| } | ||
|
|
||
| CompletableFuture<List<V>> invokeLoader(List<K> keys, List<Object> keyContexts, List<CompletableFuture<V>> queuedFutures, boolean cachingEnabled) { | ||
| CompletableFuture<List<V>> invokeLoader(BatchLoaderEnvironment environment, List<K> keys, List<Object> keyContexts, List<CompletableFuture<V>> queuedFutures, boolean cachingEnabled) { | ||
| if (!cachingEnabled) { | ||
| return invokeLoader(keys, keyContexts, queuedFutures); | ||
| return invokeLoader(environment, keys, keyContexts, queuedFutures); | ||
| } | ||
| CompletableFuture<List<Try<V>>> cacheCallCF = getFromValueCache(keys); | ||
| return cacheCallCF.thenCompose(cachedValues -> { | ||
| Expand Down Expand Up | @@ -453,7 +480,8 @@ CompletableFuture<List<V>> invokeLoader(List<K> keys, List<Object> keyContexts, | |
| // we missed some keys from cache, so send them to the batch loader | ||
| // and then fill in their values | ||
| // | ||
| CompletableFuture<List<V>> batchLoad = invokeLoader(missedKeys, missedKeyContexts, missedQueuedFutures); | ||
| BatchLoaderEnvironment missedEnvironment = mkBatchLoaderEnv(missedKeys, missedKeyContexts); | ||
| CompletableFuture<List<V>> batchLoad = invokeLoader(missedEnvironment, missedKeys, missedKeyContexts, missedQueuedFutures); | ||
| return batchLoad.thenCompose(missedValues -> { | ||
| assertResultSize(missedKeys, missedValues); | ||
|
|
||
| Expand All | @@ -472,11 +500,7 @@ CompletableFuture<List<V>> invokeLoader(List<K> keys, List<Object> keyContexts, | |
| }); | ||
| } | ||
|
|
||
| CompletableFuture<List<V>> invokeLoader(List<K> keys, List<Object> keyContexts, List<CompletableFuture<V>> queuedFutures) { | ||
| Object context = loaderOptions.getBatchLoaderContextProvider().getContext(); | ||
| BatchLoaderEnvironment environment = BatchLoaderEnvironment.newBatchLoaderEnvironment() | ||
| .context(context).keyContexts(keys, keyContexts).build(); | ||
|
|
||
| CompletableFuture<List<V>> invokeLoader(BatchLoaderEnvironment environment, List<K> keys, List<Object> keyContexts, List<CompletableFuture<V>> queuedFutures) { | ||
| DataLoaderInstrumentationContext<List<?>> instrCtx = ctxOrNoopCtx(instrumentation().beginBatchLoader(dataLoader, keys, environment)); | ||
|
|
||
| CompletableFuture<List<V>> batchLoad; | ||
| Expand Down | ||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityclearCacheKeys may now be accessed concurrently by multiple thread if we execute the Runnables in parallel.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Qualitygood call - price of side effects outside a thread
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.