Skip to content

Commit 1e2273b

Browse files
author
Peter Alfonsi
committed
Moved stats removal tests to this PR
Signed-off-by: Peter Alfonsi <[email protected]>
1 parent 2d2e9b0 commit 1e2273b

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed

plugins/cache-ehcache/src/test/java/org/opensearch/cache/store/disk/EhCacheDiskCacheTests.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.opensearch.common.cache.RemovalNotification;
2121
import org.opensearch.common.cache.serializer.BytesReferenceSerializer;
2222
import org.opensearch.common.cache.serializer.Serializer;
23+
import org.opensearch.common.cache.stats.CacheStatsCounterSnapshot;
24+
import org.opensearch.common.cache.stats.MultiDimensionCacheStats;
2325
import org.opensearch.common.cache.store.config.CacheConfig;
2426
import org.opensearch.common.metrics.CounterMetric;
2527
import org.opensearch.common.settings.Settings;
@@ -797,6 +799,68 @@ public void testInvalidate() throws Exception {
797799
}
798800
}
799801

802+
// Modified from OpenSearchOnHeapCacheTests.java
803+
public void testInvalidateWithDropDimensions() throws Exception {
804+
Settings settings = Settings.builder().build();
805+
List<String> dimensionNames = List.of("dim1", "dim2");
806+
try (NodeEnvironment env = newNodeEnvironment(settings)) {
807+
ICache<String, String> ehCacheDiskCachingTier = new EhcacheDiskCache.Builder<String, String>().setThreadPoolAlias("ehcacheTest")
808+
.setStoragePath(env.nodePaths()[0].indicesPath.toString() + "/request_cache")
809+
.setKeySerializer(new StringSerializer())
810+
.setValueSerializer(new StringSerializer())
811+
.setDimensionNames(dimensionNames)
812+
.setKeyType(String.class)
813+
.setValueType(String.class)
814+
.setCacheType(CacheType.INDICES_REQUEST_CACHE)
815+
.setSettings(settings)
816+
.setMaximumWeightInBytes(CACHE_SIZE_IN_BYTES * 20) // bigger so no evictions happen
817+
.setExpireAfterAccess(TimeValue.MAX_VALUE)
818+
.setRemovalListener(new MockRemovalListener<>())
819+
.setWeigher((key, value) -> 1)
820+
.build();
821+
822+
List<ICacheKey<String>> keysAdded = new ArrayList<>();
823+
824+
for (int i = 0; i < 20; i++) {
825+
ICacheKey<String> key = new ICacheKey<>(UUID.randomUUID().toString(), getRandomDimensions(dimensionNames));
826+
keysAdded.add(key);
827+
ehCacheDiskCachingTier.put(key, UUID.randomUUID().toString());
828+
}
829+
830+
ICacheKey<String> keyToDrop = keysAdded.get(0);
831+
832+
CacheStatsCounterSnapshot snapshot = ((MultiDimensionCacheStats) ehCacheDiskCachingTier.stats()).getStatsForDimensionValues(
833+
keyToDrop.dimensions
834+
);
835+
assertNotNull(snapshot);
836+
837+
keyToDrop.setDropStatsForDimensions(true);
838+
ehCacheDiskCachingTier.invalidate(keyToDrop);
839+
840+
// Now assert the stats are gone for any key that has this combination of dimensions, but still there otherwise
841+
for (ICacheKey<String> keyAdded : keysAdded) {
842+
snapshot = ((MultiDimensionCacheStats) ehCacheDiskCachingTier.stats()).getStatsForDimensionValues(keyAdded.dimensions);
843+
if (keyAdded.dimensions.equals(keyToDrop.dimensions)) {
844+
assertNull(snapshot);
845+
} else {
846+
assertNotNull(snapshot);
847+
}
848+
}
849+
850+
ehCacheDiskCachingTier.close();
851+
}
852+
}
853+
854+
private List<String> getRandomDimensions(List<String> dimensionNames) {
855+
Random rand = Randomness.get();
856+
int bound = 3;
857+
List<String> result = new ArrayList<>();
858+
for (String dimName : dimensionNames) {
859+
result.add(String.valueOf(rand.nextInt(bound)));
860+
}
861+
return result;
862+
}
863+
800864
private static String generateRandomString(int length) {
801865
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
802866
StringBuilder randomString = new StringBuilder(length);

server/src/test/java/org/opensearch/common/cache/store/OpenSearchOnHeapCacheTests.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88

99
package org.opensearch.common.cache.store;
1010

11+
import org.opensearch.common.Randomness;
1112
import org.opensearch.common.cache.CacheType;
1213
import org.opensearch.common.cache.ICache;
1314
import org.opensearch.common.cache.ICacheKey;
1415
import org.opensearch.common.cache.LoadAwareCacheLoader;
1516
import org.opensearch.common.cache.RemovalListener;
1617
import org.opensearch.common.cache.RemovalNotification;
18+
import org.opensearch.common.cache.stats.CacheStatsCounterSnapshot;
19+
import org.opensearch.common.cache.stats.MultiDimensionCacheStats;
1720
import org.opensearch.common.cache.store.config.CacheConfig;
1821
import org.opensearch.common.cache.store.settings.OpenSearchOnHeapCacheSettings;
1922
import org.opensearch.common.metrics.CounterMetric;
@@ -22,6 +25,7 @@
2225

2326
import java.util.ArrayList;
2427
import java.util.List;
28+
import java.util.Random;
2529
import java.util.UUID;
2630

2731
import static org.opensearch.common.cache.store.settings.OpenSearchOnHeapCacheSettings.MAXIMUM_SIZE_IN_BYTES_KEY;
@@ -96,6 +100,48 @@ private OpenSearchOnHeapCache<String, String> getCache(int maxSizeKeys, MockRemo
96100
return (OpenSearchOnHeapCache<String, String>) onHeapCacheFactory.create(cacheConfig, CacheType.INDICES_REQUEST_CACHE, null);
97101
}
98102

103+
public void testInvalidateWithDropDimensions() throws Exception {
104+
MockRemovalListener<String, String> listener = new MockRemovalListener<>();
105+
int maxKeys = 50;
106+
OpenSearchOnHeapCache<String, String> cache = getCache(maxKeys, listener);
107+
108+
List<ICacheKey<String>> keysAdded = new ArrayList<>();
109+
110+
for (int i = 0; i < maxKeys - 5; i++) {
111+
ICacheKey<String> key = new ICacheKey<>(UUID.randomUUID().toString(), getRandomDimensions());
112+
keysAdded.add(key);
113+
cache.computeIfAbsent(key, getLoadAwareCacheLoader());
114+
}
115+
116+
ICacheKey<String> keyToDrop = keysAdded.get(0);
117+
118+
CacheStatsCounterSnapshot snapshot = ((MultiDimensionCacheStats) cache.stats()).getStatsForDimensionValues(keyToDrop.dimensions);
119+
assertNotNull(snapshot);
120+
121+
keyToDrop.setDropStatsForDimensions(true);
122+
cache.invalidate(keyToDrop);
123+
124+
// Now assert the stats are gone for any key that has this combination of dimensions, but still there otherwise
125+
for (ICacheKey<String> keyAdded : keysAdded) {
126+
snapshot = ((MultiDimensionCacheStats) cache.stats()).getStatsForDimensionValues(keyAdded.dimensions);
127+
if (keyAdded.dimensions.equals(keyToDrop.dimensions)) {
128+
assertNull(snapshot);
129+
} else {
130+
assertNotNull(snapshot);
131+
}
132+
}
133+
}
134+
135+
private List<String> getRandomDimensions() {
136+
Random rand = Randomness.get();
137+
int bound = 3;
138+
List<String> result = new ArrayList<>();
139+
for (String dimName : dimensionNames) {
140+
result.add(String.valueOf(rand.nextInt(bound)));
141+
}
142+
return result;
143+
}
144+
99145
private static class MockRemovalListener<K, V> implements RemovalListener<ICacheKey<K>, V> {
100146
CounterMetric numRemovals;
101147

server/src/test/java/org/opensearch/indices/IndicesRequestCacheTests.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,15 @@
4545
import org.apache.lucene.search.TopDocs;
4646
import org.apache.lucene.store.Directory;
4747
import org.apache.lucene.util.BytesRef;
48+
import org.opensearch.cluster.metadata.IndexMetadata;
4849
import org.opensearch.common.CheckedSupplier;
4950
import org.opensearch.common.cache.ICacheKey;
5051
import org.opensearch.common.cache.RemovalNotification;
5152
import org.opensearch.common.cache.RemovalReason;
5253
import org.opensearch.common.cache.module.CacheModule;
5354
import org.opensearch.common.cache.service.CacheService;
55+
import org.opensearch.common.cache.stats.CacheStatsCounterSnapshot;
56+
import org.opensearch.common.cache.stats.MultiDimensionCacheStats;
5457
import org.opensearch.common.io.stream.BytesStreamOutput;
5558
import org.opensearch.common.lucene.index.OpenSearchDirectoryReader;
5659
import org.opensearch.common.settings.Settings;
@@ -70,13 +73,15 @@
7073
import org.opensearch.index.query.TermQueryBuilder;
7174
import org.opensearch.index.shard.IndexShard;
7275
import org.opensearch.index.shard.IndexShardState;
76+
import org.opensearch.index.shard.ShardNotFoundException;
7377
import org.opensearch.node.Node;
7478
import org.opensearch.test.OpenSearchSingleNodeTestCase;
7579
import org.opensearch.threadpool.ThreadPool;
7680

7781
import java.io.IOException;
7882
import java.util.ArrayList;
7983
import java.util.Arrays;
84+
import java.util.List;
8085
import java.util.Optional;
8186
import java.util.UUID;
8287
import java.util.concurrent.atomic.AtomicInteger;
@@ -753,6 +758,117 @@ public void testCacheCleanupBasedOnStaleThreshold_StalenessLesserThanThreshold()
753758
terminate(threadPool);
754759
}
755760

761+
public void testClosingIndexWipesStats() throws Exception {
762+
IndicesService indicesService = getInstanceFromNode(IndicesService.class);
763+
// Create two indices each with multiple shards
764+
int numShards = 3;
765+
Settings indexSettings = Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numShards).build();
766+
String indexToKeepName = "test";
767+
String indexToCloseName = "test2";
768+
IndexService indexToKeep = createIndex(indexToKeepName, indexSettings);
769+
IndexService indexToClose = createIndex(indexToCloseName, indexSettings);
770+
for (int i = 0; i < numShards; i++) {
771+
// Check we can get all the shards we expect
772+
assertNotNull(indexToKeep.getShard(i));
773+
assertNotNull(indexToClose.getShard(i));
774+
}
775+
ThreadPool threadPool = getThreadPool();
776+
Settings settings = Settings.builder().put(INDICES_REQUEST_CACHE_STALENESS_THRESHOLD_SETTING.getKey(), "0.001%").build();
777+
IndicesRequestCache cache = new IndicesRequestCache(settings, (shardId -> {
778+
IndexService indexService = null;
779+
try {
780+
indexService = indicesService.indexServiceSafe(shardId.getIndex());
781+
} catch (IndexNotFoundException ex) {
782+
return Optional.empty();
783+
}
784+
try {
785+
return Optional.of(new IndicesService.IndexShardCacheEntity(indexService.getShard(shardId.id())));
786+
} catch (ShardNotFoundException ex) {
787+
return Optional.empty();
788+
}
789+
}), new CacheModule(new ArrayList<>(), Settings.EMPTY).getCacheService(), threadPool);
790+
Directory dir = newDirectory();
791+
IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig());
792+
793+
writer.addDocument(newDoc(0, "foo"));
794+
TermQueryBuilder termQuery = new TermQueryBuilder("id", "0");
795+
BytesReference termBytes = XContentHelper.toXContent(termQuery, MediaTypeRegistry.JSON, false);
796+
if (randomBoolean()) {
797+
writer.flush();
798+
IOUtils.close(writer);
799+
writer = new IndexWriter(dir, newIndexWriterConfig());
800+
}
801+
writer.updateDocument(new Term("id", "0"), newDoc(0, "bar"));
802+
DirectoryReader secondReader = OpenSearchDirectoryReader.wrap(DirectoryReader.open(writer), new ShardId("foo", "bar", 1));
803+
804+
List<DirectoryReader> readersToClose = new ArrayList<>();
805+
List<DirectoryReader> readersToKeep = new ArrayList<>();
806+
// Put entries into the cache for each shard
807+
for (IndexService indexService : new IndexService[] { indexToKeep, indexToClose }) {
808+
for (int i = 0; i < numShards; i++) {
809+
IndexShard indexShard = indexService.getShard(i);
810+
IndicesService.IndexShardCacheEntity entity = new IndicesService.IndexShardCacheEntity(indexShard);
811+
DirectoryReader reader = OpenSearchDirectoryReader.wrap(DirectoryReader.open(writer), indexShard.shardId());
812+
if (indexService == indexToClose) {
813+
readersToClose.add(reader);
814+
} else {
815+
readersToKeep.add(reader);
816+
}
817+
Loader loader = new Loader(reader, 0);
818+
cache.getOrCompute(entity, loader, reader, termBytes);
819+
}
820+
}
821+
822+
// Check resulting stats
823+
List<List<String>> initialDimensionValues = new ArrayList<>();
824+
for (IndexService indexService : new IndexService[] { indexToKeep, indexToClose }) {
825+
for (int i = 0; i < numShards; i++) {
826+
ShardId shardId = indexService.getShard(i).shardId();
827+
List<String> dimensionValues = List.of(shardId.getIndexName(), shardId.toString());
828+
initialDimensionValues.add(dimensionValues);
829+
CacheStatsCounterSnapshot snapshot = ((MultiDimensionCacheStats) cache.getCacheStats()).getStatsForDimensionValues(
830+
dimensionValues
831+
);
832+
assertNotNull(snapshot);
833+
// check the values are not empty by confirming entries != 0, this should always be true since the missed value is loaded
834+
// into the cache
835+
assertNotEquals(0, snapshot.getEntries());
836+
}
837+
}
838+
839+
// Delete an index
840+
indexToClose.close("test_deletion", true);
841+
// This actually closes the shards associated with the readers, which is necessary for cache cleanup logic
842+
// In this UT, manually close the readers as well; could not figure out how to connect all this up in a UT so that
843+
// we could get readers that were properly connected to an index's directory
844+
for (DirectoryReader reader : readersToClose) {
845+
IOUtils.close(reader);
846+
}
847+
// Trigger cache cleanup
848+
cache.cacheCleanupManager.cleanCache();
849+
850+
// Now stats for the closed index should be gone
851+
for (List<String> dimensionValues : initialDimensionValues) {
852+
CacheStatsCounterSnapshot snapshot = ((MultiDimensionCacheStats) cache.getCacheStats()).getStatsForDimensionValues(
853+
dimensionValues
854+
);
855+
if (dimensionValues.get(0).equals(indexToCloseName)) {
856+
assertNull(snapshot);
857+
} else {
858+
assertNotNull(snapshot);
859+
// check the values are not empty by confirming entries != 0, this should always be true since the missed value is loaded
860+
// into the cache
861+
assertNotEquals(0, snapshot.getEntries());
862+
}
863+
}
864+
865+
for (DirectoryReader reader : readersToKeep) {
866+
IOUtils.close(reader);
867+
}
868+
IOUtils.close(secondReader, writer, dir, cache);
869+
terminate(threadPool);
870+
}
871+
756872
public void testEviction() throws Exception {
757873
final ByteSizeValue size;
758874
{

0 commit comments

Comments
 (0)