• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alibaba / jetcache / #414

02 Jun 2024 07:12AM UTC coverage: 88.831% (-0.07%) from 88.905%
#414

push

areyouok
fix: deploy check

4756 of 5354 relevant lines covered (88.83%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.88
/jetcache-core/src/main/java/com/alicp/jetcache/AbstractCache.java
1
package com.alicp.jetcache;
2

3
import com.alicp.jetcache.embedded.AbstractEmbeddedCache;
4
import com.alicp.jetcache.event.CacheEvent;
5
import com.alicp.jetcache.event.CacheGetAllEvent;
6
import com.alicp.jetcache.event.CacheGetEvent;
7
import com.alicp.jetcache.event.CachePutAllEvent;
8
import com.alicp.jetcache.event.CachePutEvent;
9
import com.alicp.jetcache.event.CacheRemoveAllEvent;
10
import com.alicp.jetcache.event.CacheRemoveEvent;
11
import com.alicp.jetcache.external.AbstractExternalCache;
12
import com.alicp.jetcache.support.SquashedLogger;
13
import org.slf4j.Logger;
14
import org.slf4j.LoggerFactory;
15

16
import java.nio.ByteBuffer;
17
import java.time.Duration;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.concurrent.CountDownLatch;
23
import java.util.concurrent.TimeUnit;
24
import java.util.concurrent.locks.ReentrantLock;
25
import java.util.function.Consumer;
26
import java.util.function.Function;
27

28
/**
29
 * Created on 2016/10/7.
30
 *
31
 * @author huangli
32
 */
33
public abstract class AbstractCache<K, V> implements Cache<K, V> {
1✔
34

35
    private static Logger logger = LoggerFactory.getLogger(AbstractCache.class);
1✔
36

37
    private volatile ConcurrentHashMap<Object, LoaderLock> loaderMap;
38

39
    protected volatile boolean closed;
40
    private static final ReentrantLock reentrantLock = new ReentrantLock();
1✔
41

42
    ConcurrentHashMap<Object, LoaderLock> initOrGetLoaderMap() {
43
        if (loaderMap == null) {
1✔
44
            reentrantLock.lock();
1✔
45
            try {
46
                if (loaderMap == null) {
1✔
47
                    loaderMap = new ConcurrentHashMap<>();
1✔
48
                }
49
            }finally {
50
                reentrantLock.unlock();
1✔
51
            }
52
        }
53
        return loaderMap;
1✔
54
    }
55

56
    protected void logError(String oper, Object key, Throwable e) {
57
        StringBuilder sb = new StringBuilder(256);
1✔
58
        sb.append("jetcache(")
1✔
59
                .append(this.getClass().getSimpleName()).append(") ")
1✔
60
                .append(oper)
1✔
61
                .append(" error.");
1✔
62
        if (!(key instanceof byte[])) {
1✔
63
            try {
64
                sb.append(" key=[")
1✔
65
                        .append(config().getKeyConvertor().apply((K) key))
1✔
66
                        .append(']');
1✔
67
            } catch (Exception ex) {
1✔
68
                // ignore
69
            }
1✔
70
        }
71
        SquashedLogger.getLogger(logger).error(sb, e);
1✔
72
    }
1✔
73

74
    public void notify(CacheEvent e) {
75
        List<CacheMonitor> monitors = config().getMonitors();
1✔
76
        for (CacheMonitor m : monitors) {
1✔
77
            m.afterOperation(e);
1✔
78
        }
1✔
79
    }
1✔
80

81
    @Override
82
    public final CacheGetResult<V> GET(K key) {
83
        long t = System.currentTimeMillis();
1✔
84
        CacheGetResult<V> result;
85
        if (key == null) {
1✔
86
            result = new CacheGetResult<V>(CacheResultCode.FAIL, CacheResult.MSG_ILLEGAL_ARGUMENT, null);
1✔
87
        } else {
88
            result = do_GET(key);
1✔
89
        }
90
        result.future().thenRun(() -> {
1✔
91
            CacheGetEvent event = new CacheGetEvent(this, System.currentTimeMillis() - t, key, result);
1✔
92
            notify(event);
1✔
93
        });
1✔
94
        return result;
1✔
95
    }
96

97
    protected abstract CacheGetResult<V> do_GET(K key);
98

99
    @Override
100
    public final MultiGetResult<K, V> GET_ALL(Set<? extends K> keys) {
101
        long t = System.currentTimeMillis();
1✔
102
        MultiGetResult<K, V> result;
103
        if (keys == null) {
1✔
104
            result = new MultiGetResult<>(CacheResultCode.FAIL, CacheResult.MSG_ILLEGAL_ARGUMENT, null);
1✔
105
        } else {
106
            result = do_GET_ALL(keys);
1✔
107
        }
108
        result.future().thenRun(() -> {
1✔
109
            CacheGetAllEvent event = new CacheGetAllEvent(this, System.currentTimeMillis() - t, keys, result);
1✔
110
            notify(event);
1✔
111
        });
1✔
112
        return result;
1✔
113
    }
114

115
    protected abstract MultiGetResult<K, V> do_GET_ALL(Set<? extends K> keys);
116

117
    @Override
118
    public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull) {
119
        return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull,
1✔
120
                0, null, this);
121
    }
