Line data Source code
1 : // Copyright (C) 2012 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.cache.mem;
16 :
17 : import static java.util.concurrent.TimeUnit.NANOSECONDS;
18 : import static java.util.concurrent.TimeUnit.SECONDS;
19 :
20 : import com.github.benmanes.caffeine.cache.Caffeine;
21 : import com.github.benmanes.caffeine.cache.RemovalListener;
22 : import com.github.benmanes.caffeine.cache.Weigher;
23 : import com.github.benmanes.caffeine.guava.CaffeinatedGuava;
24 : import com.google.common.base.Strings;
25 : import com.google.common.cache.Cache;
26 : import com.google.common.cache.CacheLoader;
27 : import com.google.common.cache.LoadingCache;
28 : import com.google.common.cache.RemovalNotification;
29 : import com.google.gerrit.common.Nullable;
30 : import com.google.gerrit.server.cache.CacheDef;
31 : import com.google.gerrit.server.cache.ForwardingRemovalListener;
32 : import com.google.gerrit.server.cache.MemoryCacheFactory;
33 : import com.google.gerrit.server.config.ConfigUtil;
34 : import com.google.gerrit.server.config.GerritServerConfig;
35 : import com.google.inject.Inject;
36 : import java.time.Duration;
37 : import org.eclipse.jgit.lib.Config;
38 :
39 : class DefaultMemoryCacheFactory implements MemoryCacheFactory {
40 : private final Config cfg;
41 : private final ForwardingRemovalListener.Factory forwardingRemovalListenerFactory;
42 :
43 : @Inject
44 : DefaultMemoryCacheFactory(
45 : @GerritServerConfig Config config,
46 153 : ForwardingRemovalListener.Factory forwardingRemovalListenerFactory) {
47 153 : this.cfg = config;
48 153 : this.forwardingRemovalListenerFactory = forwardingRemovalListenerFactory;
49 153 : }
50 :
51 : @Override
52 : public <K, V> Cache<K, V> build(CacheDef<K, V> def) {
53 152 : return CaffeinatedGuava.build(create(def));
54 : }
55 :
56 : @Override
57 : public <K, V> LoadingCache<K, V> build(CacheDef<K, V> def, CacheLoader<K, V> loader) {
58 153 : return cacheMaximumWeight(def) == 0
59 153 : ? new PassthroughLoadingCache<>(loader)
60 153 : : CaffeinatedGuava.build(create(def), loader);
61 : }
62 :
63 : private <K, V> Caffeine<K, V> create(CacheDef<K, V> def) {
64 153 : Caffeine<K, V> builder = newCacheBuilder();
65 153 : builder.recordStats();
66 153 : builder.maximumWeight(cacheMaximumWeight(def));
67 153 : builder = builder.removalListener(newRemovalListener(def.name()));
68 153 : builder.weigher(newWeigher(def.weigher()));
69 :
70 153 : Duration expireAfterWrite = def.expireAfterWrite();
71 153 : if (has(def.configKey(), "maxAge")) {
72 0 : builder.expireAfterWrite(
73 0 : ConfigUtil.getTimeUnit(
74 0 : cfg, "cache", def.configKey(), "maxAge", toSeconds(expireAfterWrite), SECONDS),
75 : SECONDS);
76 153 : } else if (expireAfterWrite != null) {
77 152 : builder.expireAfterWrite(expireAfterWrite.toNanos(), NANOSECONDS);
78 : }
79 :
80 153 : Duration expireAfterAccess = def.expireFromMemoryAfterAccess();
81 153 : if (has(def.configKey(), "expireFromMemoryAfterAccess")) {
82 0 : builder.expireAfterAccess(
83 0 : ConfigUtil.getTimeUnit(
84 : cfg,
85 : "cache",
86 0 : def.configKey(),
87 : "expireFromMemoryAfterAccess",
88 0 : toSeconds(expireAfterAccess),
89 : SECONDS),
90 : SECONDS);
91 153 : } else if (expireAfterAccess != null) {
92 152 : builder.expireAfterAccess(expireAfterAccess.toNanos(), NANOSECONDS);
93 : }
94 :
95 153 : Duration refreshAfterWrite = def.refreshAfterWrite();
96 153 : if (has(def.configKey(), "refreshAfterWrite")) {
97 0 : builder.refreshAfterWrite(
98 0 : ConfigUtil.getTimeUnit(
99 : cfg,
100 : "cache",
101 0 : def.configKey(),
102 : "refreshAfterWrite",
103 0 : toSeconds(refreshAfterWrite),
104 : SECONDS),
105 : SECONDS);
106 153 : } else if (refreshAfterWrite != null) {
107 152 : builder.refreshAfterWrite(refreshAfterWrite.toNanos(), NANOSECONDS);
108 : }
109 :
110 153 : return builder;
111 : }
112 :
113 : private <K, V> long cacheMaximumWeight(CacheDef<K, V> def) {
114 153 : return cfg.getLong("cache", def.configKey(), "memoryLimit", def.maximumWeight());
115 : }
116 :
117 : private static long toSeconds(@Nullable Duration duration) {
118 0 : return duration != null ? duration.getSeconds() : 0;
119 : }
120 :
121 : private boolean has(String name, String var) {
122 153 : return !Strings.isNullOrEmpty(cfg.getString("cache", name, var));
123 : }
124 :
125 : @SuppressWarnings("unchecked")
126 : private static <K, V> Caffeine<K, V> newCacheBuilder() {
127 153 : return (Caffeine<K, V>) Caffeine.newBuilder();
128 : }
129 :
130 : @SuppressWarnings("unchecked")
131 : private <V, K> RemovalListener<K, V> newRemovalListener(String cacheName) {
132 153 : return (k, v, cause) ->
133 152 : forwardingRemovalListenerFactory
134 151 : .create(cacheName)
135 151 : .onRemoval(
136 151 : RemovalNotification.create(
137 151 : k, v, com.google.common.cache.RemovalCause.valueOf(cause.name())));
138 : }
139 :
140 : private static <K, V> Weigher<K, V> newWeigher(
141 : com.google.common.cache.Weigher<K, V> guavaWeigher) {
142 153 : return guavaWeigher == null ? Weigher.singletonWeigher() : (k, v) -> guavaWeigher.weigh(k, v);
143 : }
144 : }
|