|
44 | 44 | import org.apache.lucene.search.TopDocs;
|
45 | 45 | import org.apache.lucene.store.Directory;
|
46 | 46 | import org.apache.lucene.util.BytesRef;
|
| 47 | +import org.opensearch.Version; |
47 | 48 | import org.opensearch.cluster.metadata.IndexMetadata;
|
| 49 | +import org.opensearch.cluster.node.DiscoveryNode; |
| 50 | +import org.opensearch.cluster.node.DiscoveryNodes; |
| 51 | +import org.opensearch.cluster.routing.RecoverySource; |
| 52 | +import org.opensearch.cluster.routing.ShardRouting; |
| 53 | +import org.opensearch.cluster.routing.ShardRoutingHelper; |
| 54 | +import org.opensearch.cluster.routing.UnassignedInfo; |
48 | 55 | import org.opensearch.common.CheckedSupplier;
|
49 | 56 | import org.opensearch.common.cache.ICacheKey;
|
50 | 57 | import org.opensearch.common.cache.RemovalNotification;
|
|
55 | 62 | import org.opensearch.common.io.stream.BytesStreamOutput;
|
56 | 63 | import org.opensearch.common.lucene.index.OpenSearchDirectoryReader;
|
57 | 64 | import org.opensearch.common.settings.Settings;
|
| 65 | +import org.opensearch.common.unit.TimeValue; |
58 | 66 | import org.opensearch.common.util.FeatureFlags;
|
59 | 67 | import org.opensearch.common.util.io.IOUtils;
|
60 | 68 | import org.opensearch.core.common.bytes.AbstractBytesReference;
|
61 | 69 | import org.opensearch.core.common.bytes.BytesReference;
|
62 | 70 | import org.opensearch.core.common.io.stream.StreamInput;
|
63 | 71 | import org.opensearch.core.common.unit.ByteSizeValue;
|
| 72 | +import org.opensearch.core.index.Index; |
64 | 73 | import org.opensearch.core.index.shard.ShardId;
|
65 | 74 | import org.opensearch.core.xcontent.MediaTypeRegistry;
|
66 | 75 | import org.opensearch.core.xcontent.XContentHelper;
|
|
69 | 78 | import org.opensearch.index.cache.request.RequestCacheStats;
|
70 | 79 | import org.opensearch.index.cache.request.ShardRequestCache;
|
71 | 80 | import org.opensearch.index.query.TermQueryBuilder;
|
| 81 | +import org.opensearch.index.seqno.RetentionLeaseSyncer; |
72 | 82 | import org.opensearch.index.shard.IndexShard;
|
73 | 83 | import org.opensearch.index.shard.IndexShardState;
|
| 84 | +import org.opensearch.index.shard.IndexShardTestCase; |
74 | 85 | import org.opensearch.index.shard.ShardNotFoundException;
|
| 86 | +import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; |
75 | 87 | import org.opensearch.node.Node;
|
76 | 88 | import org.opensearch.test.ClusterServiceUtils;
|
77 | 89 | import org.opensearch.test.OpenSearchSingleNodeTestCase;
|
|
95 | 107 | import java.util.concurrent.Executors;
|
96 | 108 | import java.util.concurrent.atomic.AtomicInteger;
|
97 | 109 |
|
| 110 | +import static java.util.Collections.emptyMap; |
| 111 | +import static java.util.Collections.emptySet; |
98 | 112 | import static org.opensearch.indices.IndicesRequestCache.INDEX_DIMENSION_NAME;
|
99 | 113 | import static org.opensearch.indices.IndicesRequestCache.INDICES_CACHE_QUERY_SIZE;
|
100 | 114 | import static org.opensearch.indices.IndicesRequestCache.INDICES_REQUEST_CACHE_STALENESS_THRESHOLD_SETTING;
|
@@ -1298,6 +1312,113 @@ public void testGetOrComputeConcurrentlyWithMultipleIndices() throws Exception {
|
1298 | 1312 | executorService.shutdownNow();
|
1299 | 1313 | }
|
1300 | 1314 |
|
| 1315 | + public void testDeleteAndCreateIndexShardOnSameNodeAndVerifyStats() throws Exception { |
| 1316 | + threadPool = getThreadPool(); |
| 1317 | + String indexName = "test1"; |
| 1318 | + IndicesService indicesService = getInstanceFromNode(IndicesService.class); |
| 1319 | + // Create a shard |
| 1320 | + IndexService indexService = createIndex( |
| 1321 | + indexName, |
| 1322 | + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build() |
| 1323 | + ); |
| 1324 | + Index idx = resolveIndex(indexName); |
| 1325 | + ShardRouting shardRouting = indicesService.indexService(idx).getShard(0).routingEntry(); |
| 1326 | + IndexShard indexShard = indexService.getShard(0); |
| 1327 | + Directory dir = newDirectory(); |
| 1328 | + IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig()); |
| 1329 | + writer.addDocument(newDoc(0, "foo")); |
| 1330 | + writer.addDocument(newDoc(1, "hack")); |
| 1331 | + DirectoryReader reader = OpenSearchDirectoryReader.wrap(DirectoryReader.open(writer), indexShard.shardId()); |
| 1332 | + Loader loader = new Loader(reader, 0); |
| 1333 | + |
| 1334 | + // Set clean interval to a high value as we will do it manually here. |
| 1335 | + IndicesRequestCache cache = getIndicesRequestCache( |
| 1336 | + Settings.builder() |
| 1337 | + .put(IndicesRequestCache.INDICES_REQUEST_CACHE_CLEANUP_INTERVAL_SETTING_KEY, TimeValue.timeValueMillis(100000)) |
| 1338 | + .build() |
| 1339 | + ); |
| 1340 | + IndicesService.IndexShardCacheEntity cacheEntity = new IndicesService.IndexShardCacheEntity(indexShard); |
| 1341 | + TermQueryBuilder termQuery = new TermQueryBuilder("id", "bar"); |
| 1342 | + BytesReference termBytes = XContentHelper.toXContent(termQuery, MediaTypeRegistry.JSON, false); |
| 1343 | + |
| 1344 | + // Cache some values for indexShard |
| 1345 | + BytesReference value = cache.getOrCompute(cacheEntity, loader, reader, getTermBytes()); |
| 1346 | + |
| 1347 | + // Verify response and stats. |
| 1348 | + assertEquals("foo", value.streamInput().readString()); |
| 1349 | + RequestCacheStats stats = indexShard.requestCache().stats(); |
| 1350 | + assertEquals("foo", value.streamInput().readString()); |
| 1351 | + assertEquals(1, cache.count()); |
| 1352 | + assertEquals(1, stats.getMissCount()); |
| 1353 | + assertTrue(stats.getMemorySizeInBytes() > 0); |
| 1354 | + |
| 1355 | + // Remove the shard making its cache entries stale |
| 1356 | + IOUtils.close(reader, writer, dir); |
| 1357 | + indexService.removeShard(0, "force"); |
| 1358 | + |
| 1359 | + // We again try to create a shard with same ShardId |
| 1360 | + ShardRouting newRouting = shardRouting; |
| 1361 | + String nodeId = newRouting.currentNodeId(); |
| 1362 | + UnassignedInfo unassignedInfo = new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, "boom"); |
| 1363 | + newRouting = newRouting.moveToUnassigned(unassignedInfo) |
| 1364 | + .updateUnassigned(unassignedInfo, RecoverySource.EmptyStoreRecoverySource.INSTANCE); |
| 1365 | + newRouting = ShardRoutingHelper.initialize(newRouting, nodeId); |
| 1366 | + final DiscoveryNode localNode = new DiscoveryNode("foo", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT); |
| 1367 | + indexShard = indexService.createShard( |
| 1368 | + newRouting, |
| 1369 | + s -> {}, |
| 1370 | + RetentionLeaseSyncer.EMPTY, |
| 1371 | + SegmentReplicationCheckpointPublisher.EMPTY, |
| 1372 | + null, |
| 1373 | + null, |
| 1374 | + localNode, |
| 1375 | + null, |
| 1376 | + DiscoveryNodes.builder().add(localNode).build() |
| 1377 | + ); |
| 1378 | + |
| 1379 | + // Verify that the new shard requestStats entries are empty. |
| 1380 | + stats = indexShard.requestCache().stats(); |
| 1381 | + assertEquals("foo", value.streamInput().readString()); |
| 1382 | + assertEquals(1, cache.count()); // Still contains the old indexShard stale entry |
| 1383 | + assertEquals(0, stats.getMissCount()); |
| 1384 | + assertTrue(stats.getMemorySizeInBytes() == 0); |
| 1385 | + IndexShardTestCase.updateRoutingEntry(indexShard, newRouting); |
| 1386 | + |
| 1387 | + // Now we cache again with new IndexShard(same shardId as older one). |
| 1388 | + dir = newDirectory(); |
| 1389 | + writer = new IndexWriter(dir, newIndexWriterConfig()); |
| 1390 | + writer.addDocument(newDoc(0, "foo")); |
| 1391 | + writer.addDocument(newDoc(1, "hack")); |
| 1392 | + reader = OpenSearchDirectoryReader.wrap(DirectoryReader.open(writer), indexShard.shardId()); |
| 1393 | + loader = new Loader(reader, 0); |
| 1394 | + cacheEntity = new IndicesService.IndexShardCacheEntity(indexShard); |
| 1395 | + termQuery = new TermQueryBuilder("id", "bar"); |
| 1396 | + termBytes = XContentHelper.toXContent(termQuery, MediaTypeRegistry.JSON, false); |
| 1397 | + value = cache.getOrCompute(cacheEntity, loader, reader, getTermBytes()); |
| 1398 | + |
| 1399 | + // Assert response and stats. We verify that cache now has 2 entries, one for older/removed shard and other |
| 1400 | + // for the current shard. |
| 1401 | + assertEquals("foo", value.streamInput().readString()); |
| 1402 | + stats = indexShard.requestCache().stats(); |
| 1403 | + assertEquals("foo", value.streamInput().readString()); |
| 1404 | + assertEquals(2, cache.count()); // One entry for older shard and other for the current shard. |
| 1405 | + assertEquals(1, stats.getMissCount()); |
| 1406 | + assertTrue(stats.getMemorySizeInBytes() > 0); |
| 1407 | + |
| 1408 | + // Trigger clean up of cache. |
| 1409 | + cache.cacheCleanupManager.cleanCache(); |
| 1410 | + // Verify that cache still has entries for current shard and only removed older shards entries. |
| 1411 | + assertEquals(1, cache.count()); |
| 1412 | + |
| 1413 | + // Now make current indexShard entries stale as well. |
| 1414 | + reader.close(); |
| 1415 | + // Trigger clean up of cache and verify that cache has no entries now. |
| 1416 | + cache.cacheCleanupManager.cleanCache(); |
| 1417 | + assertEquals(0, cache.count()); |
| 1418 | + |
| 1419 | + IOUtils.close(reader, writer, dir, cache); |
| 1420 | + } |
| 1421 | + |
1301 | 1422 | public static String generateString(int length) {
|
1302 | 1423 | String characters = "abcdefghijklmnopqrstuvwxyz";
|
1303 | 1424 | StringBuilder sb = new StringBuilder(length);
|
|
0 commit comments