Skip to content

Commit ad229cf

Browse files
committed
Bulk load: change interface, #116
1 parent 38856b0 commit ad229cf

File tree

9 files changed

+163
-53
lines changed

9 files changed

+163
-53
lines changed

cache2k-api/src/main/java/org/cache2k/io/AsyncBulkCacheLoader.java

+52-7
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222

2323
import org.cache2k.DataAware;
2424

25-
import java.util.Collection;
2625
import java.util.Collections;
2726
import java.util.Map;
2827
import java.util.Objects;
2928
import java.util.Set;
29+
import java.util.concurrent.Executor;
3030

3131
/**
3232
* Extension of {@link AsyncCacheLoader} with bulk load capabilities.
@@ -40,16 +40,18 @@
4040
public interface AsyncBulkCacheLoader<K, V> extends AsyncCacheLoader<K, V> {
4141

4242
/**
43-
* Load all data referenced by the key set. This operation to load more efficiently
43+
* Load all values referenced by the key set. This method is used to load more efficiently
4444
* in case the cache client uses {@link org.cache2k.Cache#loadAll(Iterable)} or
4545
* {@link org.cache2k.Cache#getAll(Iterable)}.
4646
*
4747
* @param keys the keys to load
48-
* @param contextSet set of contexts that contain the keys to load as well as more detailed
49-
* information
50-
* @return a map containing values for all keys that were requested
48+
* @param context context of this request with additional information. Also contains
49+
* per key context with current entry values, if present.
50+
* @param callback Callback to post results
51+
* @throws Exception An exception, if the load operation cannot be started. The exception will
52+
* be assigned to every requested key depending on the resilience policy.
5153
*/
52-
void loadAll(Set<K> keys, Set<Context<K, V>> contextSet, BulkCallback<K, V> callback)
54+
void loadAll(Set<K> keys, BulkLoadContext<K, V> context, BulkCallback<K, V> callback)
5355
throws Exception;
5456

