diff --git a/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCache.java b/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCache.java index e3d0c6bbf574d..02b3050ced74c 100644 --- a/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCache.java +++ b/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCache.java @@ -19,6 +19,7 @@ import org.opensearch.common.cache.RemovalReason; import org.opensearch.common.cache.policy.CachedQueryResult; import org.opensearch.common.cache.stats.CacheStats; +import org.opensearch.common.cache.stats.DimensionNode; import org.opensearch.common.cache.stats.StatsHolder; import org.opensearch.common.cache.store.config.CacheConfig; import org.opensearch.common.collect.Tuple; @@ -64,8 +65,9 @@ public class TieredSpilloverCache implements ICache { // In future we want to just read the stats from the individual tiers' statsHolder objects, but this isn't // possible right now because of the way computeIfAbsent is implemented. - private final StatsHolder heapStats; - private final StatsHolder diskStats; + //private final StatsHolder heapStats; + //private final StatsHolder diskStats; + private final StatsHolder statsHolder; private ToLongBiFunction, V> weigher; private final List dimensionNames; @@ -76,9 +78,14 @@ public class TieredSpilloverCache implements ICache { * Maintains caching tiers in ascending order of cache latency. */ private final List> cacheList; - private final List, StatsHolder>> cacheAndStatsList; + private final List, String>> cacheAndTierValueList; private final List> policies; + // Common values used for tier dimension + public static final String TIER_DIMENSION_NAME = "tier"; + public static final String TIER_DIMENSION_VALUE_ON_HEAP = "on_heap"; + public static final String TIER_DIMENSION_VALUE_DISK = "disk"; + TieredSpilloverCache(Builder builder) { Objects.requireNonNull(builder.onHeapCacheFactory, "onHeap cache builder can't be null"); Objects.requireNonNull(builder.diskCacheFactory, "disk cache builder can't be null"); @@ -116,12 +123,14 @@ public class TieredSpilloverCache implements ICache { this.cacheList = Arrays.asList(onHeapCache, diskCache); this.dimensionNames = builder.cacheConfig.getDimensionNames(); - this.heapStats = new StatsHolder(dimensionNames); - this.diskStats = new StatsHolder(dimensionNames); - this.cacheAndStatsList = List.of( - new Tuple<>(onHeapCache, heapStats), - new Tuple<>(diskCache, diskStats) + //this.heapStats = new StatsHolder(dimensionNames); + //this.diskStats = new StatsHolder(dimensionNames); + this.cacheAndTierValueList = List.of( + new Tuple<>(onHeapCache, TIER_DIMENSION_VALUE_ON_HEAP), + new Tuple<>(diskCache, TIER_DIMENSION_VALUE_DISK) ); + // Pass "tier" as the innermost dimension name, on top of whatever dimensions are specified for the cache as a whole + this.statsHolder = new StatsHolder(addTierValueToDimensionValues(dimensionNames, TIER_DIMENSION_NAME)); this.policies = builder.policies; // Will never be null; builder initializes it to an empty list } @@ -144,7 +153,7 @@ public V get(ICacheKey key) { public void put(ICacheKey key, V value) { try (ReleasableLock ignore = writeLock.acquire()) { onHeapCache.put(key, value); - updateStatsOnPut(heapStats, key, value); + updateStatsOnPut(TIER_DIMENSION_VALUE_ON_HEAP, key, value); } } @@ -161,7 +170,7 @@ public V computeIfAbsent(ICacheKey key, LoadAwareCacheLoader, V> value = onHeapCache.computeIfAbsent(key, loader); if (loader.isLoaded()) { // The value was just computed and added to the cache - updateStatsOnPut(heapStats, key, value); + updateStatsOnPut(TIER_DIMENSION_VALUE_ON_HEAP, key, value); } } return value; @@ -176,8 +185,13 @@ public void invalidate(ICacheKey key) { // also trigger a hit/miss listener event, so ignoring it for now. // We don't update stats here, as this is handled by the removal listeners for the tiers. try (ReleasableLock ignore = writeLock.acquire()) { - for (ICache cache : cacheList) { - cache.invalidate(key); + for (Tuple, String> pair : cacheAndTierValueList) { + //cache.invalidate(key); + if (key.getDropStatsForDimensions()) { + List dimensionValues = addTierValueToDimensionValues(key.dimensions, pair.v2()); + statsHolder.removeDimensions(dimensionValues); + } + pair.v1().invalidate(key); } } } @@ -189,8 +203,7 @@ public void invalidateAll() { cache.invalidateAll(); } } - heapStats.reset(); - diskStats.reset(); + statsHolder.reset(); } /** @@ -205,7 +218,7 @@ public Iterable> keys() { @Override public long count() { - return heapStats.count() + diskStats.count(); + return statsHolder.count(); } @Override @@ -226,21 +239,23 @@ public void close() throws IOException { @Override public CacheStats stats() { - // TODO: Just reuse MDCS //return new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), dimensionNames); - return null; + return statsHolder.getCacheStats(); } private Function, V> getValueFromTieredCache() { return key -> { try (ReleasableLock ignore = readLock.acquire()) { - for (Tuple, StatsHolder> pair : cacheAndStatsList) { + for (Tuple, String> pair : cacheAndTierValueList) { V value = pair.v1().get(key); + List dimensionValues = addTierValueToDimensionValues(key.dimensions, pair.v2()); // Get the tier value corresponding to this cache if (value != null) { - pair.v2().incrementHits(key.dimensions); + //pair.v2().incrementHits(key.dimensions); + statsHolder.incrementHits(dimensionValues); return value; } else { - pair.v2().incrementMisses(key.dimensions); + statsHolder.incrementMisses(dimensionValues); + //pair.v2().incrementMisses(key.dimensions); } } } @@ -257,7 +272,7 @@ void handleRemovalFromHeapTier(RemovalNotification, V> notification try (ReleasableLock ignore = writeLock.acquire()) { if (evaluatePolicies(notification.getValue())) { diskCache.put(key, notification.getValue()); // spill over to the disk tier and increment its stats - updateStatsOnPut(diskStats, key, notification.getValue()); + updateStatsOnPut(TIER_DIMENSION_VALUE_DISK, key, notification.getValue()); } } wasEvicted = true; @@ -267,7 +282,7 @@ void handleRemovalFromHeapTier(RemovalNotification, V> notification // If the removal was for another reason, send this notification to the TSC's removal listener, as the value is leaving the TSC entirely removalListener.onRemoval(notification); } - updateStatsOnRemoval(heapStats, wasEvicted, key, notification.getValue()); + updateStatsOnRemoval(TIER_DIMENSION_VALUE_ON_HEAP, wasEvicted, key, notification.getValue()); } void handleRemovalFromDiskTier(RemovalNotification, V> notification) { @@ -279,20 +294,22 @@ void handleRemovalFromDiskTier(RemovalNotification, V> notification || RemovalReason.CAPACITY.equals(notification.getRemovalReason())) { wasEvicted = true; } - updateStatsOnRemoval(diskStats, wasEvicted, notification.getKey(), notification.getValue()); + updateStatsOnRemoval(TIER_DIMENSION_VALUE_DISK, wasEvicted, notification.getKey(), notification.getValue()); } - void updateStatsOnRemoval(StatsHolder statsHolder, boolean wasEvicted, ICacheKey key, V value) { + void updateStatsOnRemoval(String removedFromTierValue, boolean wasEvicted, ICacheKey key, V value) { + List dimensionValues = addTierValueToDimensionValues(key.dimensions, removedFromTierValue); if (wasEvicted) { - statsHolder.incrementEvictions(key.dimensions); + statsHolder.incrementEvictions(dimensionValues); } - statsHolder.decrementEntries(key.dimensions); - statsHolder.decrementSizeInBytes(key.dimensions, weigher.applyAsLong(key, value)); + statsHolder.decrementEntries(dimensionValues); + statsHolder.decrementSizeInBytes(dimensionValues, weigher.applyAsLong(key, value)); } - void updateStatsOnPut(StatsHolder statsHolder, ICacheKey key, V value) { - statsHolder.incrementEntries(key.dimensions); - statsHolder.incrementSizeInBytes(key.dimensions, weigher.applyAsLong(key, value)); + void updateStatsOnPut(String destinationTierValue, ICacheKey key, V value) { + List dimensionValues = addTierValueToDimensionValues(key.dimensions, destinationTierValue); + statsHolder.incrementEntries(dimensionValues); + statsHolder.incrementSizeInBytes(dimensionValues, weigher.applyAsLong(key, value)); } boolean evaluatePolicies(V value) { @@ -304,6 +321,15 @@ boolean evaluatePolicies(V value) { return true; } + /** + * Add tierValue to the end of a copy of the initial dimension values. + */ + private List addTierValueToDimensionValues(List initialDimensions, String tierValue) { + List result = new ArrayList<>(initialDimensions); + result.add(tierValue); + return result; + } + /** * A class which receives removal events from the heap tier. */ diff --git a/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStats.java b/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStats.java deleted file mode 100644 index 350718b693bc4..0000000000000 --- a/modules/cache-common/src/main/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStats.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cache.common.tier; - -import org.opensearch.common.cache.stats.CacheStats; -import org.opensearch.common.cache.stats.MultiDimensionCacheStats; -import org.opensearch.common.cache.stats.StatsHolder; -import org.opensearch.core.common.io.stream.StreamInput; -import org.opensearch.core.common.io.stream.StreamOutput; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - - -/** - * A CacheStats implementation for TieredSpilloverCache, which keeps track of the stats for its tiers. - */ -public class TieredSpilloverCacheStats { // implements CacheStats - // Pkg-private for testing - /*final MultiDimensionCacheStats heapStats; - final MultiDimensionCacheStats diskStats; - - public static final String TIER_DIMENSION_NAME = "tier"; - public static final String TIER_DIMENSION_VALUE_ON_HEAP = "on_heap"; - public static final String TIER_DIMENSION_VALUE_DISK = "disk"; - public static final List HEAP_DIMS = List.of(new CacheStatsDimension(TIER_DIMENSION_NAME, TIER_DIMENSION_VALUE_ON_HEAP)); - public static final List DISK_DIMS = List.of(new CacheStatsDimension(TIER_DIMENSION_NAME, TIER_DIMENSION_VALUE_DISK)); - - public TieredSpilloverCacheStats( - Map heapSnapshot, - Map diskSnapshot, - List dimensionNames) { - this.heapStats = new MultiDimensionCacheStats(heapSnapshot, dimensionNames); - this.diskStats = new MultiDimensionCacheStats(diskSnapshot, dimensionNames); - } - - public TieredSpilloverCacheStats(StreamInput in) throws IOException { - this.heapStats = new MultiDimensionCacheStats(in); - this.diskStats = new MultiDimensionCacheStats(in); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - heapStats.writeTo(out); - diskStats.writeTo(out); - } - - @Override - public CacheStatsResponse.Snapshot getTotalStats() { - return combineTierResponses(heapStats.getTotalStats(), diskStats.getTotalStats()); - } - - public TreeMap aggregateByLevels(List levels) { - TreeMap result = new TreeMap<>(new MultiDimensionCacheStats.KeyComparator()); - if (levels.contains(TIER_DIMENSION_NAME)) { - // Aggregate by tier. Get the aggregations from each MultiDimensionCacheStats, and combine them into a single - // TreeMap, adding the tier dimension to each TreeMap key. - List noTierLevels = new ArrayList<>(levels); // levels might be immutable - noTierLevels.remove(TIER_DIMENSION_NAME); - TreeMap heapAgg = heapStats.aggregateByLevels(noTierLevels); - TreeMap diskAgg = diskStats.aggregateByLevels(noTierLevels); - - addKeysWithTierDimension(result, heapAgg, TIER_DIMENSION_VALUE_ON_HEAP); - addKeysWithTierDimension(result, diskAgg, TIER_DIMENSION_VALUE_DISK); - } - else { - // Don't aggregate by tier. Get aggregations from each MultiDimensionCacheStats. Combine them using combineTierResponses - // if both aggregations share a key. Otherwise, add directly from the only one which has the key. - TreeMap heapAgg = heapStats.aggregateByLevels(levels); - TreeMap diskAgg = diskStats.aggregateByLevels(levels); - - for (Map.Entry entry : heapAgg.entrySet()) { - CacheStatsResponse.Snapshot heapValue = entry.getValue(); - CacheStatsResponse.Snapshot diskValue = diskAgg.get(entry.getKey()); - StatsHolder.Key key = entry.getKey(); - if (diskValue == null) { - // Only the heap agg has this particular combination of values, add directly to result - result.put(key, heapValue); - } else { - // Both aggregations have this combination, combine them before adding and then remove from diskAgg to avoid double-counting - CacheStatsResponse.Snapshot combined = combineTierResponses(heapValue, diskValue); - result.put(key, combined); - diskAgg.remove(key); - } - } - // The remaining keys are only present in diskAgg - result.putAll(diskAgg); - } - return result; - } - - // Add all keys in originalAggregation to result, but first add tierDimName to the end of the key. - private void addKeysWithTierDimension(TreeMap result, - TreeMap originalAggregation, - String tierDimName) { - for (Map.Entry entry : originalAggregation.entrySet()) { - List newDimensions = new ArrayList<>(entry.getKey().getDimensionValues()); - newDimensions.add(tierDimName); // Tier dimension is at the end as it's the innermost dimension in API responses - StatsHolder.Key newKey = new StatsHolder.Key(newDimensions); - result.put(newKey, entry.getValue()); - } - } - - // pkg-private for testing - static CacheStatsResponse.Snapshot combineTierResponses(CacheStatsResponse.Snapshot heap, CacheStatsResponse.Snapshot disk) { - return new CacheStatsResponse.Snapshot( - heap.getHits() + disk.getHits(), - disk.getMisses(), - disk.getEvictions(), - heap.getSizeInBytes() + disk.getSizeInBytes(), - heap.getEntries() + disk.getEntries() - ); - } - - @Override - public long getTotalHits() { - return getTotalStats().getHits(); - } - - @Override - public long getTotalMisses() { - return getTotalStats().getMisses(); - } - - @Override - public long getTotalEvictions() { - return getTotalStats().getEvictions(); - } - - @Override - public long getTotalSizeInBytes() { - return getTotalStats().getSizeInBytes(); - } - - @Override - public long getTotalEntries() { - return getTotalStats().getEntries(); - } - - CacheStatsResponse.Snapshot getTotalHeapStats() { - return heapStats.getTotalStats(); - } - - CacheStatsResponse.Snapshot getTotalDiskStats() { - return diskStats.getTotalStats(); - }*/ -} diff --git a/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStatsTests.java b/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStatsTests.java deleted file mode 100644 index 089980e2fe948..0000000000000 --- a/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheStatsTests.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cache.common.tier; - -import org.opensearch.common.Randomness; -import org.opensearch.common.cache.Cache; -import org.opensearch.common.cache.ICacheKey; -import org.opensearch.common.cache.stats.CacheStats; -import org.opensearch.common.cache.stats.StatsHolder; -import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.settings.Settings; -import org.opensearch.core.common.bytes.BytesReference; -import org.opensearch.core.common.io.stream.BytesStreamInput; -import org.opensearch.test.OpenSearchTestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.TreeMap; -import java.util.UUID; - -/*import static org.opensearch.cache.common.tier.TieredSpilloverCacheStats.TIER_DIMENSION_NAME; -import static org.opensearch.cache.common.tier.TieredSpilloverCacheStats.TIER_DIMENSION_VALUE_ON_HEAP; -import static org.opensearch.cache.common.tier.TieredSpilloverCacheStats.TIER_DIMENSION_VALUE_DISK; -import static org.opensearch.cache.common.tier.TieredSpilloverCacheStats.combineTierResponses;*/ - -public class TieredSpilloverCacheStatsTests extends OpenSearchTestCase { - /*private static List dimensionNames = List.of("dim1", "dim2", "dim3"); - private static List tierNames = List.of(TIER_DIMENSION_VALUE_ON_HEAP, TIER_DIMENSION_VALUE_DISK); - public void testGets() throws Exception { - StatsHolder heapStats = new StatsHolder(dimensionNames); - StatsHolder diskStats = new StatsHolder(dimensionNames); - Map> usedDimensionValues = getUsedDimensionValues(heapStats, 10); - Map, CacheStatsResponse>> expected = populateStats(heapStats, diskStats, usedDimensionValues, 100, 2); - TieredSpilloverCacheStats stats = new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), dimensionNames); - - CacheStatsResponse heapTotalStats = totalSumExpected(expected.get(TIER_DIMENSION_VALUE_ON_HEAP)); - CacheStatsResponse diskTotalStats = totalSumExpected(expected.get(TIER_DIMENSION_VALUE_DISK)); - CacheStatsResponse.Snapshot totalTSCStats = TieredSpilloverCacheStats.combineTierResponses( - heapTotalStats.snapshot(), diskTotalStats.snapshot()); - - // test total gets - assertEquals(totalTSCStats, stats.getTotalStats()); - - assertEquals(totalTSCStats.getHits(), stats.getTotalHits()); - assertEquals(totalTSCStats.getMisses(), stats.getTotalMisses()); - assertEquals(totalTSCStats.getEvictions(), stats.getTotalEvictions()); - assertEquals(totalTSCStats.getSizeInBytes(), stats.getTotalSizeInBytes()); - assertEquals(totalTSCStats.getEntries(), stats.getTotalEntries()); - - assertEquals(heapTotalStats.snapshot(), stats.getTotalHeapStats()); - assertEquals(diskTotalStats.snapshot(), stats.getTotalDiskStats()); - } - - public void testEmptyDimensionNames() throws Exception { - StatsHolder heapStats = new StatsHolder(List.of()); - StatsHolder diskStats = new StatsHolder(List.of()); - - Map> usedDimensionValues = getUsedDimensionValues(heapStats, 10); - Map, CacheStatsResponse>> expected = populateStats(heapStats, diskStats, usedDimensionValues, 10, 10); - TieredSpilloverCacheStats stats = new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), List.of()); - - CacheStatsResponse heapTotalStats = totalSumExpected(expected.get(TIER_DIMENSION_VALUE_ON_HEAP)); - CacheStatsResponse diskTotalStats = totalSumExpected(expected.get(TIER_DIMENSION_VALUE_DISK)); - CacheStatsResponse.Snapshot totalTSCStats = TieredSpilloverCacheStats.combineTierResponses(heapTotalStats.snapshot(), diskTotalStats.snapshot()); - - assertEquals(totalTSCStats, stats.getTotalStats()); - } - - public void testSerialization() throws Exception { - StatsHolder heapStats = new StatsHolder(dimensionNames); - StatsHolder diskStats = new StatsHolder(dimensionNames); - Map> usedDimensionValues = getUsedDimensionValues(heapStats, 10); - Map, CacheStatsResponse>> expected = populateStats(heapStats, diskStats, usedDimensionValues, 100, 2); - TieredSpilloverCacheStats stats = new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), dimensionNames); - - BytesStreamOutput os = new BytesStreamOutput(); - stats.writeTo(os); - BytesStreamInput is = new BytesStreamInput(BytesReference.toBytes(os.bytes())); - TieredSpilloverCacheStats deserialized = new TieredSpilloverCacheStats(is); - - assertEquals(stats.heapStats.aggregateByLevels(dimensionNames), deserialized.heapStats.aggregateByLevels(dimensionNames)); - assertEquals(stats.diskStats.aggregateByLevels(dimensionNames), deserialized.diskStats.aggregateByLevels(dimensionNames)); - } - - public void testCombineTierResponses() throws Exception { - CacheStatsResponse.Snapshot heapResponse = new CacheStatsResponse.Snapshot(1,2,3,4,5); - CacheStatsResponse.Snapshot diskResponse = new CacheStatsResponse.Snapshot(2,3,4,5,6); - CacheStatsResponse.Snapshot tscResponse = TieredSpilloverCacheStats.combineTierResponses(heapResponse, diskResponse); - assertEquals(new CacheStatsResponse.Snapshot(3, 3, 4, 9, 11), tscResponse); - } - - public void testAggregationSomeLevelsWithoutTier() throws Exception { - StatsHolder heapStats = new StatsHolder(dimensionNames); - StatsHolder diskStats = new StatsHolder(dimensionNames); - Map> usedDimensionValues = getUsedDimensionValues(heapStats, 10); - Map, CacheStatsResponse>> expected = populateStats(heapStats, diskStats, usedDimensionValues, 100, 2); - TieredSpilloverCacheStats stats = new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), dimensionNames); - - for (int i = 0; i < (1 << dimensionNames.size()); i++) { - // Test each combination of possible levels - List levels = new ArrayList<>(); - for (int nameIndex = 0; nameIndex < dimensionNames.size(); nameIndex++) { - if ((i & (1 << nameIndex)) != 0) { - levels.add(dimensionNames.get(nameIndex)); - } - } - if (levels.size() == 0) { - assertThrows(IllegalArgumentException.class, () -> stats.aggregateByLevels(levels)); - } - else { - Map aggregated = stats.aggregateByLevels(levels); - for (Map.Entry aggregatedEntry : aggregated.entrySet()) { - StatsHolder.Key aggregatedKey = aggregatedEntry.getKey(); - Map expectedResponseForTierMap = new HashMap<>(); - for (String tier : new String[]{TIER_DIMENSION_VALUE_ON_HEAP, TIER_DIMENSION_VALUE_DISK}) { - CacheStatsResponse expectedResponseForTier = new CacheStatsResponse(); - for (Set expectedDims : expected.get(tier).keySet()) { - List orderedDimValues = StatsHolder.getOrderedDimensionValues( - new ArrayList<>(expectedDims), - dimensionNames - ); - if (orderedDimValues.containsAll(aggregatedKey.getDimensionValues())) { - expectedResponseForTier.add(expected.get(tier).get(expectedDims)); - } - } - if (expectedResponseForTier.equals(new CacheStatsResponse())) { - expectedResponseForTier = null; // If it's all 0, there were no keys - } - expectedResponseForTierMap.put(tier, expectedResponseForTier); - } - CacheStatsResponse expectedHeapResponse = expectedResponseForTierMap.get(TIER_DIMENSION_VALUE_ON_HEAP); - CacheStatsResponse expectedDiskResponse = expectedResponseForTierMap.get(TIER_DIMENSION_VALUE_DISK); - if (expectedHeapResponse != null && expectedDiskResponse != null) { - assertEquals(combineTierResponses(expectedHeapResponse.snapshot(), expectedDiskResponse.snapshot()), aggregatedEntry.getValue()); - } else if (expectedHeapResponse != null) { - assertEquals(expectedHeapResponse.snapshot(), aggregatedEntry.getValue()); - } else { - assertEquals(expectedDiskResponse.snapshot(), aggregatedEntry.getValue()); - } - } - } - } - } - - public void testAggregationSomeLevelsWithTier() throws Exception { - StatsHolder heapStats = new StatsHolder(dimensionNames); - StatsHolder diskStats = new StatsHolder(dimensionNames); - Map> usedDimensionValues = getUsedDimensionValues(heapStats, 10); - Map, CacheStatsResponse>> expected = populateStats(heapStats, diskStats, usedDimensionValues, 100, 2); - TieredSpilloverCacheStats stats = new TieredSpilloverCacheStats(heapStats.createSnapshot(), diskStats.createSnapshot(), dimensionNames); - - for (int i = 0; i < (1 << dimensionNames.size()); i++) { - // Test each combination of possible levels - List levels = new ArrayList<>(); - for (int nameIndex = 0; nameIndex < dimensionNames.size(); nameIndex++) { - if ((i & (1 << nameIndex)) != 0) { - levels.add(dimensionNames.get(nameIndex)); - } - } - levels.add(TIER_DIMENSION_NAME); - if (levels.size() == 1) { - assertThrows(IllegalArgumentException.class, () -> stats.aggregateByLevels(levels)); - } - else { - Map aggregated = stats.aggregateByLevels(levels); - for (Map.Entry aggregatedEntry : aggregated.entrySet()) { - StatsHolder.Key aggregatedKey = aggregatedEntry.getKey(); - String aggregatedKeyTier = aggregatedKey.getDimensionValues().get(aggregatedKey.getDimensionValues().size()-1); - CacheStatsResponse expectedResponse = new CacheStatsResponse(); - for (Set expectedDims : expected.get(aggregatedKeyTier).keySet()) { - List orderedDimValues = StatsHolder.getOrderedDimensionValues( - new ArrayList<>(expectedDims), - dimensionNames - ); - orderedDimValues.add(aggregatedKeyTier); - if (orderedDimValues.containsAll(aggregatedKey.getDimensionValues())) { - expectedResponse.add(expected.get(aggregatedKeyTier).get(expectedDims)); - } - } - assertEquals(expectedResponse.snapshot(), aggregatedEntry.getValue()); - } - } - } - } - - private CacheStatsResponse totalSumExpected(Map, CacheStatsResponse> expected) { - CacheStatsResponse result = new CacheStatsResponse(); - for (Set key : expected.keySet()) { - result.add(expected.get(key)); - } - return result; - } - - // Fill the tier stats and return a nested map from tier type and dimensions -> expected response - // Modified from MultiDimensionCacheStatsTests - we can't import it without adding a dependency on server.test module. - private Map, CacheStatsResponse>> populateStats(StatsHolder heapStats, StatsHolder diskStats, Map> usedDimensionValues, int numDistinctValuePairs, int numRepetitionsPerValue) { - Map, CacheStatsResponse>> expected = new HashMap<>(); - expected.put(TIER_DIMENSION_VALUE_ON_HEAP, new HashMap<>()); - expected.put(TIER_DIMENSION_VALUE_DISK, new HashMap<>()); - - Random rand = Randomness.get(); - Map statsHolderMap = Map.of(tierNames.get(0), heapStats, tierNames.get(1), diskStats); - for (String tier : tierNames) { - for (int i = 0; i < numDistinctValuePairs; i++) { - StatsHolder stats = statsHolderMap.get(tier); - List dimensions = getRandomDimList(stats.getDimensionNames(), usedDimensionValues, true, rand); - Set dimSet = new HashSet<>(dimensions); - Map, CacheStatsResponse> tierExpected = expected.get(tier); - if (tierExpected.get(dimSet) == null) { - tierExpected.put(dimSet, new CacheStatsResponse()); - } - ICacheKey dummyKey = getDummyKey(dimensions); - for (int j = 0; j < numRepetitionsPerValue; j++) { - - int numHitIncrements = rand.nextInt(10); - for (int k = 0; k < numHitIncrements; k++) { - stats.incrementHits(dummyKey); - tierExpected.get(new HashSet<>(dimensions)).hits.inc(); - } - - int numMissIncrements = rand.nextInt(10); - for (int k = 0; k < numMissIncrements; k++) { - stats.incrementMisses(dummyKey); - tierExpected.get(new HashSet<>(dimensions)).misses.inc(); - } - - int numEvictionIncrements = rand.nextInt(10); - for (int k = 0; k < numEvictionIncrements; k++) { - stats.incrementEvictions(dummyKey); - tierExpected.get(new HashSet<>(dimensions)).evictions.inc(); - } - - int numMemorySizeIncrements = rand.nextInt(10); - for (int k = 0; k < numMemorySizeIncrements; k++) { - long memIncrementAmount = rand.nextInt(5000); - stats.incrementSizeInBytes(dummyKey, memIncrementAmount); - tierExpected.get(new HashSet<>(dimensions)).sizeInBytes.inc(memIncrementAmount); - } - - int numEntryIncrements = rand.nextInt(9) + 1; - for (int k = 0; k < numEntryIncrements; k++) { - stats.incrementEntries(dummyKey); - tierExpected.get(new HashSet<>(dimensions)).entries.inc(); - } - - int numEntryDecrements = rand.nextInt(numEntryIncrements); - for (int k = 0; k < numEntryDecrements; k++) { - stats.decrementEntries(dummyKey); - tierExpected.get(new HashSet<>(dimensions)).entries.dec(); - } - } - - } - } - return expected; - } - - // Duplicated below functions from MultiDimensionCacheStatsTests. We can't import them without adding a dependency on server.test for this module. - - private List getRandomDimList(List dimensionNames, Map> usedDimensionValues, boolean pickValueForAllDims, Random rand) { - List result = new ArrayList<>(); - for (String dimName : dimensionNames) { - if (pickValueForAllDims || rand.nextBoolean()) { // if pickValueForAllDims, always pick a value for each dimension, otherwise do so 50% of the time - int index = between(0, usedDimensionValues.get(dimName).size() - 1); - result.add(new CacheStatsDimension(dimName, usedDimensionValues.get(dimName).get(index))); - } - } - return result; - } - private Map> getUsedDimensionValues(StatsHolder stats, int numValuesPerDim) { - Map> usedDimensionValues = new HashMap<>(); - for (int i = 0; i < stats.getDimensionNames().size(); i++) { - List values = new ArrayList<>(); - for (int j = 0; j < numValuesPerDim; j++) { - values.add(UUID.randomUUID().toString()); - } - usedDimensionValues.put(stats.getDimensionNames().get(i), values); - } - return usedDimensionValues; - } - - private static ICacheKey getDummyKey(List dims) { - return new ICacheKey<>(null, dims); - }*/ - -} diff --git a/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheTests.java b/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheTests.java index 98628203761a7..1637e0da90718 100644 --- a/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheTests.java +++ b/modules/cache-common/src/test/java/org/opensearch/cache/common/tier/TieredSpilloverCacheTests.java @@ -16,6 +16,7 @@ import org.opensearch.common.cache.RemovalNotification; import org.opensearch.common.cache.policy.CachedQueryResult; import org.opensearch.common.cache.settings.CacheSettings; +import org.opensearch.common.cache.stats.CacheStatsCounterSnapshot; import org.opensearch.common.cache.store.OpenSearchOnHeapCache; import org.opensearch.common.cache.store.config.CacheConfig; import org.opensearch.common.cache.store.settings.OpenSearchOnHeapCacheSettings;