This is an automated email from the ASF dual-hosted git repository.
dsmiley pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new ec218b20cc1 Replace ConcurrentLRUCache and Cache interface with
Caffeine (#4516)
ec218b20cc1 is described below
commit ec218b20cc11899e41cc0dfe0fd5f6b8e7dfc28f
Author: David Smiley <[email protected]>
AuthorDate: Tue Jun 23 00:55:04 2026 -0400
Replace ConcurrentLRUCache and Cache interface with Caffeine (#4516)
Replaces a custom utility with our Caffeine dependency. Unlikely to be
noticed.
Co-authored-by: google-labs-jules[bot]
<161369871+google-labs-jules[bot]@users.noreply.github.com>
---
.../java/org/apache/solr/schema/IndexSchema.java | 10 +-
.../processor/TemplateUpdateProcessorFactory.java | 31 +-
.../org/apache/solr/util/ConcurrentLRUCache.java | 974 ---------------------
.../java/org/apache/solr/common/util/Cache.java | 44 -
.../org/apache/solr/common/util/JavaBinCodec.java | 19 +-
.../apache/solr/common/util/MapBackedCache.java | 60 --
.../org/apache/solr/common/util/ObjectCache.java | 27 +-
.../apache/solr/common/util/TestJavaBinCodec.java | 77 +-
8 files changed, 94 insertions(+), 1148 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
index 05b362f95b6..d90251d8635 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
@@ -18,6 +18,8 @@ package org.apache.solr.schema;
import static java.util.Arrays.asList;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
import java.io.IOException;
import java.io.Writer;
import java.lang.invoke.MethodHandles;
@@ -65,7 +67,6 @@ import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.common.util.Cache;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.ConfigSetService;
@@ -76,7 +77,6 @@ import org.apache.solr.response.SchemaXmlWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.similarities.SchemaSimilarityFactory;
import org.apache.solr.uninverting.UninvertingReader;
-import org.apache.solr.util.ConcurrentLRUCache;
import org.apache.solr.util.PayloadUtils;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger;
@@ -141,8 +141,8 @@ public class IndexSchema {
private static final Set<String> FIELDTYPE_KEYS = Set.of("fieldtype",
"fieldType");
private static final Set<String> FIELD_KEYS = Set.of("dynamicField",
"field");
- protected Cache<String, SchemaField> dynamicFieldCache =
- new ConcurrentLRUCache<>(10000, 8000, 9000, 100, false, false, null);
+ protected final Cache<String, SchemaField> dynamicFieldCache =
+ Caffeine.newBuilder().initialCapacity(100).maximumSize(10000).build();
private Analyzer indexAnalyzer;
private Analyzer queryAnalyzer;
@@ -1386,7 +1386,7 @@ public class IndexSchema {
public SchemaField getFieldOrNull(String fieldName) {
SchemaField f = fields.get(fieldName);
if (f != null) return f;
- f = dynamicFieldCache.get(fieldName);
+ f = dynamicFieldCache.getIfPresent(fieldName);
if (f != null) return f;
for (DynamicField df : dynamicFields) {
diff --git
a/solr/core/src/java/org/apache/solr/update/processor/TemplateUpdateProcessorFactory.java
b/solr/core/src/java/org/apache/solr/update/processor/TemplateUpdateProcessorFactory.java
index f37e4a4c419..ce10e143afc 100644
---
a/solr/core/src/java/org/apache/solr/update/processor/TemplateUpdateProcessorFactory.java
+++
b/solr/core/src/java/org/apache/solr/update/processor/TemplateUpdateProcessorFactory.java
@@ -17,17 +17,17 @@
package org.apache.solr.update.processor;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.solr.common.SolrInputDocument;
-import org.apache.solr.common.util.Cache;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.update.AddUpdateCommand;
-import org.apache.solr.util.ConcurrentLRUCache;
/**
* Adds new fields to documents based on a template pattern specified via
Template.field request
@@ -41,7 +41,7 @@ import org.apache.solr.util.ConcurrentLRUCache;
*/
public class TemplateUpdateProcessorFactory extends
SimpleUpdateProcessorFactory {
private Cache<String, Resolved> templateCache =
- new ConcurrentLRUCache<>(1000, 800, 900, 10, false, false, null);
+ Caffeine.newBuilder().initialCapacity(10).maximumSize(1000).build();
public static final String NAME = "template";
@Override
@@ -79,17 +79,20 @@ public class TemplateUpdateProcessorFactory extends
SimpleUpdateProcessorFactory
public static Resolved getResolved(
String template, Cache<String, Resolved> cache, Pattern pattern) {
- Resolved r = cache == null ? null : cache.get(template);
- if (r == null) {
- r = new Resolved();
- Matcher m = pattern.matcher(template);
- while (m.find()) {
- String variable = m.group(1);
- r.startIndexes.add(m.start(0));
- r.endOffsets.add(m.end(0));
- r.variables.add(variable);
- }
- if (cache != null) cache.put(template, r);
+ if (cache == null) {
+ return resolve(template, pattern);
+ }
+ return cache.get(template, t -> resolve(t, pattern));
+ }
+
+ private static Resolved resolve(String template, Pattern pattern) {
+ Resolved r = new Resolved();
+ Matcher m = pattern.matcher(template);
+ while (m.find()) {
+ String variable = m.group(1);
+ r.startIndexes.add(m.start(0));
+ r.endOffsets.add(m.end(0));
+ r.variables.add(variable);
}
return r;
}
diff --git a/solr/core/src/java/org/apache/solr/util/ConcurrentLRUCache.java
b/solr/core/src/java/org/apache/solr/util/ConcurrentLRUCache.java
deleted file mode 100644
index a32b6158000..00000000000
--- a/solr/core/src/java/org/apache/solr/util/ConcurrentLRUCache.java
+++ /dev/null
@@ -1,974 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.util;
-
-import static
org.apache.lucene.util.RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_ENTRY;
-import static
org.apache.lucene.util.RamUsageEstimator.QUERY_DEFAULT_RAM_BYTES_USED;
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.LongAdder;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Function;
-import org.apache.lucene.util.Accountable;
-import org.apache.lucene.util.PriorityQueue;
-import org.apache.lucene.util.RamUsageEstimator;
-import org.apache.solr.common.util.Cache;
-import org.apache.solr.common.util.TimeSource;
-
-/**
- * A LRU cache implementation based upon ConcurrentHashMap and other
techniques to reduce contention
- * and synchronization overhead to utilize multiple CPU cores more effectively.
- *
- * <p>Note that the implementation does not follow a true LRU
(least-recently-used) eviction
- * strategy. Instead it strives to remove least recently used items but when
the initial cleanup
- * does not remove enough items to reach the 'acceptableWaterMark' limit, it
can remove more items
- * forcefully regardless of access order.
- *
- * @since solr 1.4
- */
-public class ConcurrentLRUCache<K, V> implements Cache<K, V>, Accountable {
-
- private static final long BASE_RAM_BYTES_USED =
- RamUsageEstimator.shallowSizeOfInstance(ConcurrentLRUCache.class)
- + new Stats().ramBytesUsed()
- + RamUsageEstimator.primitiveSizes.get(long.class)
- + RamUsageEstimator.shallowSizeOfInstance(ConcurrentHashMap.class);
-
- private final ConcurrentHashMap<Object, CacheEntry<K, V>> map;
- private int upperWaterMark, lowerWaterMark;
- private final ReentrantLock markAndSweepLock = new ReentrantLock(true);
- private boolean isCleaning = false; // not volatile... piggybacked on other
volatile vars
- private boolean newThreadForCleanup;
- private volatile boolean islive = true;
- private final Stats stats = new Stats();
- private int acceptableWaterMark;
- private long oldestEntry = 0; // not volatile, only accessed in the cleaning
method
- private final TimeSource timeSource = TimeSource.NANO_TIME;
- private final AtomicLong oldestEntryNs = new AtomicLong(0);
- private long maxIdleTimeNs;
- private final EvictionListener<K, V> evictionListener;
- private CleanupThread cleanupThread;
- private boolean runCleanupThread;
-
- private long ramLowerWatermark, ramUpperWatermark;
- private final LongAdder ramBytes = new LongAdder();
-
- public ConcurrentLRUCache(
- long ramLowerWatermark,
- long ramUpperWatermark,
- boolean runCleanupThread,
- EvictionListener<K, V> evictionListener) {
- this(ramLowerWatermark, ramUpperWatermark, runCleanupThread,
evictionListener, -1);
- }
-
- public ConcurrentLRUCache(
- long ramLowerWatermark,
- long ramUpperWatermark,
- boolean runCleanupThread,
- EvictionListener<K, V> evictionListener,
- int maxIdleTimeSec) {
- this.ramLowerWatermark = ramLowerWatermark;
- this.ramUpperWatermark = ramUpperWatermark;
-
- this.evictionListener = evictionListener;
- this.map = new ConcurrentHashMap<>();
- this.newThreadForCleanup = false;
-
- this.acceptableWaterMark = -1;
- this.lowerWaterMark = Integer.MIN_VALUE;
- this.upperWaterMark = Integer.MAX_VALUE;
-
- setMaxIdleTime(maxIdleTimeSec);
- setRunCleanupThread(runCleanupThread);
- }
-
- public ConcurrentLRUCache(
- int upperWaterMark,
- final int lowerWaterMark,
- int acceptableWaterMark,
- int initialSize,
- boolean runCleanupThread,
- boolean runNewThreadForCleanup,
- EvictionListener<K, V> evictionListener) {
- this(
- upperWaterMark,
- lowerWaterMark,
- acceptableWaterMark,
- initialSize,
- runCleanupThread,
- runNewThreadForCleanup,
- evictionListener,
- -1);
- }
-
- public ConcurrentLRUCache(
- int upperWaterMark,
- final int lowerWaterMark,
- int acceptableWaterMark,
- int initialSize,
- boolean runCleanupThread,
- boolean runNewThreadForCleanup,
- EvictionListener<K, V> evictionListener,
- int maxIdleTimeSec) {
- if (upperWaterMark < 1) throw new IllegalArgumentException("upperWaterMark
must be > 0");
- if (lowerWaterMark >= upperWaterMark)
- throw new IllegalArgumentException("lowerWaterMark must be <
upperWaterMark");
- map = new ConcurrentHashMap<>(initialSize);
- newThreadForCleanup = runNewThreadForCleanup;
- this.upperWaterMark = upperWaterMark;
- this.lowerWaterMark = lowerWaterMark;
- this.acceptableWaterMark = acceptableWaterMark;
- this.evictionListener = evictionListener;
- this.ramLowerWatermark = Long.MIN_VALUE;
- this.ramUpperWatermark = Long.MAX_VALUE;
- setMaxIdleTime(maxIdleTimeSec);
- setRunCleanupThread(runCleanupThread);
- }
-
- public ConcurrentLRUCache(int size, int lowerWaterMark) {
- this(
- size,
- lowerWaterMark,
- (int) Math.floor((lowerWaterMark + size) / 2.0),
- (int) Math.ceil(0.75 * size),
- false,
- false,
- null,
- -1);
- }
-
- public void setAlive(boolean live) {
- islive = live;
- }
-
- public void setUpperWaterMark(int upperWaterMark) {
- if (upperWaterMark < 1) throw new IllegalArgumentException("upperWaterMark
must be >= 1");
- this.upperWaterMark = upperWaterMark;
- }
-
- public void setLowerWaterMark(int lowerWaterMark) {
- this.lowerWaterMark = lowerWaterMark;
- }
-
- public void setAcceptableWaterMark(int acceptableWaterMark) {
- this.acceptableWaterMark = acceptableWaterMark;
- }
-
- public void setRamUpperWatermark(long ramUpperWatermark) {
- if (ramUpperWatermark < 1) {
- throw new IllegalArgumentException("ramUpperWaterMark must be >= 1");
- }
- this.ramUpperWatermark = ramUpperWatermark;
- }
-
- public void setRamLowerWatermark(long ramLowerWatermark) {
- if (ramLowerWatermark < 0) {
- throw new IllegalArgumentException("ramLowerWaterMark must be >= 0");
- }
- this.ramLowerWatermark = ramLowerWatermark;
- }
-
- public void setMaxIdleTime(int maxIdleTime) {
- long oldMaxIdleTimeNs = maxIdleTimeNs;
- maxIdleTimeNs =
- maxIdleTime > 0
- ? TimeUnit.NANOSECONDS.convert(maxIdleTime, TimeUnit.SECONDS)
- : Long.MAX_VALUE;
- if (cleanupThread != null && maxIdleTimeNs < oldMaxIdleTimeNs) {
- cleanupThread.wakeThread();
- }
- }
-
- public synchronized void setRunCleanupThread(boolean runCleanupThread) {
- this.runCleanupThread = runCleanupThread;
- if (this.runCleanupThread) {
- if (cleanupThread == null) {
- cleanupThread = new CleanupThread(this);
- cleanupThread.start();
- }
- } else {
- if (cleanupThread != null) {
- cleanupThread.stopThread();
- cleanupThread = null;
- }
- }
- }
-
- @Override
- public V get(K key) {
- CacheEntry<K, V> e = map.get(key);
- if (e == null) {
- if (islive) stats.missCounter.increment();
- return null;
- }
- if (islive) e.lastAccessed = stats.accessCounter.incrementAndGet();
- return e.value;
- }
-
- @Override
- public V remove(K key) {
- CacheEntry<K, V> cacheEntry = map.remove(key);
- if (cacheEntry != null) {
- stats.size.decrement();
- ramBytes.add(-cacheEntry.ramBytesUsed() - HASHTABLE_RAM_BYTES_PER_ENTRY);
- return cacheEntry.value;
- }
- return null;
- }
-
- @Override
- public V computeIfAbsent(K key, Function<? super K, ? extends V>
mappingFunction) {
- // prescreen access first
- V val = get(key);
- if (val != null) {
- return val;
- }
- AtomicBoolean newEntry = new AtomicBoolean();
- CacheEntry<K, V> entry =
- map.computeIfAbsent(
- key,
- k -> {
- V value = mappingFunction.apply(key);
- // preserve the semantics of computeIfAbsent
- if (value == null) {
- return null;
- }
- CacheEntry<K, V> e =
- new CacheEntry<>(
- key,
- value,
- timeSource.getEpochTimeNs(),
- stats.accessCounter.incrementAndGet());
- oldestEntryNs.updateAndGet(x -> x > e.createTime || x == 0 ?
e.createTime : x);
- stats.size.increment();
- ramBytes.add(
- e.ramBytesUsed() + HASHTABLE_RAM_BYTES_PER_ENTRY); // added
key + value + entry
- if (islive) {
- stats.putCounter.increment();
- } else {
- stats.nonLivePutCounter.increment();
- }
- newEntry.set(true);
- return e;
- });
- if (newEntry.get()) {
- maybeMarkAndSweep();
- } else {
- if (islive && entry != null) {
- entry.lastAccessed = stats.accessCounter.incrementAndGet();
- }
- }
- return entry != null ? entry.value : null;
- }
-
- @Override
- public V put(K key, V val) {
- if (val == null) return null;
- CacheEntry<K, V> e =
- new CacheEntry<>(
- key, val, timeSource.getEpochTimeNs(),
stats.accessCounter.incrementAndGet());
- return putCacheEntry(e);
- }
-
- /**
- * Visible for testing to create synthetic cache entries.
- *
- * @lucene.internal
- */
- public V putCacheEntry(CacheEntry<K, V> e) {
- // initialize oldestEntryNs
- oldestEntryNs.updateAndGet(x -> x > e.createTime || x == 0 ? e.createTime
: x);
- CacheEntry<K, V> oldCacheEntry = map.put(e.key, e);
- if (oldCacheEntry == null) {
- stats.size.increment();
- ramBytes.add(e.ramBytesUsed() + HASHTABLE_RAM_BYTES_PER_ENTRY); // added
key + value + entry
- } else {
- ramBytes.add(-oldCacheEntry.ramBytesUsed());
- ramBytes.add(e.ramBytesUsed());
- }
- if (islive) {
- stats.putCounter.increment();
- } else {
- stats.nonLivePutCounter.increment();
- }
- maybeMarkAndSweep();
- return oldCacheEntry == null ? null : oldCacheEntry.value;
- }
-
- private void maybeMarkAndSweep() {
- // Check if we need to clear out old entries from the cache.
- // isCleaning variable is checked instead of markAndSweepLock.isLocked()
- // for performance because every put invocation will check until
- // the size is back to an acceptable level.
- //
- // There is a race between the check and the call to markAndSweep, but
- // it's unimportant because markAndSweep actually acquires the lock or
returns if it can't.
- //
- // Thread safety note: isCleaning read is piggybacked (comes after) other
volatile reads
- // in this method.
- long idleCutoff = timeSource.getEpochTimeNs() - maxIdleTimeNs;
- int currentSize = stats.size.intValue();
- if ((currentSize > upperWaterMark
- || ramBytes.sum() > ramUpperWatermark
- || oldestEntryNs.get() < idleCutoff)
- && !isCleaning) {
- if (newThreadForCleanup) {
- new Thread(this::markAndSweep, "CacheCleanupThread").start();
- } else if (cleanupThread != null) {
- cleanupThread.wakeThread();
- } else {
- markAndSweep();
- }
- }
- }
-
- /**
- * Removes items from the cache to bring the size down to an acceptable
value.
- *
- * <p>Visible for unit testing.
- *
- * @lucene.internal
- */
- public void markAndSweep() {
- // if we want to keep at least 1000 entries, then timestamps of
- // current through current-1000 are guaranteed not to be the oldest (but
that does
- // not mean there are 1000 entries in that group... it's actually anywhere
between
- // 1 and 1000).
- // Also, if we want to remove 500 entries, then
- // oldestEntry through oldestEntry+500 are guaranteed to be
- // removed (however many there are there).
-
- if (!markAndSweepLock.tryLock()) return;
- try {
- if (maxIdleTimeNs != Long.MAX_VALUE) {
- long idleCutoff = timeSource.getEpochTimeNs() - maxIdleTimeNs;
- if (oldestEntryNs.get() < idleCutoff) {
- markAndSweepByIdleTime();
- }
- }
- if (upperWaterMark < size()) {
- markAndSweepByCacheSize();
- } else if (ramUpperWatermark < ramBytesUsed()) {
- markAndSweepByRamSize();
- } else if (upperWaterMark == Integer.MAX_VALUE && ramUpperWatermark ==
Long.MAX_VALUE) {
- // should never happen
- throw new AssertionError(
- "ConcurrentLRUCache initialized with neither size limits nor ram
limits");
- }
- } finally {
- isCleaning = false; // set before markAndSweep.unlock() for visibility
- markAndSweepLock.unlock();
- }
- }
-
- /*
- Must be called after acquiring markAndSweepLock
- */
- private void markAndSweepByIdleTime() {
- assert markAndSweepLock.isHeldByCurrentThread() : "markAndSweepLock held
by another thread";
- Iterator<Map.Entry<Object, CacheEntry<K, V>>> iterator =
map.entrySet().iterator();
- long idleCutoff = timeSource.getEpochTimeNs() - maxIdleTimeNs;
- long currentOldestEntry = Long.MAX_VALUE;
- while (iterator.hasNext()) {
- Map.Entry<Object, CacheEntry<K, V>> entry = iterator.next();
- if (entry.getValue().createTime < idleCutoff) {
- iterator.remove();
- stats.evictionIdleCounter.increment();
- postRemoveEntry(entry.getValue());
- } else {
- if (entry.getValue().createTime < currentOldestEntry) {
- currentOldestEntry = entry.getValue().createTime;
- }
- }
- }
- if (currentOldestEntry != Long.MAX_VALUE) {
- oldestEntryNs.set(currentOldestEntry);
- }
- }
-
- /*
- Must be called after acquiring markAndSweepLock
- */
- private void markAndSweepByRamSize() {
- assert markAndSweepLock.isHeldByCurrentThread() : "markAndSweepLock held
by another thread";
- List<CacheEntry<K, V>> entriesInAccessOrder = new ArrayList<>(map.size());
- map.forEach(
- (o, kvCacheEntry) -> {
- // important because we want to avoid volatile read during
comparisons
- kvCacheEntry.lastAccessedCopy = kvCacheEntry.lastAccessed;
- entriesInAccessOrder.add(kvCacheEntry);
- });
-
- Collections.sort(entriesInAccessOrder); // newer access is smaller, older
access is bigger
-
- // iterate in oldest to newest order
- for (int i = entriesInAccessOrder.size() - 1; i >= 0; i--) {
- CacheEntry<K, V> kvCacheEntry = entriesInAccessOrder.get(i);
- evictEntry(kvCacheEntry.key);
- if (ramBytes.sum() <= ramLowerWatermark) {
- break; // we are done!
- }
- }
- }
-
- /*
- * Removes items from the cache to bring the size down
- * to an acceptable value ('acceptableWaterMark').
- * <p>
- * It is done in two stages. In the first stage, least recently used items
are evicted.
- * If, after the first stage, the cache size is still greater than
'acceptableSize'
- * config parameter, the second stage takes over.
- * </p>
- * <p>The second stage is more intensive and tries to bring down the cache
size
- * to the 'lowerWaterMark' config parameter.</p>
- * Must be called after acquiring markAndSweepLock
- */
- private void markAndSweepByCacheSize() {
- assert markAndSweepLock.isHeldByCurrentThread() : "markAndSweepLock held
by another thread";
- long oldestEntry = this.oldestEntry;
- isCleaning = true;
- this.oldestEntry = oldestEntry; // volatile write to make isCleaning
visible
-
- long timeCurrent = stats.accessCounter.longValue();
- int sz = stats.size.intValue();
-
- int numRemoved = 0;
- int numKept = 0;
- long newestEntry = timeCurrent;
- long newNewestEntry = -1;
- long newOldestEntry = Long.MAX_VALUE;
-
- int wantToKeep = lowerWaterMark;
- int wantToRemove = sz - lowerWaterMark;
-
- @SuppressWarnings("unchecked")
- CacheEntry<K, V>[] eset = (CacheEntry<K, V>[])
Array.newInstance(CacheEntry.class, sz);
- int eSize = 0;
-
- // System.out.println("newestEntry="+newestEntry + "
oldestEntry="+oldestEntry);
- // System.out.println("items removed:" + numRemoved + " numKept=" +
numKept + " esetSz="+ eSize
- // + " sz-numRemoved=" + (sz-numRemoved));
-
- for (CacheEntry<K, V> ce : map.values()) {
- // set lastAccessedCopy to avoid more volatile reads
- ce.lastAccessedCopy = ce.lastAccessed;
- long thisEntry = ce.lastAccessedCopy;
-
- // since the wantToKeep group is likely to be bigger than wantToRemove,
check it first
- if (thisEntry > newestEntry - wantToKeep) {
- // this entry is guaranteed not to be in the bottom
- // group, so do nothing.
- numKept++;
- newOldestEntry = Math.min(thisEntry, newOldestEntry);
- } else if (thisEntry < oldestEntry + wantToRemove) { // entry in bottom
group?
- // this entry is guaranteed to be in the bottom group
- // so immediately remove it from the map.
- evictEntry(ce.key);
- numRemoved++;
- } else {
- // This entry *could* be in the bottom group.
- // Collect these entries to avoid another full pass... this is wasted
- // effort if enough entries are normally removed in this first pass.
- // An alternate impl could make a full second pass.
- if (eSize < eset.length - 1) {
- eset[eSize++] = ce;
- newNewestEntry = Math.max(thisEntry, newNewestEntry);
- newOldestEntry = Math.min(thisEntry, newOldestEntry);
- }
- }
- }
-
- // System.out.println("items removed:" + numRemoved + " numKept=" +
numKept + " esetSz="+ eSize
- // + " sz-numRemoved=" + (sz-numRemoved));
- // TODO: allow this to be customized in the constructor?
- int numPasses = 1; // maximum number of linear passes over the data
-
- // if we didn't remove enough entries, then make more passes
- // over the values we collected, with updated min and max values.
- while (sz - numRemoved > acceptableWaterMark && --numPasses >= 0) {
-
- oldestEntry = newOldestEntry == Long.MAX_VALUE ? oldestEntry :
newOldestEntry;
- newOldestEntry = Long.MAX_VALUE;
- newestEntry = newNewestEntry;
- newNewestEntry = -1;
- wantToKeep = lowerWaterMark - numKept;
- wantToRemove = sz - lowerWaterMark - numRemoved;
-
- // iterate backward to make it easy to remove items.
- for (int i = eSize - 1; i >= 0; i--) {
- CacheEntry<K, V> ce = eset[i];
- long thisEntry = ce.lastAccessedCopy;
-
- if (thisEntry > newestEntry - wantToKeep) {
- // this entry is guaranteed not to be in the bottom
- // group, so do nothing but remove it from the eset.
- numKept++;
- // remove the entry by moving the last element to its position
- eset[i] = eset[eSize - 1];
- eSize--;
-
- newOldestEntry = Math.min(thisEntry, newOldestEntry);
-
- } else if (thisEntry < oldestEntry + wantToRemove) { // entry in
bottom group?
-
- // this entry is guaranteed to be in the bottom group
- // so immediately remove it from the map.
- evictEntry(ce.key);
- numRemoved++;
-
- // remove the entry by moving the last element to its position
- eset[i] = eset[eSize - 1];
- eSize--;
- } else {
- // This entry *could* be in the bottom group, so keep it in the eset,
- // and update the stats.
- newNewestEntry = Math.max(thisEntry, newNewestEntry);
- newOldestEntry = Math.min(thisEntry, newOldestEntry);
- }
- }
- // System.out.println("items removed:" + numRemoved + " numKept=" +
numKept + " esetSz="+
- // eSize + " sz-numRemoved=" + (sz-numRemoved));
- }
-
- // if we still didn't remove enough entries, then make another pass while
- // inserting into a priority queue
- if (sz - numRemoved > acceptableWaterMark) {
-
- oldestEntry = newOldestEntry == Long.MAX_VALUE ? oldestEntry :
newOldestEntry;
- newOldestEntry = Long.MAX_VALUE;
- newestEntry = newNewestEntry;
- newNewestEntry = -1;
- wantToKeep = lowerWaterMark - numKept;
- wantToRemove = sz - lowerWaterMark - numRemoved;
-
- PQueue<K, V> queue = new PQueue<>(wantToRemove);
-
- for (int i = eSize - 1; i >= 0; i--) {
- CacheEntry<K, V> ce = eset[i];
- long thisEntry = ce.lastAccessedCopy;
-
- if (thisEntry > newestEntry - wantToKeep) {
- // this entry is guaranteed not to be in the bottom
- // group, so do nothing but remove it from the eset.
- numKept++;
- // removal not necessary on last pass.
- // eset[i] = eset[eSize-1];
- // eSize--;
-
- newOldestEntry = Math.min(thisEntry, newOldestEntry);
-
- } else if (thisEntry < oldestEntry + wantToRemove) { // entry in
bottom group?
- // this entry is guaranteed to be in the bottom group
- // so immediately remove it.
- evictEntry(ce.key);
- numRemoved++;
-
- // removal not necessary on last pass.
- // eset[i] = eset[eSize-1];
- // eSize--;
- } else {
- // This entry *could* be in the bottom group.
- // add it to the priority queue
-
- // everything in the priority queue will be removed, so keep track of
- // the lowest value that ever comes back out of the queue.
-
- // first reduce the size of the priority queue to account for
- // the number of items we have already removed while executing
- // this loop so far.
- queue.myMaxSize = sz - lowerWaterMark - numRemoved;
- while (queue.size() > queue.myMaxSize && queue.size() > 0) {
- CacheEntry<K, V> otherEntry = queue.pop();
- newOldestEntry = Math.min(otherEntry.lastAccessedCopy,
newOldestEntry);
- }
- if (queue.myMaxSize <= 0) break;
-
- CacheEntry<K, V> o = queue.myInsertWithOverflow(ce);
- if (o != null) {
- newOldestEntry = Math.min(o.lastAccessedCopy, newOldestEntry);
- }
- }
- }
-
- // Now delete everything in the priority queue.
- // avoid using pop() since order doesn't matter anymore
- for (CacheEntry<K, V> ce : queue.getValues()) {
- if (ce == null) continue;
- evictEntry(ce.key);
- numRemoved++;
- }
-
- // System.out.println("items removed:" + numRemoved + " numKept=" +
numKept + "
- // initialQueueSize="+ wantToRemove + " finalQueueSize=" + queue.size()
+ " sz-numRemoved=" +
- // (sz-numRemoved));
- }
-
- oldestEntry = newOldestEntry == Long.MAX_VALUE ? oldestEntry :
newOldestEntry;
- this.oldestEntry = oldestEntry;
- }
-
- private static class PQueue<K, V> extends PriorityQueue<CacheEntry<K, V>> {
- int myMaxSize;
- final Object[] heap;
-
- PQueue(int maxSz) {
- super(maxSz);
- heap = getHeapArray();
- myMaxSize = maxSz;
- }
-
- @SuppressWarnings("unchecked")
- Iterable<CacheEntry<K, V>> getValues() {
- return (Iterable)
Collections.unmodifiableCollection(Arrays.asList(heap));
- }
-
- @Override
- protected boolean lessThan(CacheEntry<K, V> a, CacheEntry<K, V> b) {
- // reverse the parameter order so that the queue keeps the oldest items
- return b.lastAccessedCopy < a.lastAccessedCopy;
- }
-
- // necessary because maxSize is private in base class
- @SuppressWarnings("unchecked")
- public CacheEntry<K, V> myInsertWithOverflow(CacheEntry<K, V> element) {
- if (size() < myMaxSize) {
- add(element);
- return null;
- } else if (size() > 0 && !lessThan(element, (CacheEntry<K, V>) heap[1]))
{
- CacheEntry<K, V> ret = (CacheEntry<K, V>) heap[1];
- heap[1] = element;
- updateTop();
- return ret;
- } else {
- return element;
- }
- }
- }
-
- private void evictEntry(K key) {
- CacheEntry<K, V> o = map.remove(key);
- postRemoveEntry(o);
- }
-
- private void postRemoveEntry(CacheEntry<K, V> o) {
- if (o == null) return;
- ramBytes.add(-(o.ramBytesUsed() + HASHTABLE_RAM_BYTES_PER_ENTRY));
- stats.size.decrement();
- stats.evictionCounter.increment();
- if (evictionListener != null) evictionListener.evictedEntry(o.key,
o.value);
- }
-
- /**
- * Returns 'n' number of oldest accessed entries present in this cache.
- *
- * <p>This uses a TreeSet to collect the 'n' oldest items ordered by
ascending last access time
- * and returns a LinkedHashMap containing 'n' or less than 'n' entries.
- *
- * @param n the number of oldest items needed
- * @return a LinkedHashMap containing 'n' or less than 'n' entries
- */
- public Map<K, V> getOldestAccessedItems(int n) {
- Map<K, V> result = new LinkedHashMap<>();
- if (n <= 0) return result;
- TreeSet<CacheEntry<K, V>> tree = new TreeSet<>();
- markAndSweepLock.lock();
- try {
- for (Map.Entry<Object, CacheEntry<K, V>> entry : map.entrySet()) {
- CacheEntry<K, V> ce = entry.getValue();
- ce.lastAccessedCopy = ce.lastAccessed;
- if (tree.size() < n) {
- tree.add(ce);
- } else {
- if (ce.lastAccessedCopy < tree.first().lastAccessedCopy) {
- tree.remove(tree.first());
- tree.add(ce);
- }
- }
- }
- } finally {
- markAndSweepLock.unlock();
- }
- for (CacheEntry<K, V> e : tree) {
- result.put(e.key, e.value);
- }
- return result;
- }
-
- public Map<K, V> getLatestAccessedItems(int n) {
- Map<K, V> result = new LinkedHashMap<>();
- if (n <= 0) return result;
- TreeSet<CacheEntry<K, V>> tree = new TreeSet<>();
- // we need to grab the lock since we are changing lastAccessedCopy
- markAndSweepLock.lock();
- try {
- for (Map.Entry<Object, CacheEntry<K, V>> entry : map.entrySet()) {
- CacheEntry<K, V> ce = entry.getValue();
- ce.lastAccessedCopy = ce.lastAccessed;
- if (tree.size() < n) {
- tree.add(ce);
- } else {
- if (ce.lastAccessedCopy > tree.last().lastAccessedCopy) {
- tree.remove(tree.last());
- tree.add(ce);
- }
- }
- }
- } finally {
- markAndSweepLock.unlock();
- }
- for (CacheEntry<K, V> e : tree) {
- result.put(e.key, e.value);
- }
- return result;
- }
-
- public int size() {
- return stats.size.intValue();
- }
-
- @Override
- public void clear() {
- map.clear();
- ramBytes.reset();
- }
-
- public Map<Object, CacheEntry<K, V>> getMap() {
- return map;
- }
-
- public static class CacheEntry<K, V> implements Comparable<CacheEntry<K,
V>>, Accountable {
- public static long BASE_RAM_BYTES_USED =
RamUsageEstimator.shallowSizeOf(CacheEntry.class);
-
- final K key;
- final V value;
- final long createTime;
- final long ramBytesUsed; // cache
- volatile long lastAccessed = 0;
- long lastAccessedCopy = 0;
-
- public CacheEntry(K key, V value, long createTime, long lastAccessed) {
- this.key = key;
- this.value = value;
- this.createTime = createTime;
- this.lastAccessed = lastAccessed;
- this.ramBytesUsed =
- BASE_RAM_BYTES_USED
- + RamUsageEstimator.sizeOfObject(key,
QUERY_DEFAULT_RAM_BYTES_USED)
- + RamUsageEstimator.sizeOfObject(value,
QUERY_DEFAULT_RAM_BYTES_USED);
- }
-
- public void setLastAccessed(long lastAccessed) {
- this.lastAccessed = lastAccessed;
- }
-
- @Override
- public int compareTo(CacheEntry<K, V> that) {
- if (this.lastAccessedCopy == that.lastAccessedCopy) return 0;
- return this.lastAccessedCopy < that.lastAccessedCopy ? 1 : -1;
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return value.equals(obj);
- }
-
- @Override
- public String toString() {
- return "key: " + key + " value: " + value + " lastAccessed:" +
lastAccessed;
- }
-
- @Override
- public long ramBytesUsed() {
- return ramBytesUsed;
- }
-
- @Override
- public Collection<Accountable> getChildResources() {
- return List.of();
- }
- }
-
- private boolean isDestroyed = false;
-
- public void destroy() {
- try {
- if (cleanupThread != null) {
- cleanupThread.stopThread();
- }
- } finally {
- isDestroyed = true;
- }
- }
-
- public Stats getStats() {
- return stats;
- }
-
- public static class Stats implements Accountable {
- private static final long RAM_BYTES_USED =
- // accounts for field refs
- RamUsageEstimator.shallowSizeOfInstance(Stats.class)
- +
- // LongAdder
- 6
- * (RamUsageEstimator.NUM_BYTES_ARRAY_HEADER
- + RamUsageEstimator.primitiveSizes.get(long.class)
- + 2L
- * (RamUsageEstimator.NUM_BYTES_OBJECT_REF
- +
RamUsageEstimator.primitiveSizes.get(long.class)))
- +
- // AtomicLong
- RamUsageEstimator.primitiveSizes.get(long.class);
-
- private final AtomicLong accessCounter = new AtomicLong(0);
- private final LongAdder putCounter = new LongAdder();
- private final LongAdder nonLivePutCounter = new LongAdder();
- private final LongAdder missCounter = new LongAdder();
- private final LongAdder size = new LongAdder();
- private LongAdder evictionCounter = new LongAdder();
- private LongAdder evictionIdleCounter = new LongAdder();
-
- public long getCumulativeLookups() {
- return (accessCounter.longValue() - putCounter.longValue() -
nonLivePutCounter.longValue())
- + missCounter.longValue();
- }
-
- public long getCumulativeHits() {
- return accessCounter.longValue() - putCounter.longValue() -
nonLivePutCounter.longValue();
- }
-
- public long getCumulativePuts() {
- return putCounter.longValue();
- }
-
- public long getCumulativeEvictions() {
- return evictionCounter.longValue();
- }
-
- public long getCumulativeIdleEvictions() {
- return evictionIdleCounter.longValue();
- }
-
- public int getCurrentSize() {
- return size.intValue();
- }
-
- public long getCumulativeNonLivePuts() {
- return nonLivePutCounter.longValue();
- }
-
- public long getCumulativeMisses() {
- return missCounter.longValue();
- }
-
- public void add(Stats other) {
- accessCounter.addAndGet(other.accessCounter.get());
- putCounter.add(other.putCounter.longValue());
- nonLivePutCounter.add(other.nonLivePutCounter.longValue());
- missCounter.add(other.missCounter.longValue());
- evictionCounter.add(other.evictionCounter.longValue());
- long maxSize = Math.max(size.longValue(), other.size.longValue());
- size.reset();
- size.add(maxSize);
- }
-
- @Override
- public long ramBytesUsed() {
- return RAM_BYTES_USED;
- }
- }
-
- public static interface EvictionListener<K, V> {
- public void evictedEntry(K key, V value);
- }
-
- private static class CleanupThread extends Thread {
- private final WeakReference<ConcurrentLRUCache<?, ?>> cache;
-
- private boolean stop = false;
-
- public CleanupThread(ConcurrentLRUCache<?, ?> c) {
- super("CacheCleanupThread");
- cache = new WeakReference<>(c);
- }
-
- @Override
- public void run() {
- while (true) {
- ConcurrentLRUCache<?, ?> c = cache.get();
- if (c == null) break;
- synchronized (this) {
- if (stop) break;
- long waitTimeMs =
- c.maxIdleTimeNs != Long.MAX_VALUE
- ? TimeUnit.MILLISECONDS.convert(c.maxIdleTimeNs,
TimeUnit.NANOSECONDS)
- : 0L;
- try {
- this.wait(waitTimeMs);
- } catch (InterruptedException e) {
- }
- }
- if (stop) break;
- c = cache.get();
- if (c == null) break;
- c.markAndSweep();
- }
- }
-
- void wakeThread() {
- synchronized (this) {
- this.notify();
- }
- }
-
- void stopThread() {
- synchronized (this) {
- stop = true;
- this.notify();
- }
- }
- }
-
- @Override
- public long ramBytesUsed() {
- return BASE_RAM_BYTES_USED + ramBytes.sum();
- }
-
- @Override
- public Collection<Accountable> getChildResources() {
- return List.of();
- }
-}
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/Cache.java
b/solr/solrj/src/java/org/apache/solr/common/util/Cache.java
deleted file mode 100644
index 4f589099485..00000000000
--- a/solr/solrj/src/java/org/apache/solr/common/util/Cache.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.common.util;
-
-import java.util.Objects;
-import java.util.function.Function;
-
-public interface Cache<K, V> {
- V put(K key, V val);
-
- V get(K key);
-
- V remove(K key);
-
- void clear();
-
- default V computeIfAbsent(K key, Function<? super K, ? extends V>
mappingFunction) {
- Objects.requireNonNull(mappingFunction);
- V v;
- if ((v = get(key)) == null) {
- V newValue;
- if ((newValue = mappingFunction.apply(key)) != null) {
- put(key, newValue);
- return newValue;
- }
- }
-
- return v;
- }
-}
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
b/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
index 63e6613ae25..f47ff63b980 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
@@ -1450,15 +1450,12 @@ public class JavaBinCodec implements PushWriter {
boolean wantsAllFields();
}
- public static class StringCache {
- private final Cache<StringBytes, String> cache;
-
- public StringCache(Cache<StringBytes, String> cache) {
- this.cache = cache;
- }
-
+ /**
+ * @lucene.internal
+ */
+ public abstract static class StringCache {
public String get(StringBytes b) {
- String result = cache.get(b);
+ String result = getFromCache(b);
if (result == null) {
// make a copy because the buffer received may be changed later by the
caller
StringBytes copy =
@@ -1467,10 +1464,14 @@ public class JavaBinCodec implements PushWriter {
CharArr arr = new CharArr();
ByteUtils.UTF8toUTF16(b.bytes, b.offset, b.length, arr);
result = arr.toString();
- cache.put(copy, result);
+ putIntoCache(copy, result);
}
return result;
}
+
+ protected abstract String getFromCache(StringBytes b);
+
+ protected abstract void putIntoCache(StringBytes b, String val);
}
@Override
diff --git
a/solr/solrj/src/java/org/apache/solr/common/util/MapBackedCache.java
b/solr/solrj/src/java/org/apache/solr/common/util/MapBackedCache.java
deleted file mode 100644
index c10520bb86e..00000000000
--- a/solr/solrj/src/java/org/apache/solr/common/util/MapBackedCache.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.common.util;
-
-import java.util.Map;
-import java.util.function.Function;
-
-// A cache backed by a map
-public class MapBackedCache<K, V> implements Cache<K, V> {
-
- protected final Map<K, V> map;
-
- public MapBackedCache(Map<K, V> map) {
- this.map = map;
- }
-
- public Map<K, V> asMap() {
- return map;
- }
-
- @Override
- public V put(K key, V val) {
- return map.put(key, val);
- }
-
- @Override
- public V get(K key) {
- return map.get(key);
- }
-
- @Override
- public V remove(K key) {
- return map.remove(key);
- }
-
- @Override
- public void clear() {
- map.clear();
- }
-
- @Override
- public V computeIfAbsent(K key, Function<? super K, ? extends V>
mappingFunction) {
- return map.computeIfAbsent(key, mappingFunction);
- }
-}
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/ObjectCache.java
b/solr/solrj/src/java/org/apache/solr/common/util/ObjectCache.java
index 096998b76c8..d3dd33a705a 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/ObjectCache.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/ObjectCache.java
@@ -19,18 +19,20 @@ package org.apache.solr.common.util;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import org.apache.solr.common.SolrCloseable;
/** Simple object cache with a type-safe accessor. */
-public class ObjectCache extends MapBackedCache<String, Object> implements
SolrCloseable {
+public class ObjectCache implements SolrCloseable {
+ protected final ConcurrentMap<String, Object> map;
private final AtomicBoolean isClosed = new AtomicBoolean(false);
public ObjectCache() {
- super(new ConcurrentHashMap<>());
+ this.map = new ConcurrentHashMap<>();
}
private void ensureNotClosed() {
@@ -39,28 +41,24 @@ public class ObjectCache extends MapBackedCache<String,
Object> implements SolrC
}
}
- @Override
public Object put(String key, Object val) {
ensureNotClosed();
- return super.put(key, val);
+ return map.put(key, val);
}
- @Override
public Object get(String key) {
ensureNotClosed();
- return super.get(key);
+ return map.get(key);
}
- @Override
public Object remove(String key) {
ensureNotClosed();
- return super.remove(key);
+ return map.remove(key);
}
- @Override
public void clear() {
ensureNotClosed();
- super.clear();
+ map.clear();
}
public <T> T get(String key, Class<T> clazz) {
@@ -72,11 +70,14 @@ public class ObjectCache extends MapBackedCache<String,
Object> implements SolrC
}
}
+ public Object computeIfAbsent(String key, Function<String, ?>
mappingFunction) {
+ ensureNotClosed();
+ return map.computeIfAbsent(key, mappingFunction);
+ }
+
public <T> T computeIfAbsent(
String key, Class<T> clazz, Function<String, ? extends T>
mappingFunction) {
- ensureNotClosed();
- Object o = super.computeIfAbsent(key, mappingFunction);
- return clazz.cast(o);
+ return clazz.cast(computeIfAbsent(key, mappingFunction));
}
@Override
diff --git
a/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
b/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
index 65a89bbc21b..07c4cb64cd3 100644
--- a/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
+++ b/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
@@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.lucene.tests.util.TestUtil;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.EnumFieldValue;
@@ -39,7 +40,6 @@ import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.SolrInputField;
-import org.apache.solr.util.ConcurrentLRUCache;
import org.apache.solr.util.RTimer;
import org.junit.Test;
import org.noggit.CharArr;
@@ -480,7 +480,19 @@ public class TestJavaBinCodec extends SolrTestCaseJ4 {
assertNotSame(l1.get(1), l2.get(1));
JavaBinCodec.StringCache stringCache =
- new JavaBinCodec.StringCache(new MapBackedCache<>(new HashMap<>()));
+ new JavaBinCodec.StringCache() {
+ Map<StringBytes, String> cache = new HashMap<>();
+
+ @Override
+ protected String getFromCache(StringBytes b) {
+ return cache.get(b);
+ }
+
+ @Override
+ protected void putIntoCache(StringBytes b, String val) {
+ cache.put(b, val);
+ }
+ };
try (JavaBinCodec c1 = new JavaBinCodec(null, stringCache);
JavaBinCodec c2 = new JavaBinCodec(null, stringCache)) {
@@ -525,21 +537,24 @@ public class TestJavaBinCodec extends SolrTestCaseJ4 {
private void testPerf() throws InterruptedException {
final ArrayList<StringBytes> l = new ArrayList<>();
- Cache<StringBytes, String> cache = null;
- /* cache = new ConcurrentLRUCache<JavaBinCodec.StringBytes,String>(10000,
9000, 10000, 1000, false, true, null){
- @Override
- public String put(JavaBinCodec.StringBytes key, String val) {
- l.add(key);
- return super.put(key, val);
- }
- };*/
Runtime.getRuntime().gc();
printMem("before cache init");
- Cache<StringBytes, String> cache1 = new MapBackedCache<>(new HashMap<>());
- final JavaBinCodec.StringCache STRING_CACHE = new
JavaBinCodec.StringCache(cache1);
+ final JavaBinCodec.StringCache STRING_CACHE =
+ new JavaBinCodec.StringCache() {
+ Map<StringBytes, String> cache = new HashMap<>();
+
+ @Override
+ protected String getFromCache(StringBytes b) {
+ return cache.get(b);
+ }
+
+ @Override
+ protected void putIntoCache(StringBytes b, String val) {
+ cache.put(b, val);
+ }
+ };
- // STRING_CACHE = new JavaBinCodec.StringCache(cache);
byte[] bytes = new byte[0];
StringBytes stringBytes = new StringBytes(null, 0, 0);
@@ -656,14 +671,26 @@ public class TestJavaBinCodec extends SolrTestCaseJ4 {
int ret = 0;
final RTimer timer = new RTimer();
- ConcurrentLRUCache<StringBytes, String> underlyingCache =
- cacheSz > 0
- ? new ConcurrentLRUCache<>(
- cacheSz, cacheSz - cacheSz / 10, cacheSz, cacheSz / 10, false,
true, null)
- : null; // the cache in the first version of the patch was
- // 10000,9000,10000,1000,false,true,null
- final JavaBinCodec.StringCache stringCache =
- underlyingCache == null ? null : new
JavaBinCodec.StringCache(underlyingCache);
+ final JavaBinCodec.StringCache stringCache;
+ if (cacheSz > 0) {
+ stringCache =
+ new JavaBinCodec.StringCache() {
+ private final Map<StringBytes, String> cache = new
ConcurrentHashMap<>(cacheSz);
+
+ @Override
+ protected String getFromCache(StringBytes b) {
+ return cache.get(b);
+ }
+
+ @Override
+ protected void putIntoCache(StringBytes b, String val) {
+ cache.put(b, val);
+ }
+ };
+ } else {
+ stringCache = null;
+ }
+
if (nThreads <= 0) {
ret += doDecode(buffers, iter, stringCache);
} else {
@@ -680,14 +707,6 @@ public class TestJavaBinCodec extends SolrTestCaseJ4 {
long n = iter * Math.max(1, nThreads);
System.out.println("ret=" + ret + " THROUGHPUT=" + (n * 1000 /
timer.getTime()));
- if (underlyingCache != null)
- System.out.println(
- "cache: hits="
- + underlyingCache.getStats().getCumulativeHits()
- + " lookups="
- + underlyingCache.getStats().getCumulativeLookups()
- + " size="
- + underlyingCache.getStats().getCurrentSize());
}
public static int doDecode(byte[][] buffers, long iter,
JavaBinCodec.StringCache stringCache)