This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new c60a3b83320 Optimized the dual key cache memory computation
c60a3b83320 is described below
commit c60a3b833201080fe1a0377dec79d4197e3f9e42
Author: Caideyipi <[email protected]>
AuthorDate: Tue Apr 15 16:48:59 2025 +0800
Optimized the dual key cache memory computation
---
.../cache/schema/dualkeycache/IDualKeyCache.java | 28 +-
.../dualkeycache/impl/CacheEntryGroupImpl.java | 57 ++-
.../cache/schema/dualkeycache/impl/CacheStats.java | 47 +-
.../schema/dualkeycache/impl/DualKeyCacheImpl.java | 526 +++++++--------------
.../schema/dualkeycache/impl/ICacheEntryGroup.java | 13 +-
.../planner/plan/node/write/InsertTabletNode.java | 6 +-
.../node/write/RelationalInsertTabletNode.java | 14 +-
.../db/storageengine/dataregion/DataRegion.java | 4 +-
.../cache/dualkeycache/DualKeyCacheTest.java | 171 -------
9 files changed, 263 insertions(+), 603 deletions(-)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/IDualKeyCache.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/IDualKeyCache.java
index f6e20ad58c7..9552a0f6ea6 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/IDualKeyCache.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/IDualKeyCache.java
@@ -19,8 +19,6 @@
package org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache;
-import org.apache.iotdb.commons.utils.TestOnly;
-
import javax.annotation.concurrent.GuardedBy;
import java.util.function.Predicate;
@@ -37,22 +35,7 @@ import java.util.function.ToIntFunction;
public interface IDualKeyCache<FK, SK, V> {
/** Get the cache value with given first key and second key. */
- V get(FK firstKey, SK secondKey);
-
- /**
- * Traverse target cache values via given first key and second keys provided
in computation and
- * execute the defined computation logic. The computation is read only.
- */
- void compute(IDualKeyCacheComputation<FK, SK, V> computation);
-
- /**
- * Traverse target cache values via given first key and second keys provided
in computation and
- * execute the defined computation logic. Value can be updated in this
computation.
- */
- void updateWithLock(final IDualKeyCacheUpdating<FK, SK, V> updating);
-
- /** put the cache value into cache */
- void put(final FK firstKey, final SK secondKey, final V value);
+ V get(final FK firstKey, final SK secondKey);
/**
* Update the existing value. The updater shall return the difference caused
by the update,
@@ -102,18 +85,9 @@ public interface IDualKeyCache<FK, SK, V> {
@GuardedBy("DataNodeSchemaCache#writeLock")
void invalidateAll();
- /**
- * Clean up all data and info of this cache, including cache keys, cache
values and cache stats.
- */
- @GuardedBy("DataNodeSchemaCache#writeLock")
- void cleanUp();
-
/** Return all the current cache status and statistics. */
IDualKeyCacheStats stats();
- @TestOnly
- void evictOneEntry();
-
/** remove all entries for firstKey */
@GuardedBy("DataNodeSchemaCache#writeLock")
void invalidate(final FK firstKey);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheEntryGroupImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheEntryGroupImpl.java
index 2847a3d1ccc..54c53a15d50 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheEntryGroupImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheEntryGroupImpl.java
@@ -19,22 +19,36 @@
package
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.impl;
+import org.apache.tsfile.utils.RamUsageEstimator;
+
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Function;
public class CacheEntryGroupImpl<FK, SK, V, T extends ICacheEntry<SK, V>>
implements ICacheEntryGroup<FK, SK, V, T> {
+ private static final long INSTANCE_SIZE =
+ RamUsageEstimator.shallowSizeOfInstance(CacheEntryGroupImpl.class)
+ + RamUsageEstimator.shallowSizeOfInstance(AtomicLong.class)
+ + RamUsageEstimator.shallowSizeOfInstance(ConcurrentHashMap.class)
+ // Calculate the outer entry of the "firstKeyMap" here
+ + RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_ENTRY;
+
private final FK firstKey;
private final Map<SK, T> cacheEntryMap = new ConcurrentHashMap<>();
+ private final ICacheSizeComputer<FK, SK, V> sizeComputer;
+ private final AtomicLong memory;
- CacheEntryGroupImpl(final FK firstKey) {
+ CacheEntryGroupImpl(final FK firstKey, final ICacheSizeComputer<FK, SK, V>
sizeComputer) {
this.firstKey = firstKey;
+ this.sizeComputer = sizeComputer;
+ this.memory = new AtomicLong(INSTANCE_SIZE +
sizeComputer.computeFirstKeySize(firstKey));
}
@Override
@@ -53,30 +67,49 @@ public class CacheEntryGroupImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
}
@Override
- public T computeCacheEntry(final SK secondKey, final BiFunction<SK, T, T>
computation) {
- return cacheEntryMap.compute(secondKey, computation);
+ public T computeCacheEntry(
+ final SK secondKey, final Function<AtomicLong, BiFunction<SK, T, T>>
computation) {
+ return cacheEntryMap.compute(secondKey, computation.apply(memory));
}
@Override
- public T computeCacheEntryIfAbsent(final SK secondKey, final Function<SK, T>
computation) {
- return cacheEntryMap.computeIfAbsent(secondKey, computation);
+ public long removeCacheEntry(final SK secondKey) {
+ final T result = cacheEntryMap.remove(secondKey);
+ if (Objects.nonNull(result)) {
+ final long delta =
+ sizeComputer.computeSecondKeySize(result.getSecondKey())
+ + sizeComputer.computeValueSize(result.getValue())
+ + RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_ENTRY;
+ memory.addAndGet(-delta);
+ return delta;
+ }
+ return 0;
}
@Override
- public T removeCacheEntry(final SK secondKey) {
- return cacheEntryMap.remove(secondKey);
+ public boolean isEmpty() {
+ return cacheEntryMap.isEmpty();
}
@Override
- public boolean isEmpty() {
- return cacheEntryMap.isEmpty();
+ public long getMemory() {
+ return memory.get();
+ }
+
+ @Override
+ public int getEntriesCount() {
+ return cacheEntryMap.size();
}
@Override
public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- CacheEntryGroupImpl<?, ?, ?, ?> that = (CacheEntryGroupImpl<?, ?, ?, ?>) o;
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final CacheEntryGroupImpl<?, ?, ?, ?> that = (CacheEntryGroupImpl<?, ?, ?,
?>) o;
return Objects.equals(firstKey, that.firstKey);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheStats.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheStats.java
index 99c331962a2..0628d35db16 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheStats.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/CacheStats.java
@@ -22,6 +22,7 @@ package
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.i
import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheStats;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
class CacheStats implements IDualKeyCacheStats {
@@ -30,26 +31,23 @@ class CacheStats implements IDualKeyCacheStats {
private final long memoryThreshold;
- private final AtomicLong memoryUsage = new AtomicLong(0);
- private final AtomicLong entriesCount = new AtomicLong(0);
+ private final Supplier<Long> memoryComputation;
+ private final Supplier<Long> entriesComputation;
private final AtomicLong requestCount = new AtomicLong(0);
private final AtomicLong hitCount = new AtomicLong(0);
- CacheStats(long memoryCapacity) {
+ CacheStats(
+ long memoryCapacity,
+ final Supplier<Long> memoryComputation,
+ final Supplier<Long> entriesComputation) {
this.memoryThreshold = (long) (memoryCapacity * MEMORY_THRESHOLD_RATIO);
+ this.memoryComputation = memoryComputation;
+ this.entriesComputation = entriesComputation;
}
- void increaseMemoryUsage(int size) {
- memoryUsage.getAndAdd(size);
- }
-
- void decreaseMemoryUsage(int size) {
- memoryUsage.getAndAdd(-size);
- }
-
- boolean isExceedMemoryCapacity() {
- return memoryUsage.get() > memoryThreshold;
+ long getExceedMemory() {
+ return memoryUsage() - memoryThreshold;
}
void recordHit(int num) {
@@ -69,14 +67,6 @@ class CacheStats implements IDualKeyCacheStats {
requestCount.getAndAdd(num);
}
- void increaseEntryCount() {
- entriesCount.incrementAndGet();
- }
-
- void decreaseEntryCount() {
- entriesCount.decrementAndGet();
- }
-
@Override
public long requestCount() {
return requestCount.get();
@@ -102,7 +92,7 @@ class CacheStats implements IDualKeyCacheStats {
@Override
public long memoryUsage() {
- return memoryUsage.get();
+ return memoryComputation.get();
}
@Override
@@ -112,17 +102,6 @@ class CacheStats implements IDualKeyCacheStats {
@Override
public long entriesCount() {
- return entriesCount.get();
- }
-
- void reset() {
- resetMemoryUsageAndEntriesCount();
- hitCount.set(0);
- requestCount.set(0);
- }
-
- void resetMemoryUsageAndEntriesCount() {
- memoryUsage.set(0);
- entriesCount.set(0);
+ return entriesComputation.get();
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/DualKeyCacheImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/DualKeyCacheImpl.java
index d165abbf210..f88be9c3366 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/DualKeyCacheImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/DualKeyCacheImpl.java
@@ -19,11 +19,10 @@
package
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.impl;
-import org.apache.iotdb.commons.utils.TestOnly;
import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCache;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheComputation;
import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheStats;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheUpdating;
+
+import org.apache.tsfile.utils.RamUsageEstimator;
import javax.annotation.Nonnull;
@@ -34,8 +33,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
@@ -57,7 +54,7 @@ class DualKeyCacheImpl<FK, SK, V, T extends ICacheEntry<SK,
V>>
final long memoryCapacity) {
this.cacheEntryManager = cacheEntryManager;
this.sizeComputer = sizeComputer;
- this.cacheStats = new CacheStats(memoryCapacity);
+ this.cacheStats = new CacheStats(memoryCapacity, this::getMemory,
this::getEntriesCount);
}
@Override
@@ -79,106 +76,6 @@ class DualKeyCacheImpl<FK, SK, V, T extends ICacheEntry<SK,
V>>
}
}
- @Override
- public void compute(final IDualKeyCacheComputation<FK, SK, V> computation) {
- final FK firstKey = computation.getFirstKey();
- final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.get(firstKey);
- final SK[] secondKeyList = computation.getSecondKeyList();
- if (cacheEntryGroup == null) {
- for (int i = 0; i < secondKeyList.length; i++) {
- computation.computeValue(i, null);
- }
- cacheStats.recordMiss(secondKeyList.length);
- } else {
- T cacheEntry;
- int hitCount = 0;
- for (int i = 0; i < secondKeyList.length; i++) {
- cacheEntry = cacheEntryGroup.getCacheEntry(secondKeyList[i]);
- if (cacheEntry == null) {
- computation.computeValue(i, null);
- } else {
- computation.computeValue(i, cacheEntry.getValue());
- cacheEntryManager.access(cacheEntry);
- hitCount++;
- }
- }
- cacheStats.recordHit(hitCount);
- cacheStats.recordMiss(secondKeyList.length - hitCount);
- }
- }
-
- @Override
- public void updateWithLock(final IDualKeyCacheUpdating<FK, SK, V> updating) {
- final FK firstKey = updating.getFirstKey();
- final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.get(firstKey);
- final SK[] secondKeyList = updating.getSecondKeyList();
- if (cacheEntryGroup == null) {
- for (int i = 0; i < secondKeyList.length; i++) {
- updating.updateValue(i, null);
- }
- cacheStats.recordMiss(secondKeyList.length);
- } else {
- T cacheEntry;
- for (int i = 0; i < secondKeyList.length; i++) {
- cacheEntry = cacheEntryGroup.getCacheEntry(secondKeyList[i]);
- if (cacheEntry == null) {
- updating.updateValue(i, null);
- } else {
- int changeSize = 0;
- synchronized (cacheEntry) {
- if (cacheEntry.getBelongedGroup() != null) {
- // Only update the value when the cache entry is not evicted.
- // If the cache entry is evicted, getBelongedGroup is null.
- // Synchronized is to guarantee the cache entry is not evicted
during the update.
- changeSize = updating.updateValue(i, cacheEntry.getValue());
- cacheEntryManager.access(cacheEntry);
- }
- }
- if (changeSize > 0) {
- increaseMemoryUsageAndMayEvict(changeSize);
- }
- }
- }
- }
- }
-
- @Override
- public void put(final FK firstKey, final SK secondKey, final V value) {
- final AtomicInteger usedMemorySize = new AtomicInteger(0);
- firstKeyMap.compute(
- firstKey,
- (k, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- cacheEntryGroup = new CacheEntryGroupImpl<>(firstKey);
-
usedMemorySize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- }
- final ICacheEntryGroup<FK, SK, V, T> finalCacheEntryGroup =
cacheEntryGroup;
- cacheEntryGroup.computeCacheEntry(
- secondKey,
- (sk, cacheEntry) -> {
- if (cacheEntry == null) {
- cacheEntry =
- cacheEntryManager.createCacheEntry(secondKey, value,
finalCacheEntryGroup);
- cacheEntryManager.put(cacheEntry);
- cacheStats.increaseEntryCount();
-
usedMemorySize.getAndAdd(sizeComputer.computeSecondKeySize(sk));
- } else {
- final V existingValue = cacheEntry.getValue();
- if (existingValue != value && !existingValue.equals(value)) {
- cacheEntry.replaceValue(value);
-
usedMemorySize.getAndAdd(-sizeComputer.computeValueSize(existingValue));
- }
- // update the cache status
- cacheEntryManager.access(cacheEntry);
- }
- usedMemorySize.getAndAdd(sizeComputer.computeValueSize(value));
- return cacheEntry;
- });
- return cacheEntryGroup;
- });
- increaseMemoryUsageAndMayEvict(usedMemorySize.get());
- }
-
@Override
public void update(
final FK firstKey,
@@ -186,76 +83,65 @@ class DualKeyCacheImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
final V value,
final ToIntFunction<V> updater,
final boolean createIfNotExists) {
- final AtomicInteger usedMemorySize = new AtomicInteger(0);
-
- firstKeyMap.compute(
- firstKey,
- (k, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- if (!createIfNotExists) {
- return null;
- }
- cacheEntryGroup = new CacheEntryGroupImpl<>(firstKey);
-
usedMemorySize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- }
- final ICacheEntryGroup<FK, SK, V, T> finalCacheEntryGroup =
cacheEntryGroup;
-
- final T cacheEntry =
- createIfNotExists
- ? cacheEntryGroup.computeCacheEntryIfAbsent(
- secondKey,
- sk -> {
- final T entry =
- cacheEntryManager.createCacheEntry(
- secondKey, value, finalCacheEntryGroup);
- cacheEntryManager.put(entry);
- cacheStats.increaseEntryCount();
- usedMemorySize.getAndAdd(
- sizeComputer.computeSecondKeySize(sk)
- +
sizeComputer.computeValueSize(entry.getValue()));
- return entry;
- })
- : cacheEntryGroup.getCacheEntry(secondKey);
-
- if (Objects.nonNull(cacheEntry)) {
- final int result = updater.applyAsInt(cacheEntry.getValue());
- if (Objects.nonNull(cacheEntryGroup.getCacheEntry(secondKey))) {
- usedMemorySize.getAndAdd(result);
- }
- }
- return cacheEntryGroup;
- });
- increaseMemoryUsageAndMayEvict(usedMemorySize.get());
+
+ ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup = firstKeyMap.get(firstKey);
+ if (Objects.isNull(cacheEntryGroup)) {
+ if (createIfNotExists) {
+ cacheEntryGroup = new CacheEntryGroupImpl<>(firstKey, sizeComputer);
+ firstKeyMap.put(firstKey, cacheEntryGroup);
+ } else {
+ return;
+ }
+ }
+
+ final ICacheEntryGroup<FK, SK, V, T> finalCacheEntryGroup =
cacheEntryGroup;
+ cacheEntryGroup.computeCacheEntry(
+ secondKey,
+ memory ->
+ (sk, cacheEntry) -> {
+ if (Objects.isNull(cacheEntry)) {
+ if (!createIfNotExists) {
+ return null;
+ }
+ cacheEntry =
+ cacheEntryManager.createCacheEntry(secondKey, value,
finalCacheEntryGroup);
+ cacheEntryManager.put(cacheEntry);
+ memory.getAndAdd(
+ sizeComputer.computeSecondKeySize(sk)
+ + sizeComputer.computeValueSize(cacheEntry.getValue())
+ + RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_ENTRY);
+ }
+ memory.getAndAdd(updater.applyAsInt(cacheEntry.getValue()));
+ return cacheEntry;
+ });
+
+ mayEvict();
}
@Override
public void update(
final FK firstKey, final Predicate<SK> secondKeyChecker, final
ToIntFunction<V> updater) {
- final AtomicInteger usedMemorySize = new AtomicInteger(0);
-
- firstKeyMap.compute(
- firstKey,
- (k, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- return null;
- }
- final ICacheEntryGroup<FK, SK, V, T> finalCacheEntryGroup =
cacheEntryGroup;
-
- cacheEntryGroup
- .getAllCacheEntries()
- .forEachRemaining(
- entry -> {
- if (!secondKeyChecker.test(entry.getKey())) {
- return;
- }
- final int result =
updater.applyAsInt(entry.getValue().getValue());
- if
(Objects.nonNull(finalCacheEntryGroup.getCacheEntry(entry.getKey()))) {
- usedMemorySize.getAndAdd(result);
- }
- });
- return cacheEntryGroup;
- });
- increaseMemoryUsageAndMayEvict(usedMemorySize.get());
+ final ICacheEntryGroup<FK, SK, V, T> entryGroup =
firstKeyMap.get(firstKey);
+ if (Objects.nonNull(entryGroup)) {
+ entryGroup
+ .getAllCacheEntries()
+ .forEachRemaining(
+ entry -> {
+ if (!secondKeyChecker.test(entry.getKey())) {
+ return;
+ }
+ entryGroup.computeCacheEntry(
+ entry.getKey(),
+ memory ->
+ (secondKey, cacheEntry) -> {
+ if (Objects.nonNull(cacheEntry)) {
+
memory.getAndAdd(updater.applyAsInt(cacheEntry.getValue()));
+ }
+ return cacheEntry;
+ });
+ });
+ }
+ mayEvict();
}
@Override
@@ -263,93 +149,72 @@ class DualKeyCacheImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
final Predicate<FK> firstKeyChecker,
final Predicate<SK> secondKeyChecker,
final ToIntFunction<V> updater) {
- final AtomicInteger usedMemorySize = new AtomicInteger(0);
for (final FK firstKey : firstKeyMap.getAllKeys()) {
if (!firstKeyChecker.test(firstKey)) {
continue;
}
- firstKeyMap.compute(
- firstKey,
- (fk, entryGroup) -> {
- if (Objects.nonNull(entryGroup)) {
- entryGroup
- .getAllCacheEntries()
- .forEachRemaining(
- entry -> {
- if (!secondKeyChecker.test(entry.getKey())) {
- return;
- }
- final int result =
updater.applyAsInt(entry.getValue().getValue());
- if
(Objects.nonNull(entryGroup.getCacheEntry(entry.getKey()))) {
- usedMemorySize.getAndAdd(result);
- }
- });
- }
- return entryGroup;
- });
+ final ICacheEntryGroup<FK, SK, V, T> entryGroup =
firstKeyMap.get(firstKey);
+ if (Objects.nonNull(entryGroup)) {
+ entryGroup
+ .getAllCacheEntries()
+ .forEachRemaining(
+ entry -> {
+ if (!secondKeyChecker.test(entry.getKey())) {
+ return;
+ }
+ entryGroup.computeCacheEntry(
+ entry.getKey(),
+ memory ->
+ (secondKey, cacheEntry) -> {
+
memory.getAndAdd(updater.applyAsInt(cacheEntry.getValue()));
+ return cacheEntry;
+ });
+ });
+ }
+ mayEvict();
}
- increaseMemoryUsageAndMayEvict(usedMemorySize.get());
}
- private void increaseMemoryUsageAndMayEvict(final int memorySize) {
- cacheStats.increaseMemoryUsage(memorySize);
- while (cacheStats.isExceedMemoryCapacity()) {
- cacheStats.decreaseMemoryUsage(evictOneCacheEntry());
+ private void mayEvict() {
+ long exceedMemory;
+ while ((exceedMemory = cacheStats.getExceedMemory()) > 0) {
+ // Not compute each time to save time when FK is too many
+ // The hard-coded size is 100
+ do {
+ exceedMemory -= evictOneCacheEntry();
+ } while (exceedMemory > 0 && firstKeyMap.size() > 100);
}
}
- private int evictOneCacheEntry() {
+ // The returned delta may have some error, but it's OK
+ // Because the delta is only for loop round estimation
+ private long evictOneCacheEntry() {
final ICacheEntry<SK, V> evictCacheEntry = cacheEntryManager.evict();
if (evictCacheEntry == null) {
return 0;
}
- final AtomicInteger evictedSize = new AtomicInteger(0);
-
final ICacheEntryGroup<FK, SK, V, T> belongedGroup =
evictCacheEntry.getBelongedGroup();
evictCacheEntry.setBelongedGroup(null);
- firstKeyMap.compute(
- belongedGroup.getFirstKey(),
- (firstKey, cacheEntryGroup) -> {
- belongedGroup.removeCacheEntry(evictCacheEntry.getSecondKey());
- cacheStats.decreaseEntryCount();
- evictedSize.getAndAdd(
- sizeComputer.computeValueSize(evictCacheEntry.getValue())
- +
sizeComputer.computeSecondKeySize(evictCacheEntry.getSecondKey()));
-
- if (cacheEntryGroup == null) {
- // has been removed by other threads
- return null;
- }
-
- if (cacheEntryGroup.isEmpty()) {
- evictedSize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- return null;
- }
+ long memory =
belongedGroup.removeCacheEntry(evictCacheEntry.getSecondKey());
- // some other thread has put value to it
- return cacheEntryGroup;
- });
-
- return evictedSize.get();
+ final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
+ firstKeyMap.get(belongedGroup.getFirstKey());
+ if (Objects.nonNull(cacheEntryGroup) && cacheEntryGroup.isEmpty()) {
+ if (Objects.nonNull(firstKeyMap.remove(belongedGroup.getFirstKey()))) {
+ memory +=
+ sizeComputer.computeFirstKeySize(belongedGroup.getFirstKey())
+ + RamUsageEstimator.HASHTABLE_RAM_BYTES_PER_ENTRY;
+ }
+ }
+ return memory;
}
@Override
public void invalidateAll() {
- executeInvalidateAll();
- }
-
- private void executeInvalidateAll() {
firstKeyMap.clear();
cacheEntryManager.cleanUp();
- cacheStats.resetMemoryUsageAndEntriesCount();
- }
-
- @Override
- public void cleanUp() {
- executeInvalidateAll();
- cacheStats.reset();
}
@Override
@@ -357,137 +222,104 @@ class DualKeyCacheImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
return cacheStats;
}
- @Override
- @TestOnly
- public void evictOneEntry() {
- cacheStats.decreaseMemoryUsage(evictOneCacheEntry());
- }
-
@Override
public void invalidate(final FK firstKey) {
- int estimateSize = 0;
final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.remove(firstKey);
if (cacheEntryGroup != null) {
- estimateSize += sizeComputer.computeFirstKeySize(firstKey);
for (final Iterator<Map.Entry<SK, T>> it =
cacheEntryGroup.getAllCacheEntries();
it.hasNext(); ) {
- final Map.Entry<SK, T> entry = it.next();
- if (cacheEntryManager.invalidate(entry.getValue())) {
- cacheStats.decreaseEntryCount();
- estimateSize +=
- sizeComputer.computeSecondKeySize(entry.getKey())
- + sizeComputer.computeValueSize(entry.getValue().getValue());
- }
+ cacheEntryManager.invalidate(it.next().getValue());
}
- cacheStats.decreaseMemoryUsage(estimateSize);
}
}
@Override
public void invalidate(final FK firstKey, final SK secondKey) {
- final AtomicInteger usedMemorySize = new AtomicInteger(0);
-
- firstKeyMap.compute(
- firstKey,
- (key, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- // has been removed by other threads
- return null;
- }
-
- final T entry = cacheEntryGroup.getCacheEntry(secondKey);
- if (Objects.nonNull(entry) && cacheEntryManager.invalidate(entry)) {
- cacheStats.decreaseEntryCount();
- usedMemorySize.getAndAdd(
- sizeComputer.computeSecondKeySize(entry.getSecondKey())
- + sizeComputer.computeValueSize(entry.getValue()));
- cacheEntryGroup.removeCacheEntry(entry.getSecondKey());
- }
+ final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.get(firstKey);
+ if (Objects.isNull(cacheEntryGroup)) {
+ return;
+ }
- if (cacheEntryGroup.isEmpty()) {
-
usedMemorySize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- return null;
- }
+ final T entry = cacheEntryGroup.getCacheEntry(secondKey);
+ if (Objects.nonNull(entry) && cacheEntryManager.invalidate(entry)) {
+ cacheEntryGroup.removeCacheEntry(entry.getSecondKey());
+ }
- return cacheEntryGroup;
- });
- cacheStats.decreaseMemoryUsage(usedMemorySize.get());
+ if (cacheEntryGroup.isEmpty()) {
+ firstKeyMap.remove(firstKey);
+ }
}
@Override
public void invalidate(final FK firstKey, final Predicate<SK>
secondKeyChecker) {
- final AtomicInteger estimateSize = new AtomicInteger(0);
- firstKeyMap.compute(
- firstKey,
- (key, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- // has been removed by other threads
- return null;
- }
-
- for (final Iterator<Map.Entry<SK, T>> it =
cacheEntryGroup.getAllCacheEntries();
- it.hasNext(); ) {
- final Map.Entry<SK, T> entry = it.next();
- if (secondKeyChecker.test(entry.getKey())
- && cacheEntryManager.invalidate(entry.getValue())) {
- cacheStats.decreaseEntryCount();
- cacheEntryGroup.removeCacheEntry(entry.getKey());
- estimateSize.addAndGet(
- sizeComputer.computeSecondKeySize(entry.getKey())
- +
sizeComputer.computeValueSize(entry.getValue().getValue()));
- }
- }
-
- if (cacheEntryGroup.isEmpty()) {
- estimateSize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- return null;
- }
-
- return cacheEntryGroup;
- });
+ final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.get(firstKey);
+ if (Objects.isNull(cacheEntryGroup)) {
+ return;
+ }
+ for (final Iterator<Map.Entry<SK, T>> it =
cacheEntryGroup.getAllCacheEntries();
+ it.hasNext(); ) {
+ final Map.Entry<SK, T> entry = it.next();
+ if (secondKeyChecker.test(entry.getKey()) &&
cacheEntryManager.invalidate(entry.getValue())) {
+ cacheEntryGroup.removeCacheEntry(entry.getKey());
+ }
+ }
- cacheStats.decreaseMemoryUsage(estimateSize.get());
+ if (cacheEntryGroup.isEmpty()) {
+ firstKeyMap.remove(firstKey);
+ }
}
@Override
public void invalidate(
final Predicate<FK> firstKeyChecker, final Predicate<SK>
secondKeyChecker) {
- final AtomicInteger estimateSize = new AtomicInteger(0);
for (final FK firstKey : firstKeyMap.getAllKeys()) {
if (!firstKeyChecker.test(firstKey)) {
continue;
}
- firstKeyMap.compute(
- firstKey,
- (fk, cacheEntryGroup) -> {
- if (cacheEntryGroup == null) {
- // has been removed by other threads
- return null;
- }
-
- for (final Iterator<Map.Entry<SK, T>> it =
cacheEntryGroup.getAllCacheEntries();
- it.hasNext(); ) {
- final Map.Entry<SK, T> entry = it.next();
-
- if (secondKeyChecker.test(entry.getKey())
- && cacheEntryManager.invalidate(entry.getValue())) {
- cacheStats.decreaseEntryCount();
- cacheEntryGroup.removeCacheEntry(entry.getKey());
- estimateSize.addAndGet(
- sizeComputer.computeSecondKeySize(entry.getKey())
- +
sizeComputer.computeValueSize(entry.getValue().getValue()));
- }
- }
-
- if (cacheEntryGroup.isEmpty()) {
-
estimateSize.getAndAdd(sizeComputer.computeFirstKeySize(firstKey));
- return null;
- }
- return cacheEntryGroup;
- });
+ final ICacheEntryGroup<FK, SK, V, T> cacheEntryGroup =
firstKeyMap.get(firstKey);
+ if (Objects.isNull(cacheEntryGroup)) {
+ return;
+ }
+
+ for (final Iterator<Map.Entry<SK, T>> it =
cacheEntryGroup.getAllCacheEntries();
+ it.hasNext(); ) {
+ final Map.Entry<SK, T> entry = it.next();
+
+ if (secondKeyChecker.test(entry.getKey())
+ && cacheEntryManager.invalidate(entry.getValue())) {
+ cacheEntryGroup.removeCacheEntry(entry.getKey());
+ }
+ }
+
+ if (cacheEntryGroup.isEmpty()) {
+ firstKeyMap.remove(firstKey);
+ }
}
- cacheStats.decreaseMemoryUsage(estimateSize.get());
+ }
+
+ private long getMemory() {
+ long memory = 0;
+ for (final Map<FK, ICacheEntryGroup<FK, SK, V, T>> map : firstKeyMap.maps)
{
+ if (Objects.nonNull(map)) {
+ for (final ICacheEntryGroup<FK, SK, V, T> group : map.values()) {
+ memory += group.getMemory();
+ }
+ }
+ }
+ return memory;
+ }
+
+ private long getEntriesCount() {
+ long count = 0;
+ for (final Map<FK, ICacheEntryGroup<FK, SK, V, T>> map : firstKeyMap.maps)
{
+ if (Objects.nonNull(map)) {
+ for (final ICacheEntryGroup<FK, SK, V, T> group : map.values()) {
+ count += group.getEntriesCount();
+ }
+ }
+ }
+ return count;
}
/**
@@ -500,27 +332,25 @@ class DualKeyCacheImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
private final Map<K, V>[] maps = new ConcurrentHashMap[SLOT_NUM];
- V get(K key) {
+ V get(final K key) {
return getBelongedMap(key).get(key);
}
- V remove(K key) {
+ V remove(final K key) {
return getBelongedMap(key).remove(key);
}
- V compute(K key, BiFunction<? super K, ? super V, ? extends V>
remappingFunction) {
- return getBelongedMap(key).compute(key, remappingFunction);
+ V put(final K key, final V value) {
+ return getBelongedMap(key).put(key, value);
}
void clear() {
synchronized (maps) {
- for (int i = 0; i < SLOT_NUM; i++) {
- maps[i] = null;
- }
+ Arrays.fill(maps, null);
}
}
- Map<K, V> getBelongedMap(K key) {
+ Map<K, V> getBelongedMap(final K key) {
int slotIndex = key.hashCode() % SLOT_NUM;
slotIndex = slotIndex < 0 ? slotIndex + SLOT_NUM : slotIndex;
Map<K, V> map = maps[slotIndex];
@@ -538,7 +368,7 @@ class DualKeyCacheImpl<FK, SK, V, T extends ICacheEntry<SK,
V>>
// Copied list, deletion-safe
List<K> getAllKeys() {
- List<K> res = new ArrayList<>();
+ final List<K> res = new ArrayList<>();
Arrays.stream(maps)
.iterator()
.forEachRemaining(
@@ -549,5 +379,15 @@ class DualKeyCacheImpl<FK, SK, V, T extends
ICacheEntry<SK, V>>
});
return res;
}
+
+ int size() {
+ int size = 0;
+ for (final Map<K, V> map : maps) {
+ if (Objects.nonNull(map)) {
+ size += map.size();
+ }
+ }
+ return size;
+ }
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/ICacheEntryGroup.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/ICacheEntryGroup.java
index 6dde21e1518..d1f8ab9923e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/ICacheEntryGroup.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/dualkeycache/impl/ICacheEntryGroup.java
@@ -21,6 +21,7 @@ package
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.i
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -41,11 +42,15 @@ interface ICacheEntryGroup<FK, SK, V, T extends
ICacheEntry<SK, V>> {
Iterator<Map.Entry<SK, T>> getAllCacheEntries();
- T computeCacheEntry(final SK secondKey, final BiFunction<SK, T, T>
computation);
+ T computeCacheEntry(
+ final SK secondKey, final Function<AtomicLong, BiFunction<SK, T, T>>
computation);
- T computeCacheEntryIfAbsent(final SK secondKey, final Function<SK, T>
computation);
-
- T removeCacheEntry(final SK secondKey);
+ long removeCacheEntry(final SK secondKey);
boolean isEmpty();
+
+ // Metric
+ long getMemory();
+
+ int getEntriesCount();
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java
index 08557346c39..a9695475a18 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/InsertTabletNode.java
@@ -1285,9 +1285,9 @@ public class InsertTabletNode extends InsertNode
implements WALEntryValue {
return firstAliveLoc;
}
- public void updateLastCache(String databaseName) {
- String[] rawMeasurements = getRawMeasurements();
- TimeValuePair[] timeValuePairs = new TimeValuePair[rawMeasurements.length];
+ public void updateLastCache(final String databaseName) {
+ final String[] rawMeasurements = getRawMeasurements();
+ final TimeValuePair[] timeValuePairs = new
TimeValuePair[rawMeasurements.length];
for (int i = 0; i < rawMeasurements.length; i++) {
timeValuePairs[i] = composeLastTimeValuePair(i);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalInsertTabletNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalInsertTabletNode.java
index 70321743453..0d4b698108e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalInsertTabletNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/write/RelationalInsertTabletNode.java
@@ -353,16 +353,16 @@ public class RelationalInsertTabletNode extends
InsertTabletNode {
}
@Override
- public void updateLastCache(String databaseName) {
- String[] rawMeasurements = getRawMeasurements();
+ public void updateLastCache(final String databaseName) {
+ final String[] rawMeasurements = getRawMeasurements();
- List<Pair<IDeviceID, Integer>> deviceEndOffsetPairs = splitByDevice(0,
rowCount);
+ final List<Pair<IDeviceID, Integer>> deviceEndOffsetPairs =
splitByDevice(0, rowCount);
int startOffset = 0;
- for (Pair<IDeviceID, Integer> deviceEndOffsetPair : deviceEndOffsetPairs) {
- IDeviceID deviceID = deviceEndOffsetPair.getLeft();
- int endOffset = deviceEndOffsetPair.getRight();
+ for (final Pair<IDeviceID, Integer> deviceEndOffsetPair :
deviceEndOffsetPairs) {
+ final IDeviceID deviceID = deviceEndOffsetPair.getLeft();
+ final int endOffset = deviceEndOffsetPair.getRight();
- TimeValuePair[] timeValuePairs = new
TimeValuePair[rawMeasurements.length];
+ final TimeValuePair[] timeValuePairs = new
TimeValuePair[rawMeasurements.length];
for (int i = 0; i < rawMeasurements.length; i++) {
timeValuePairs[i] = composeLastTimeValuePair(i, startOffset,
endOffset);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
index 59e179fcff1..70d1c944237 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
@@ -1400,7 +1400,7 @@ public class DataRegion implements IDataRegionForQuery {
}
}
- private void tryToUpdateInsertTabletLastCache(InsertTabletNode node) {
+ private void tryToUpdateInsertTabletLastCache(final InsertTabletNode node) {
node.updateLastCache(getDatabaseName());
}
@@ -1424,7 +1424,7 @@ public class DataRegion implements IDataRegionForQuery {
return tsFileProcessor;
}
- private void tryToUpdateInsertRowLastCache(InsertRowNode node) {
+ private void tryToUpdateInsertRowLastCache(final InsertRowNode node) {
node.updateLastCache(databaseName);
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/cache/dualkeycache/DualKeyCacheTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/cache/dualkeycache/DualKeyCacheTest.java
deleted file mode 100644
index 305717a3db6..00000000000
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/cache/dualkeycache/DualKeyCacheTest.java
+++ /dev/null
@@ -1,171 +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.iotdb.db.metadata.cache.dualkeycache;
-
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCache;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheComputation;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.IDualKeyCacheUpdating;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.impl.DualKeyCacheBuilder;
-import
org.apache.iotdb.db.queryengine.plan.analyze.cache.schema.dualkeycache.impl.DualKeyCachePolicy;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(Parameterized.class)
-public class DualKeyCacheTest {
-
- private final String policy;
-
- public DualKeyCacheTest(String policy) {
- this.policy = policy;
- }
-
- @Parameterized.Parameters
- public static List<String> getTestModes() {
- return Arrays.asList("FIFO", "LRU");
- }
-
- @Test
- public void testBasicReadPut() {
- DualKeyCacheBuilder<String, String, String> dualKeyCacheBuilder = new
DualKeyCacheBuilder<>();
- IDualKeyCache<String, String, String> dualKeyCache =
- dualKeyCacheBuilder
- .cacheEvictionPolicy(DualKeyCachePolicy.valueOf(policy))
- .memoryCapacity(300)
- .firstKeySizeComputer(this::computeStringSize)
- .secondKeySizeComputer(this::computeStringSize)
- .valueSizeComputer(this::computeStringSize)
- .build();
-
- String[] firstKeyList = new String[] {"root.db.d1", "root.db.d2"};
- String[] secondKeyList = new String[] {"s1", "s2"};
- String[][] valueTable =
- new String[][] {new String[] {"1-1", "1-2"}, new String[] {"2-1",
"2-2"}};
-
- for (int i = 0; i < firstKeyList.length; i++) {
- for (int j = 0; j < secondKeyList.length; j++) {
- dualKeyCache.put(firstKeyList[i], secondKeyList[j], valueTable[i][j]);
- }
- }
-
- int firstKeyOfMissingEntry = -1, secondKeyOfMissingEntry = -1;
- for (int i = 0; i < firstKeyList.length; i++) {
- for (int j = 0; j < secondKeyList.length; j++) {
- String value = dualKeyCache.get(firstKeyList[i], secondKeyList[j]);
- if (value == null) {
- if (firstKeyOfMissingEntry == -1) {
- firstKeyOfMissingEntry = i;
- secondKeyOfMissingEntry = j;
- } else {
- Assert.fail();
- }
- } else {
- Assert.assertEquals(valueTable[i][j], value);
- }
- }
- }
-
- Assert.assertEquals(230, dualKeyCache.stats().memoryUsage());
- Assert.assertEquals(4, dualKeyCache.stats().requestCount());
- Assert.assertEquals(3, dualKeyCache.stats().hitCount());
-
- dualKeyCache.put(
- firstKeyList[firstKeyOfMissingEntry],
- secondKeyList[secondKeyOfMissingEntry],
- valueTable[firstKeyOfMissingEntry][secondKeyOfMissingEntry]);
- Assert.assertEquals(230, dualKeyCache.stats().memoryUsage());
-
- for (int i = 0; i < firstKeyList.length; i++) {
- int finalI = i;
- dualKeyCache.compute(
- new IDualKeyCacheComputation<String, String, String>() {
- @Override
- public String getFirstKey() {
- return firstKeyList[finalI];
- }
-
- @Override
- public String[] getSecondKeyList() {
- return secondKeyList;
- }
-
- @Override
- public void computeValue(int index, String value) {
- if (value != null) {
- Assert.assertEquals(valueTable[finalI][index], value);
- }
- }
- });
- }
-
- Assert.assertEquals(8, dualKeyCache.stats().requestCount());
- Assert.assertEquals(6, dualKeyCache.stats().hitCount());
- }
-
- private int computeStringSize(String string) {
- return 8 + 8 + 4 + 2 * string.length();
- }
-
- @Test
- public void testComputeAndUpdateSize() {
- final DualKeyCacheBuilder<String, String, String> dualKeyCacheBuilder =
- new DualKeyCacheBuilder<>();
- final IDualKeyCache<String, String, String> dualKeyCache =
- dualKeyCacheBuilder
- .cacheEvictionPolicy(DualKeyCachePolicy.valueOf(policy))
- .memoryCapacity(500)
- .firstKeySizeComputer(this::computeStringSize)
- .secondKeySizeComputer(this::computeStringSize)
- .valueSizeComputer(this::computeStringSize)
- .build();
- final String firstKey = "db";
- final String[] secondKeyList = new String[] {"root.db.d1", "root.db.d2"};
- for (final String s : secondKeyList) {
- dualKeyCache.put(firstKey, s, "");
- }
- int expectedSize =
- computeStringSize("db") + computeStringSize("root.db.d1") * 2 +
computeStringSize("") * 2;
- Assert.assertEquals(expectedSize, dualKeyCache.stats().memoryUsage());
- dualKeyCache.updateWithLock(
- new IDualKeyCacheUpdating<String, String, String>() {
- @Override
- public String getFirstKey() {
- return firstKey;
- }
-
- @Override
- public String[] getSecondKeyList() {
- return secondKeyList;
- }
-
- @Override
- public int updateValue(final int index, final String value) {
- return computeStringSize("b") - computeStringSize("");
- }
- });
- expectedSize += (computeStringSize("b") - computeStringSize("")) * 2;
- Assert.assertEquals(expectedSize, dualKeyCache.stats().memoryUsage());
- }
-}