122

123
    @Override
124
    public final V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull,
125
                                   long expireAfterWrite, TimeUnit timeUnit) {
126
        return computeIfAbsentImpl(key, loader, cacheNullWhenLoaderReturnNull,
1✔
127
                expireAfterWrite, timeUnit, this);
128
    }
129

130
    private static <K, V> boolean needUpdate(V loadedValue, boolean cacheNullWhenLoaderReturnNull, Function<K, V> loader) {
131
        if (loadedValue == null && !cacheNullWhenLoaderReturnNull) {
1✔
132
            return false;
1✔
133
        }
134
        if (loader instanceof CacheLoader && ((CacheLoader<K, V>) loader).vetoCacheUpdate()) {
1✔
135
            return false;
1✔
136
        }
137
        return true;
1✔
138
    }
139

140
    static <K, V> V computeIfAbsentImpl(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull,
141
                                               long expireAfterWrite, TimeUnit timeUnit, Cache<K, V> cache) {
142
        AbstractCache<K, V> abstractCache = CacheUtil.getAbstractCache(cache);
1✔
143
        CacheLoader<K, V> newLoader = CacheUtil.createProxyLoader(cache, loader, abstractCache::notify);
1✔
144
        CacheGetResult<V> r;
145
        if (cache instanceof RefreshCache) {
1✔
146
            RefreshCache<K, V> refreshCache = ((RefreshCache<K, V>) cache);
1✔
147
            r = refreshCache.GET(key);
1✔
148
            refreshCache.addOrUpdateRefreshTask(key, newLoader);
1✔
149
        } else {
1✔
150
            r = cache.GET(key);
1✔
151
        }
152
        if (r.isSuccess()) {
1✔
153
            return r.getValue();
1✔
154
        } else {
155
            Consumer<V> cacheUpdater = (loadedValue) -> {
1✔
156
                if(needUpdate(loadedValue, cacheNullWhenLoaderReturnNull, newLoader)) {
1✔
157
                    if (timeUnit != null) {
1✔
158
                        cache.PUT(key, loadedValue, expireAfterWrite, timeUnit).waitForResult();
1✔
159
                    } else {
160
                        cache.PUT(key, loadedValue).waitForResult();
1✔
161
                    }
162
                }
163
            };
1✔
164

165
            V loadedValue;
166
            if (cache.config().isCachePenetrationProtect()) {
1✔
167
                loadedValue = synchronizedLoad(cache.config(), abstractCache, key, newLoader, cacheUpdater);
1✔
168
            } else {
169
                loadedValue = newLoader.apply(key);
1✔
170
                cacheUpdater.accept(loadedValue);
1✔
171
            }
172

173
            return loadedValue;
1✔
174
        }
175
    }
176

177
    static <K, V> V synchronizedLoad(CacheConfig config, AbstractCache<K,V> abstractCache,
178
                                     K key, Function<K, V> newLoader, Consumer<V> cacheUpdater) {
179
        ConcurrentHashMap<Object, LoaderLock> loaderMap = abstractCache.initOrGetLoaderMap();
1✔
180
        Object lockKey = buildLoaderLockKey(abstractCache, key);
1✔
181
        while (true) {
182
            boolean create[] = new boolean[1];
1✔
183
            LoaderLock ll = loaderMap.computeIfAbsent(lockKey, (unusedKey) -> {
1✔
184
                create[0] = true;
1✔
185
                LoaderLock loaderLock = new LoaderLock();
1✔
186
                loaderLock.signal = new CountDownLatch(1);
1✔
187
                loaderLock.loaderThread = Thread.currentThread();
1✔
188
                return loaderLock;
1✔
189
            });
190
            if (create[0] || ll.loaderThread == Thread.currentThread()) {
1✔
191
                try {
192
                    CacheGetResult<V> getResult = abstractCache.GET(key);
1✔
193
                    if (getResult.isSuccess()) {
1✔
194
                        ll.success = true;
×
195
                        ll.value = getResult.getValue();
×
196
                        return getResult.getValue();
×
197
                    } else {
198
                        V loadedValue = newLoader.apply(key);
1✔
199
                        ll.success = true;
1✔
200
                        ll.value = loadedValue;
1✔
201
                        cacheUpdater.accept(loadedValue);
1✔
202
                        return loadedValue;
1✔
203
                    }
204
                } finally {
205
                    if (create[0]) {
1✔
206
                        ll.signal.countDown();
1✔
207
                        loaderMap.remove(lockKey);
1✔
208
                    }
209
                }
210
            } else {
211
                try {
212
                    Duration timeout = config.getPenetrationProtectTimeout();
1✔
213
                    if (timeout == null) {
1✔
214
                        ll.signal.await();
1✔
215
                    } else {
216
                        boolean ok = ll.signal.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
1✔
217
                        if(!ok) {
1✔
218
                            logger.info("loader wait timeout:" + timeout);
1✔
219
                            return newLoader.apply(key);
1✔
220
                        }
221
                    }
222
                } catch (InterruptedException e) {
1✔
223
                    logger.warn("loader wait interrupted");
1✔
224
                    return newLoader.apply(key);
1✔
225
                }
1✔
226
                if (ll.success) {
1✔
227
                    return (V) ll.value;
1✔
228
                } else {
229
                    continue;
230
                }
231

232
            }
233
        }
234
    }
