Skip to content

Commit 43069d5

Browse files
authored
Merge pull request #58 from salesforce/replace-guava-with-caffeine-fix
Configurable cache with Caffeine or Guava
2 parents f961c99 + 579f48c commit 43069d5

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

src/main/java/com/force/i18n/grammar/parser/GrammaticalLabelSetLoader.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,48 @@ public GrammaticalLabelSetLoader(LabelSetLoaderConfig config, boolean useSharedK
155155
this.seedKeyMap = null;
156156
}
157157

158-
this.cache = CaffeinatedGuava.build(getCacheBuilder(config), getCacheLoader());
158+
this.cache = initCache(config);
159159
}
160160

161+
/**
162+
* initialize internal cache that holds {@link GrammaticalLabelSet}.
163+
*
164+
* @param config a configuration to initialize this loader
165+
* @return a {@code LoadingCache} object to use as a cache
166+
*/
167+
protected LoadingCache<GrammaticalLabelSetDescriptor, GrammaticalLabelSet> initCache(LabelSetLoaderConfig config) {
168+
// switch between Caffeine / Guava based on the config
169+
return config.useCaffeine()
170+
? CaffeinatedGuava.build(getCaffeineCacheBuilder(config), getCacheLoader())
171+
: getGuavaCacheBuilder(config).build(getCacheLoader());
172+
}
173+
174+
/**
175+
* @param config
176+
* a configuration to initialize this loader. if the configuration is set to use Guava, the
177+
* {@link UnsupportedOperationException} will be thrown.
178+
* @return Caffeine instance to build a cache
179+
* @deprecated This method is no longer used. In order to obtain a cache builder to modify cache settings, use
180+
* {@link #getCaffeineCacheBuilder(LabelSetLoaderConfig)} for Caffeine, or
181+
* {@link #getGuavaCacheBuilder(LabelSetLoaderConfig)} for Guava.
182+
* @see #initCache(LabelSetLoaderConfig)
183+
* @see LabelSetLoaderConfig#setCaffeine(boolean)
184+
*/
185+
@Deprecated(since = "1.2.27", forRemoval = true)
161186
protected Caffeine<Object, Object> getCacheBuilder(LabelSetLoaderConfig config) {
187+
if (!config.useCaffeine()) throw new UnsupportedOperationException();
188+
return getCaffeineCacheBuilder(config);
189+
}
190+
191+
/**
192+
* Return a Caffeine-based cache builder instance.
193+
* Override this method to replace, or edit cache option.
194+
*
195+
* @param config a config to initialize this loader
196+
* @return Caffeine-based builder
197+
* @see #initCache(LabelSetLoaderConfig)
198+
*/
199+
protected Caffeine<Object, Object> getCaffeineCacheBuilder(LabelSetLoaderConfig config) {
162200
Caffeine<Object, Object> builder = Caffeine.newBuilder().initialCapacity(64);
163201

164202
Duration expiration = config.getCacheExpireAfter();
@@ -173,6 +211,30 @@ protected Caffeine<Object, Object> getCacheBuilder(LabelSetLoaderConfig config)
173211
return builder;
174212
}
175213