5557
/**
@@ -59,7 +61,20 @@ void loadAll(Set<K> keys, Set<Context<K, V>> contextSet, BulkCallback<K, V> call
5961
*/
6062
@Override
6163
default void load(K key, Context<K, V> context, Callback<V> callback) throws Exception {
62-
loadAll(Collections.singleton(key), Collections.singleton(context), new BulkCallback<K, V>() {
64+
Set<K> keySet = Collections.singleton(key);
65+
BulkLoadContext<K, V> bulkLoadContext = new BulkLoadContext<K, V>() {
66+
@Override
67+
public Set<Context<K, V>> getContextSet() { return Collections.singleton(context); }
68+
@Override
69+
public long getStartTime() { return context.getStartTime(); }
70+
@Override
71+
public Set<K> getKeys() { return keySet; }
72+
@Override
73+
public Executor getExecutor() { return context.getExecutor(); }
74+
@Override
75+
public Executor getLoaderExecutor() { return context.getLoaderExecutor(); }
76+
};
77+
loadAll(keySet, bulkLoadContext, new BulkCallback<K, V>() {
6378
@Override
6479
public void onLoadSuccess(Map<? extends K, ? extends V> data) {
6580
Map.Entry<? extends K, ? extends V> entry = data.entrySet().iterator().next();
@@ -79,6 +94,36 @@ public void onLoadFailure(Throwable exception) {
7994
});
8095
}
8196

97+
interface BulkLoadContext<K, V> {
98+
99+
/**
100+
* Individual load context for each key.
101+
*/
102+
Set<Context<K, V>> getContextSet();
103+
104+
long getStartTime();
105+
106+
/**
107+
* Keys requested to load.
108+
*/
109+
Set<K> getKeys();
110+
111+
/**
112+
* The configured executor for async operations.
113+
*
114+
* @see org.cache2k.Cache2kBuilder#executor(Executor)
115+
*/
116+
Executor getExecutor();
117+
118+
/**
119+
* The configured loader executor.
120+
*
121+
* @see org.cache2k.Cache2kBuilder#loaderExecutor(Executor)
122+
*/
123+
Executor getLoaderExecutor();
124+
125+
}
126+
82127
interface BulkCallback<K, V> extends DataAware<K, V> {
83128

84129
/**

cache2k-api/src/main/java/org/cache2k/io/CacheLoader.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public interface CacheLoader<K, V> extends DataAwareCustomization<K, V> {
8383
* values a {@link NullPointerException} is thrown, but the expiry policy is
8484
* called before it.
8585
* @throws Exception Unhandled exception from the loader. Exceptions are suppressed or
86-
* wrapped and rethrown via a {@link CacheLoaderException}
86+
* wrapped and rethrown via a {@link CacheLoaderException}. Rethrow
8787
*/
8888
V load(K key) throws Exception;
8989

cache2k-core/src/main/java/org/cache2k/core/BulkAction.java

+59-6
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626

2727
import java.util.ArrayList;
2828
import java.util.Collection;
29+
import java.util.Collections;
2930
import java.util.HashMap;
3031
import java.util.HashSet;
3132
import java.util.Iterator;
3233
import java.util.List;
3334
import java.util.Map;
3435
import java.util.Set;
36+
import java.util.concurrent.Executor;
3537

3638
/**
3739
* Execute a set of entry actions in parallel to leverage bulk I/O. The basic idea is to
@@ -51,14 +53,21 @@ public abstract class BulkAction<K, V, R> implements
5153
AsyncBulkCacheLoader.BulkCallback<K, V>,
5254
EntryAction.CompletedCallback<K, V, R> {
5355

56+
/** Used for executors **/
57+
private HeapCache heapCache;
58+
/**
59+
* Map with the individual entry actions, the map will not be modified after the
60+
* bulk operation start, so its save to read from it from different threads.
61+
*/
5462
private final Map<K, EntryAction<K, V, R>> key2action;
5563
private final Collection<EntryAction<K, V, R>> toStart;
5664
private final Set<K> toLoad;
5765
private final AsyncCacheLoader<K, V> loader;
5866
private int completedCount = 0;
5967

6068
/** Create object and start operation. */
61-
public BulkAction(AsyncCacheLoader<K, V> loader, Set<K> keys, boolean sync) {
69+
public BulkAction(HeapCache heapCache, AsyncCacheLoader<K, V> loader, Set<K> keys, boolean sync) {
70+
this.heapCache = heapCache;
6271
this.loader = loader;
6372
key2action = new HashMap<>(keys.size());
6473
toLoad = new HashSet<>(keys.size());
@@ -204,15 +213,15 @@ private void startLoadingSingle() {
204213

205214
/**
206215
* Start loading via calling the async bulk loader. The keys are removed from
207-
* toLoad in the callback to ourselves, we use toLoad additionally to keep track
208-
* of partial completions
216+
* toLoad in the callback to ourselves since we use toLoad additionally to keep track
217+
* of partial completions. We need to copy the keys, since we modify the toLoad set
218+
* if we get partial completions.
209219
*/
210220
private void startLoadingBulk() {
211221
AsyncBulkCacheLoader<K, V> bulkLoader = (AsyncBulkCacheLoader<K, V>) loader;
212-
Set<Context<K, V>> contextSet = new HashSet<>(toLoad.size());
213-
for (K key : toLoad) { contextSet.add(key2action.get(key)); }
222+
Set<K> keysCopy = Collections.unmodifiableSet(new HashSet<K>(toLoad));
214223
try {
215-
bulkLoader.loadAll(toLoad, contextSet, this);
224+
bulkLoader.loadAll(keysCopy, new MyBulkLoadContext(keysCopy), this);
216225
} catch (Throwable ouch) {
217226
onLoadFailure(ouch);
218227
}
@@ -311,4 +320,48 @@ protected void bulkOperationCompleted() { }
311320

312321
protected abstract EntryAction<K, V, R> createEntryAction(K key, BulkAction<K, V, R> self);
313322

323+
private class MyBulkLoadContext implements AsyncBulkCacheLoader.BulkLoadContext<K, V> {
324+
325+
private final Set<K> keys;
326+
private Set<Context<K, V>> contextSet;
327+
328+
MyBulkLoadContext(Set<K> keys) {
329+
this.keys = keys;
330+
}
331+
332+
/** Lazily create set with entry contexts */
333+
@Override
334+
public Set<Context<K, V>> getContextSet() {
335+
if (contextSet == null) {
336+
contextSet = new HashSet<>(toLoad.size());
337+
for (K key : keys) {
338+
contextSet.add(key2action.get(key));
339+
}
340+
}
341+
return contextSet;
342+
}
343+
344+
@Override
345+
public long getStartTime() {
346+
K firstKey = keys.iterator().next();
347+
return key2action.get(firstKey).getStartTime();
348+
}
349+
350+
@Override
351+
public Set<K> getKeys() {
352+
return keys;
353+
}
354+
355+
@Override
356+
public Executor getExecutor() {
357+
return heapCache.getExecutor();
358+
}
359+
360+
@Override
361+
public Executor getLoaderExecutor() {
362+
return heapCache.getLoaderExecutor();
363+
}
364+
365+
}
366+
314367
}

cache2k-core/src/main/java/org/cache2k/core/BulkResultCollector.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public static CacheLoaderException createBulkLoaderException(
8282
int exceptionCount, int operationCount, Throwable example) {
8383
if (exceptionCount > 1) {
8484
String txt =
85-
exceptionCount + " out of " + operationCount + " cache requests" +
85+
exceptionCount + " out of " + operationCount + " requests" +
8686
", one as cause";
8787
return new CacheLoaderException(txt, example);
8888
}

cache2k-core/src/main/java/org/cache2k/core/CacheBaseInfo.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ class CacheBaseInfo implements InternalCacheInfo {
108108
metrics.getPeekHitNotFreshCount() + metrics.getPeekMissCount();
109109
hitCnt = em.getHitCount();
110110
correctedPutCnt = metrics.getPutNewEntryCount() + metrics.getPutHitCount();
111-
if (heapCache.loaderExecutor instanceof ExclusiveExecutor) {
111+
if (heapCache.getLoaderExecutor() instanceof ExclusiveExecutor) {
112112
ThreadPoolExecutor ex =
113-
((ExclusiveExecutor) heapCache.loaderExecutor).getThreadPoolExecutor();
113+
((ExclusiveExecutor) heapCache.getLoaderExecutor()).getThreadPoolExecutor();
114114
asyncLoadsInFlight = ex.getActiveCount();
115115
asyncLoadsStarted = ex.getTaskCount();
116116
loaderThreadsLimit = ex.getCorePoolSize();

cache2k-core/src/main/java/org/cache2k/core/HeapCache.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
import org.cache2k.core.timing.TimeAgnosticTiming;
4343
import org.cache2k.core.timing.Timing;
44-
import org.cache2k.io.CacheLoaderException;
4544
import org.cache2k.operation.TimeReference;
4645
import org.cache2k.core.log.Log;
4746
import org.cache2k.core.util.TunableConstants;
@@ -158,7 +157,11 @@ public Timing<K, V> getTiming() {
158157

159158
private Executor executor;
160159

161-
protected volatile Executor loaderExecutor = new LazyLoaderExecutor();
160+
private volatile Executor loaderExecutor = new LazyLoaderExecutor();
161+
162+
public Executor getLoaderExecutor() {
163+
return loaderExecutor;
164+
}
162165

163166
/**
164167
* Create executor only if needed.

cache2k-core/src/main/java/org/cache2k/core/WiredCache.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ private <R> BulkAction<K, V, R> syncBulkOp(Semantic<K, V, R> op, Set<K> keys) {
281281
callback.onLoadFailure(ouch);
282282
}
283283
};
284-
BulkAction<K, V, R> bulkAction = new BulkAction<K, V, R>(myLoader, keys, true) {
284+
BulkAction<K, V, R> bulkAction = new BulkAction<K, V, R>(heapCache, myLoader, keys, true) {
285285
@Override
286286
protected EntryAction<K, V, R> createEntryAction(K key, BulkAction<K, V, R> self) {
287287
return new MyEntryAction<R>(op, key, null) {
@@ -302,7 +302,7 @@ protected AsyncCacheLoader<K, V> asyncLoader() {
302302
private <R> CompletableFuture<BulkAction<K, V, R>> asyncBulkOp(
303303
Semantic<K, V, R> op, Set<K> keys) {
304304
CompletableFuture<BulkAction<K, V, R>> future = new CompletableFuture<>();
305-
new BulkAction<K, V, R>(asyncLoader, keys, false) {
305+
new BulkAction<K, V, R>(heapCache, asyncLoader, keys, false) {
306306
@Override
307307
protected EntryAction<K, V, R> createEntryAction(K key, BulkAction<K, V, R> self) {
308308
return new MyEntryAction<R>(op, key, null, self) {
@@ -875,7 +875,7 @@ protected Timing<K, V> timing() {
875875
*/
876876
@Override
877877
public Executor getLoaderExecutor() {
878-
return heapCache.loaderExecutor;
878+
return heapCache.getLoaderExecutor();
879879
}
880880

881881
@Override

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy