Skip to content

Commit e63f53c

Browse files
committed
fix
1 parent a654b3f commit e63f53c

2 files changed

Lines changed: 40 additions & 7 deletions

File tree

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ConcurrentCache.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.util.Arrays;
1515
import java.util.Objects;
16+
import java.util.concurrent.ConcurrentHashMap;
1617

1718
/**
1819
* Fixed-size concurrent cache with approximate FIFO eviction per hash set. The cache never grows beyond its slot
@@ -21,16 +22,20 @@
2122
public class ConcurrentCache<K, V> {
2223

2324
private static final int WAYS = 4;
25+
private static final float LOAD_FACTOR = 0.75f;
2426

2527
private final Entry<K, V>[] entries;
2628
private final int[] nextVictim;
2729
private final int setMask;
2830
private final int setShift;
2931

32+
protected final ConcurrentHashMap<K, V> cache;
33+
3034
private volatile long generation = 1;
3135

3236
@SuppressWarnings("unchecked")
3337
public ConcurrentCache(int capacity) {
38+
cache = new ConcurrentHashMap<>((int) (Math.max(1, capacity) / LOAD_FACTOR), LOAD_FACTOR);
3439
if (capacity <= 0) {
3540
entries = (Entry<K, V>[]) new Entry[0];
3641
nextVictim = new int[0];
@@ -50,7 +55,7 @@ public ConcurrentCache(int capacity) {
5055
public V get(Object key) {
5156
Objects.requireNonNull(key);
5257
if (entries.length == 0) {
53-
return null;
58+
return cache.get(key);
5459
}
5560

5661
int hash = spread(key.hashCode());
@@ -65,14 +70,17 @@ && sameKey(key, entry.key)) {
6570
return entry.value;
6671
}
6772
}
68-
return null;
73+
return cache.get(key);
6974
}
7075

7176
public V put(K key, V value) {
7277
Objects.requireNonNull(key);
7378
Objects.requireNonNull(value);
79+
cleanUp();
80+
81+
V previous = cache.put(key, value);
7482
if (entries.length == 0) {
75-
return null;
83+
return previous;
7684
}
7785

7886
int hash = spread(key.hashCode());
@@ -96,13 +104,13 @@ public V put(K key, V value) {
96104
if (entry.value != value) {
97105
entries[slot] = new Entry<>(key, value, hash, currentGeneration);
98106
}
99-
return entry.value;
107+
return previous;
100108
}
101109
}
102110

103111
if (emptySlot >= 0) {
104112
entries[emptySlot] = new Entry<>(key, value, hash, currentGeneration);
105-
return null;
113+
return previous;
106114
}
107115

108116
int firstVictim = nextVictim[setIndex] & (WAYS - 1);
@@ -112,17 +120,21 @@ public V put(K key, V value) {
112120
Entry<K, V> victim = entries[slot];
113121
if (victim == null || victim.generation != currentGeneration || onEntryRemoval(victim.key)) {
114122
entries[slot] = new Entry<>(key, value, hash, currentGeneration);
123+
if (victim != null && victim.generation == currentGeneration && !sameKey(victim.key, key)) {
124+
cache.remove(victim.key, victim.value);
125+
}
115126
nextVictim[setIndex] = (victimOffset + 1) & (WAYS - 1);
116-
return null;
127+
return previous;
117128
}
118129
}
119130

120-
return null;
131+
return previous;
121132
}
122133

123134
public void clear() {
124135
long nextGeneration = generation + 1;
125136
generation = nextGeneration == 0 ? 1 : nextGeneration;
137+
cache.clear();
126138
Arrays.fill(entries, null);
127139
Arrays.fill(nextVictim, 0);
128140
}
@@ -136,6 +148,10 @@ protected boolean onEntryRemoval(K key) {
136148
return true;
137149
}
138150

151+
protected void cleanUp() {
152+
// Legacy hook. Eviction happens during put.
153+
}
154+
139155
private int setBase(int hash) {
140156
return (hash & setMask) * WAYS;
141157
}

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/ValueStoreNamespaceCacheTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
1010
*******************************************************************************/
11+
// Some portions generated by Codex
1112
package org.eclipse.rdf4j.sail.lmdb;
1213

1314
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
1416

1517
import java.io.File;
1618
import java.lang.invoke.MethodHandle;
1719
import java.lang.invoke.MethodHandles;
1820
import java.lang.invoke.MethodType;
1921
import java.lang.reflect.Field;
22+
import java.lang.reflect.Method;
23+
import java.lang.reflect.Modifier;
2024
import java.util.concurrent.atomic.AtomicInteger;
2125

2226
import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig;
@@ -25,6 +29,19 @@
2529

2630
class ValueStoreNamespaceCacheTest {
2731

32+
@Test
33+
void legacyProtectedApiRemainsAvailable() throws Exception {
34+
Field cacheField = ConcurrentCache.class.getDeclaredField("cache");
35+
assertNotNull(cacheField);
36+
assertEquals("cache", cacheField.getName());
37+
assertEquals(Modifier.PROTECTED | Modifier.FINAL, cacheField.getModifiers());
38+
39+
Method cleanUp = ConcurrentCache.class.getDeclaredMethod("cleanUp");
40+
assertNotNull(cleanUp);
41+
assertEquals(void.class, cleanUp.getReturnType());
42+
assertEquals(Modifier.PROTECTED, cleanUp.getModifiers());
43+
}
44+
2845
@Test
2946
void getNamespaceUsesLastResult(@TempDir File dataDir) throws Throwable {
3047
ValueStore valueStore = new ValueStore(new File(dataDir, "values"), new LmdbStoreConfig());

0 commit comments

Comments
 (0)