214+
/**
215+
* Return a Guava-based cache builder instance.
216+
* Override this method to replace, or edit cache option.
217+
*
218+
* @param config a config to initialize this loader
219+
* @return Guava-based builder
220+
* @see #initCache(LabelSetLoaderConfig)
221+
*/
222+
protected CacheBuilder<Object, Object> getGuavaCacheBuilder(LabelSetLoaderConfig config) {
223+
CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().initialCapacity(64);
224+
225+
Duration expiration = config.getCacheExpireAfter();
226+
if (!expiration.isZero() && !expiration.isNegative()) builder.expireAfterAccess(expiration);
227+
228+
long maxSize = config.getCacheMaxSize();
229+
if (maxSize > 0) builder.maximumSize(maxSize);
230+
231+
if (config.isCacheStatsEnabled()) {
232+
builder.recordStats();
233+
}
234+
return builder;
235+
}
236+
237+
176238
protected CacheLoader<GrammaticalLabelSetDescriptor, GrammaticalLabelSet> getCacheLoader() {
177239
return new CacheLoader<GrammaticalLabelSetDescriptor, GrammaticalLabelSet>() {
178240
@Override

src/main/java/com/force/i18n/grammar/parser/LabelSetLoaderConfig.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class LabelSetLoaderConfig {
2323
public static final String RECORD_STATS = "loader.cache.stats";
2424
public static final String LOADER_EXPIRE_AFTER = "loader.cache.expireAfter";
2525
public static final String LOADER_MAX_SIZE = "loader.cache.maxSize";
26+
public static final String USE_CAFFEINE = "loader.cache.useCaffeine";
2627

2728
private final GrammaticalLabelSetDescriptor desc;
2829
private final GrammaticalLabelSetProvider parent;
@@ -33,6 +34,7 @@ public class LabelSetLoaderConfig {
3334
private boolean isCacheStatsEnabled;
3435
private Duration cacheExpireAfter; // expiration in minues
3536
private long cacheMaxSize; // max allowed entires
37+
private boolean useCaffeine;
3638

3739
public LabelSetLoaderConfig(GrammaticalLabelSetDescriptor baseDesc, GrammaticalLabelSetProvider parent) {
3840
this.desc = baseDesc;
@@ -45,6 +47,7 @@ public LabelSetLoaderConfig(GrammaticalLabelSetDescriptor baseDesc, GrammaticalL
4547
setCacheStatsEnabled(BasePropertyFile.stringToBoolean(getProperty(RECORD_STATS)));
4648
setCacheExpireAfter(Duration.ofMinutes(getPropertyLong(LOADER_EXPIRE_AFTER)));
4749
setCacheMaxSize(getPropertyLong(LOADER_MAX_SIZE));
50+
setCaffeine(BasePropertyFile.stringToBoolean(getProperty(USE_CAFFEINE)));
4851
}
4952

5053
public LabelSetLoaderConfig(LabelSetLoaderConfig copyFrom) {
@@ -56,6 +59,7 @@ public LabelSetLoaderConfig(LabelSetLoaderConfig copyFrom) {
5659
setCacheStatsEnabled(copyFrom.isCacheStatsEnabled());
5760
setCacheExpireAfter(copyFrom.getCacheExpireAfter());
5861
setCacheMaxSize(copyFrom.getCacheMaxSize());
62+
setCaffeine(copyFrom.useCaffeine());
5963
}
6064

6165
public static String getProperty(String prop) {
@@ -136,13 +140,23 @@ public LabelSetLoaderConfig setCacheMaxSize(long newVal) {
136140
return this;
137141
}
138142

143+
public LabelSetLoaderConfig setCaffeine(boolean useCaffeine) {
144+
this.useCaffeine = useCaffeine;
145+
return this;
146+
}
147+
148+
public boolean useCaffeine() {
149+
return this.useCaffeine;
150+
}
151+
139152
@Override
140153
public String toString() {
141154
StringBuilder sb = new StringBuilder();
142155
sb.append("stats=").append(this.isCacheStatsEnabled)
143156
.append(", expire=").append(this.cacheExpireAfter)
144157
.append(", size=").append(this.cacheMaxSize)
145-
.append(", dir=").append(this.cacheDir.toAbsolutePath());
158+
.append(", dir=").append(this.cacheDir.toAbsolutePath())
159+
.append(", useCaffeine=").append(this.useCaffeine);
146160
return sb.toString();
147161
}
148162
}

src/main/java/com/force/i18n/grammaticus.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ loader.cache.expireAfter=0
5252

5353
# maximum size of cache entry. This cache key is LabelSetDescriptor(language). no limit for 0.
5454
loader.cache.maxSize=0
55+
56+
# use Caffeine as internal cache otherwise, Guava LoadingCache will be used.
57+
loader.cache.useCaffeine=true

src/test/java/com/force/i18n/grammar/parser/GrammaticalLabelTest.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
package com.force.i18n.grammar.parser;
99

10+
import static org.junit.Assert.assertThrows;
11+
1012
import java.io.IOException;
1113
import java.lang.reflect.Field;
1214
import java.net.URL;
@@ -547,11 +549,24 @@ public void testCacheConfig() throws InterruptedException {
547549

548550
GrammaticalLabelSetDescriptor desc = getDescriptor();
549551

550-
// Test expiration
551552
LabelSetLoaderConfig config = new LabelSetLoaderConfig(desc, null);
553+
GrammaticalLabelSetLoader loader = new GrammaticalLabelSetLoader(config);
554+
// default is Ceffeine. comparing with the class name because the class is not public
555+
assertEquals("CaffeinatedGuavaLoadingCache", loader.getCache().getClass().getSimpleName());
556+
557+
// try switching to Guava cache.
558+
config.setCaffeine(false);
559+
GrammaticalLabelSetLoader guavaLoader = new GrammaticalLabelSetLoader(config);
560+
assertEquals("LocalLoadingCache", guavaLoader.getCache().getClass().getSimpleName());
561+
562+
assertThrows(UnsupportedOperationException.class, () -> guavaLoader.getCacheBuilder(config));
563+
564+
// Test expiration
565+
// re-construct the loader with Caffeine and set expiration period to 1ms
566+
config.setCaffeine(true);
552567
config.setCacheExpireAfter(Duration.ofMillis(1));
568+
loader = new GrammaticalLabelSetLoader(config);
553569

554-
GrammaticalLabelSetLoader loader = new GrammaticalLabelSetLoader(config);
555570
loader.getSet(ENGLISH);
556571
Thread.sleep(1);
557572
assertNull(loader.getCache().getIfPresent(desc));
@@ -580,12 +595,12 @@ public void testCacheConfig() throws InterruptedException {
580595

581596
// Caffeine does not evict prior the threadshold, but after the size crossed
582597
loader.getSet(FRENCH);
583-
Thread.sleep(1);
598+
Thread.sleep(5);
584599
assertEquals(3, loader.getCache().size());
585600
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(FRENCH)));
586601

587602
loader.getSet(GERMAN);
588-
Thread.sleep(1);
603+
Thread.sleep(5);
589604
assertEquals(3, loader.getCache().size());
590605
assertNotNull(loader.getCache().getIfPresent(desc.getForOtherLanguage(GERMAN)));
591606
}

0 commit comments

Comments
 (0)