235

236
    private static Object buildLoaderLockKey(Cache c, Object key) {
237
        if (c instanceof AbstractEmbeddedCache) {
1✔
238
            return ((AbstractEmbeddedCache) c).buildKey(key);
1✔
239
        } else if (c instanceof AbstractExternalCache) {
1✔
240
            byte bytes[] = ((AbstractExternalCache) c).buildKey(key);
1✔
241
            return ByteBuffer.wrap(bytes);
1✔
242
        } else if (c instanceof MultiLevelCache) {
1✔
243
            c = ((MultiLevelCache) c).caches()[0];
1✔
244
            return buildLoaderLockKey(c, key);
1✔
245
        } else if(c instanceof ProxyCache) {
×
246
            c = ((ProxyCache) c).getTargetCache();
×
247
            return buildLoaderLockKey(c, key);
×
248
        } else {
249
            throw new CacheException("impossible");
×
250
        }
251
    }
252

253
    @Override
254
    public final CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
255
        long t = System.currentTimeMillis();
1✔
256
        CacheResult result;
257
        if (key == null) {
1✔
258
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
1✔
259
        } else {
260
            result = do_PUT(key, value, expireAfterWrite, timeUnit);
1✔
261
        }
262
        result.future().thenRun(() -> {
1✔
263
            CachePutEvent event = new CachePutEvent(this, System.currentTimeMillis() - t, key, value, result);
1✔
264
            notify(event);
1✔
265
        });
1✔
266
        return result;
1✔
267
    }
268

269
    protected abstract CacheResult do_PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
270

271
    @Override
272
    public final CacheResult PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) {
273
        long t = System.currentTimeMillis();
1✔
274
        CacheResult result;
275
        if (map == null) {
1✔
276
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
1✔
277
        } else {
278
            result = do_PUT_ALL(map, expireAfterWrite, timeUnit);
1✔
279
        }
280
        result.future().thenRun(() -> {
1✔
281
            CachePutAllEvent event = new CachePutAllEvent(this, System.currentTimeMillis() - t, map, result);
1✔
282
            notify(event);
1✔
283
        });
1✔
284
        return result;
1✔
285
    }
286

287
    protected abstract CacheResult do_PUT_ALL(Map<? extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
288

289
    @Override
290
    public final CacheResult REMOVE(K key) {
291
        long t = System.currentTimeMillis();
1✔
292
        CacheResult result;
293
        if (key == null) {
1✔
294
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
1✔
295
        } else {
296
            result = do_REMOVE(key);
1✔
297
        }
298
        result.future().thenRun(() -> {
1✔
299
            CacheRemoveEvent event = new CacheRemoveEvent(this, System.currentTimeMillis() - t, key, result);
1✔
300
            notify(event);
1✔
301
        });
1✔
302
        return result;
1✔
303
    }
304

305
    protected abstract CacheResult do_REMOVE(K key);
306

307
    @Override
308
    public final CacheResult REMOVE_ALL(Set<? extends K> keys) {
309
        long t = System.currentTimeMillis();
1✔
310
        CacheResult result;
311
        if (keys == null) {
1✔
312
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
1✔
313
        } else {
314
            result = do_REMOVE_ALL(keys);
1✔
315
        }
316
        result.future().thenRun(() -> {
1✔
317
            CacheRemoveAllEvent event = new CacheRemoveAllEvent(this, System.currentTimeMillis() - t, keys, result);
1✔
318
            notify(event);
1✔
319
        });
1✔
320
        return result;
1✔
321
    }
322

323
    protected abstract CacheResult do_REMOVE_ALL(Set<? extends K> keys);
324

325
    @Override
326
    public final CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
327
        long t = System.currentTimeMillis();
1✔
328
        CacheResult result;
329
        if (key == null) {
1✔
330
            result = CacheResult.FAIL_ILLEGAL_ARGUMENT;
1✔
331
        } else {
332
            result = do_PUT_IF_ABSENT(key, value, expireAfterWrite, timeUnit);
1✔
333
        }
334
        result.future().thenRun(() -> {
1✔
335
            CachePutEvent event = new CachePutEvent(this, System.currentTimeMillis() - t, key, value, result);
1✔
336
            notify(event);
1✔
337
        });
1✔
338
        return result;
1✔
339
    }
340

341
    protected abstract CacheResult do_PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
342

343
    @Override
344
    public void close() {
345
        this.closed = true;
1✔
346
    }
1✔
347

348
    public boolean isClosed() {
349
        return this.closed;
1✔
350
    }
351

352
    static class LoaderLock {
1✔
353
        CountDownLatch signal;
354
        Thread loaderThread;
355
        volatile boolean success;
356
        volatile Object value;
357
    }
358
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc