Skip to content

Commit a56b238

Browse files
committed
Fix unit factor usage in Caffeine cache (fixes #75)
1 parent 4e19e7a commit a56b238

File tree

3 files changed

+100
-35
lines changed

3 files changed

+100
-35
lines changed

prj/coherence-core/src/main/java/com/oracle/coherence/caffeine/CaffeineCache.java

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
* amortized {@code O(1)} expiration.
5757
* <p>
5858
* This implementation does not support providing an {@link EvictionPolicy} or
59-
* {@link EvictionApprover}, and always uses TinyLFU policy. The maximum size is
60-
* set by {@link #setHighUnits(int)} and the low watermark, {@link
59+
* {@link EvictionApprover}, and always uses the TinyLFU policy. The maximum
60+
* size is set by {@link #setHighUnits(int)} and the low watermark, {@link
6161
* #setLowUnits(int)}, has no effect. Cache entries do not support {@code
6262
* touch()}, {@code getTouchCount()}, {@code getLastTouchMillis()}, or {@code
6363
* setUnits(c)}. By default, the cache is unbounded and will not be limited by
@@ -196,21 +196,21 @@ else if (cMillis == EXPIRY_DEFAULT)
196196
@SuppressWarnings("OptionalGetWithoutIsPresent")
197197
public int getUnits()
198198
{
199-
return (int) Math.min(f_eviction.weightedSize().getAsLong(), Integer.MAX_VALUE);
199+
return toExternalUnits(f_eviction.weightedSize().getAsLong(), getUnitFactor());
200200
}
201201

202202
// ---- ConfigurableCacheMap interface ----------------------------------
203203

204204
@Override
205205
public int getHighUnits()
206206
{
207-
return (int) Math.min(f_eviction.getMaximum(), Integer.MAX_VALUE);
207+
return toExternalUnits(f_eviction.getMaximum(), getUnitFactor());
208208
}
209209

210210
@Override
211211
public synchronized void setHighUnits(int units)
212212
{
213-
f_eviction.setMaximum(units);
213+
f_eviction.setMaximum(toInternalUnits(units, getUnitFactor()));
214214
}
215215

216216
@Override
@@ -243,7 +243,19 @@ else if (!isEmpty())
243243
throw new IllegalStateException(
244244
"The unit factor cannot be set after the cache has been populated");
245245
}
246-
this.m_nUnitFactor = nUnitFactor;
246+
247+
// only adjust the max units if there was no unit factor set previously
248+
if (m_nUnitFactor == 1)
249+
{
250+
long cCurrentMaxUnits = f_eviction.getMaximum();
251+
if (cCurrentMaxUnits < (Long.MAX_VALUE - Integer.MAX_VALUE))
252+
{
253+
long cMaxUnits = (cCurrentMaxUnits * nUnitFactor);
254+
f_eviction.setMaximum((cMaxUnits < 0) ? Long.MAX_VALUE : cMaxUnits);
255+
}
256+
}
257+
258+
m_nUnitFactor = nUnitFactor;
247259
}
248260

249261
@Override
@@ -266,6 +278,41 @@ public void setUnitCalculator(UnitCalculator calculator)
266278
}
267279
}
268280

281+
/**
282+
* Convert from an external 32-bit unit value to an internal 64-bit unit
283+
* value using the configured units factor.
284+
*
285+
* @param cUnits an external 32-bit units value
286+
* @param nFactor the unit factor
287+
*
288+
* @return an internal 64-bit units value
289+
*/
290+
protected static long toInternalUnits(int cUnits, int nFactor)
291+
{
292+
return (cUnits <= 0) || (cUnits == Integer.MAX_VALUE)
293+
? Long.MAX_VALUE
294+
: ((long) cUnits) * nFactor;
295+
}
296+
297+
/**
298+
* Convert from an internal 64-bit unit value to an external 32-bit unit
299+
* value using the configured units factor.
300+
*
301+
* @param cUnits an internal 64-bit units value
302+
* @param nFactor the unit factor
303+
*
304+
* @return an external 32-bit units value
305+
*/
306+
protected static int toExternalUnits(long cUnits, int nFactor)
307+
{
308+
if (nFactor > 1)
309+
{
310+
cUnits = (cUnits + nFactor - 1) / nFactor;
311+
}
312+
313+
return (cUnits > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) cUnits;
314+
}
315+
269316
@Override
270317
public void evict(Object oKey)
271318
{
@@ -705,13 +752,7 @@ public String toString()
705752
*/
706753
private int weigh(Object oKey, Object oValue)
707754
{
708-
int cUnits = m_unitCalculator.calculateUnits(oKey, oValue);
709-
if (cUnits < 0)
710-
{
711-
throw new IllegalStateException(String.format(
712-
"Negative unit (%s) for %s=%s", cUnits, oKey, oValue));
713-
}
714-
return (cUnits / m_nUnitFactor);
755+
return m_unitCalculator.calculateUnits(oKey, oValue);
715756
}
716757

717758
/**
@@ -722,8 +763,12 @@ private int weigh(Object oKey, Object oValue)
722763
*/
723764
private void notifyCreate(Object oKey, Object oValueNew)
724765
{
725-
fireEvent(new CacheEvent(this, MapEvent.ENTRY_INSERTED,
726-
oKey, null, oValueNew, false));
766+
if (!f_listeners.isEmpty())
767+
{
768+
CacheEvent event = new CacheEvent(this, MapEvent.ENTRY_INSERTED,
769+
oKey, null, oValueNew, false);
770+
f_listeners.fireEvent(event, false);
771+
}
727772
}
728773

729774
/**
@@ -735,8 +780,12 @@ private void notifyCreate(Object oKey, Object oValueNew)
735780
*/
736781
private void notifyUpdate(Object oKey, Object oValueOld, Object oValueNew)
737782
{
738-
fireEvent(new CacheEvent(this, MapEvent.ENTRY_UPDATED,
739-
oKey, oValueOld, oValueNew, false));
783+
if (!f_listeners.isEmpty())
784+
{
785+
CacheEvent event = new CacheEvent(this, MapEvent.ENTRY_UPDATED,
786+
oKey, oValueOld, oValueNew, false);
787+
f_listeners.fireEvent(event, false);
788+
}
740789
}
741790

742791
/**
@@ -748,8 +797,12 @@ private void notifyUpdate(Object oKey, Object oValueOld, Object oValueNew)
748797
*/
749798
private void notifyDelete(Object oKey, Object oValueOld)
750799
{
751-
fireEvent(new CacheEvent(this, MapEvent.ENTRY_DELETED,
752-
oKey, oValueOld, null, false));
800+
if (!f_listeners.isEmpty())
801+
{
802+
CacheEvent event = new CacheEvent(this, MapEvent.ENTRY_DELETED,
803+
oKey, oValueOld, null, false);
804+
f_listeners.fireEvent(event, false);
805+
}
753806
}
754807

755808
/**
@@ -761,22 +814,13 @@ private void notifyDelete(Object oKey, Object oValueOld)
761814
* @param removalCause the eviction type (size, expired)
762815
*/
763816
private void notifyEvicted(Object oKey, Object oValueOld, RemovalCause removalCause)
764-
{
765-
boolean fExpired = removalCause == RemovalCause.EXPIRED;
766-
fireEvent(new CacheEvent(this, MapEvent.ENTRY_DELETED,
767-
oKey, oValueOld, null, true,
768-
TransformationState.TRANSFORMABLE, false, fExpired));
769-
}
770-
771-
/**
772-
* Fire the specified cache event.
773-
*
774-
* @param event the cache event to fire
775-
*/
776-
private void fireEvent(CacheEvent event)
777817
{
778818
if (!f_listeners.isEmpty())
779819
{
820+
boolean fExpired = removalCause == RemovalCause.EXPIRED;
821+
CacheEvent event = new CacheEvent(this, MapEvent.ENTRY_DELETED, oKey, oValueOld,
822+
null, true, TransformationState.TRANSFORMABLE,
823+
false, fExpired);
780824
f_listeners.fireEvent(event, false);
781825
}
782826
}
@@ -1247,7 +1291,7 @@ public void setExpiryMillis(long cMillis)
12471291
@Override
12481292
public int getUnits()
12491293
{
1250-
return f_nWeight * m_nUnitFactor;
1294+
return f_nWeight;
12511295
}
12521296

12531297
@Override

prj/coherence-dependencies/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@
156156
<asm.version>9.3</asm.version>
157157
<bdb.version>6.2.31</bdb.version>
158158
<bnd.version>5.2.0</bnd.version>
159-
<caffeine.version>3.1.0</caffeine.version>
159+
<caffeine.version>3.1.1</caffeine.version>
160160
<codemodel.version>2.6</codemodel.version>
161161
<com.oracle.ipc.version>12.1.4-150528</com.oracle.ipc.version>
162162
<!-- NOTE: this version should ideally be in sync' with that used by Helidon microprofile -->

prj/test/unit/coherence-core-tests/src/test/java/com/oracle/coherence/caffeine/CaffeineCacheTest.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void mapListener(ConfigurableCacheMap cache)
7676

7777
@ParameterizedTest
7878
@MethodSource("caches")
79-
public void weight(ConfigurableCacheMap cache)
79+
public void smallUnits(ConfigurableCacheMap cache)
8080
{
8181
cache.setUnitCalculator(new FixedCalculator(10));
8282
cache.setHighUnits(100);
@@ -90,6 +90,27 @@ public void weight(ConfigurableCacheMap cache)
9090
assertThat(cache.getUnits(), is(10));
9191
}
9292

93+
@ParameterizedTest
94+
@MethodSource("caches")
95+
public void largeUnits(ConfigurableCacheMap cache)
96+
{
97+
int cSize = 4;
98+
int nUnitFactor = 8;
99+
int cMaximum = Integer.MAX_VALUE;
100+
101+
cache.setHighUnits(cMaximum);
102+
cache.setUnitFactor(nUnitFactor);
103+
cache.setUnitCalculator(new FixedCalculator(cMaximum));
104+
for (int i = 0; i < cSize; i++)
105+
{
106+
cache.put(i, i);
107+
assertThat(cache.getCacheEntry(i).getUnits(), is(cMaximum));
108+
}
109+
110+
int cExpectedUnits = (int) ((((long) cSize * cMaximum) + nUnitFactor - 1) / nUnitFactor);
111+
assertThat(cache.getUnits(), is(cExpectedUnits));
112+
}
113+
93114
@ParameterizedTest
94115
@MethodSource("caches")
95116
public void statistics(ConfigurableCacheMap cache)

0 commit comments

Comments
 (0)