|  | 
| 8 | 8 |  */ | 
| 9 | 9 | package org.elasticsearch.index.engine; | 
| 10 | 10 | 
 | 
|  | 11 | +import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; | 
|  | 12 | +import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; | 
| 11 | 13 | import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; | 
| 12 | 14 | import org.elasticsearch.action.bulk.BulkRequestBuilder; | 
| 13 | 15 | import org.elasticsearch.action.bulk.BulkResponse; | 
| 14 | 16 | import org.elasticsearch.action.index.IndexRequest; | 
|  | 17 | +import org.elasticsearch.common.settings.Settings; | 
|  | 18 | +import org.elasticsearch.index.query.QueryBuilders; | 
| 15 | 19 | import org.elasticsearch.test.ESIntegTestCase; | 
| 16 | 20 | import org.elasticsearch.test.ESIntegTestCase.ClusterScope; | 
| 17 | 21 | import org.elasticsearch.test.ESIntegTestCase.Scope; | 
|  | 22 | +import org.elasticsearch.threadpool.ThreadPool; | 
| 18 | 23 | 
 | 
| 19 | 24 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; | 
|  | 25 | +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; | 
| 20 | 26 | import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; | 
| 21 | 27 | import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; | 
| 22 | 28 | import static org.hamcrest.Matchers.equalTo; | 
| 23 | 29 | import static org.hamcrest.Matchers.lessThan; | 
| 24 | 30 | import static org.hamcrest.Matchers.lessThanOrEqualTo; | 
| 25 | 31 | 
 | 
| 26 |  | -@ClusterScope(supportsDedicatedMasters = false, numDataNodes = 1, scope = Scope.SUITE) | 
|  | 32 | +@ClusterScope(supportsDedicatedMasters = false, numDataNodes = 1, numClientNodes = 0, scope = Scope.TEST) | 
| 27 | 33 | public class InternalEngineMergeIT extends ESIntegTestCase { | 
| 28 | 34 | 
 | 
|  | 35 | +    private boolean useThreadPoolMerging; | 
|  | 36 | + | 
|  | 37 | +    @Override | 
|  | 38 | +    protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { | 
|  | 39 | +        useThreadPoolMerging = randomBoolean(); | 
|  | 40 | +        Settings.Builder settings = Settings.builder().put(super.nodeSettings(nodeOrdinal, otherSettings)); | 
|  | 41 | +        settings.put(ThreadPoolMergeScheduler.USE_THREAD_POOL_MERGE_SCHEDULER_SETTING.getKey(), useThreadPoolMerging); | 
|  | 42 | +        return settings.build(); | 
|  | 43 | +    } | 
|  | 44 | + | 
| 29 | 45 |     public void testMergesHappening() throws Exception { | 
| 30 | 46 |         final int numOfShards = randomIntBetween(1, 5); | 
| 31 | 47 |         // some settings to keep num segments low | 
| @@ -83,4 +99,60 @@ public void testMergesHappening() throws Exception { | 
| 83 | 99 |         assertThat(count, lessThanOrEqualTo(upperNumberSegments)); | 
| 84 | 100 |     } | 
| 85 | 101 | 
 | 
|  | 102 | +    public void testMergesUseTheMergeThreadPool() throws Exception { | 
|  | 103 | +        final String indexName = randomIdentifier(); | 
|  | 104 | +        createIndex(indexName, indexSettings(randomIntBetween(1, 3), 0).build()); | 
|  | 105 | +        long id = 0; | 
|  | 106 | +        final int minMerges = randomIntBetween(1, 5); | 
|  | 107 | +        long totalDocs = 0; | 
|  | 108 | + | 
|  | 109 | +        while (true) { | 
|  | 110 | +            int docs = randomIntBetween(100, 200); | 
|  | 111 | +            totalDocs += docs; | 
|  | 112 | + | 
|  | 113 | +            BulkRequestBuilder request = client().prepareBulk(); | 
|  | 114 | +            for (int j = 0; j < docs; ++j) { | 
|  | 115 | +                request.add( | 
|  | 116 | +                    new IndexRequest(indexName).id(Long.toString(id++)) | 
|  | 117 | +                        .source(jsonBuilder().startObject().field("l", randomLong()).endObject()) | 
|  | 118 | +                ); | 
|  | 119 | +            } | 
|  | 120 | +            BulkResponse response = request.get(); | 
|  | 121 | +            assertNoFailures(response); | 
|  | 122 | +            refresh(indexName); | 
|  | 123 | + | 
|  | 124 | +            var mergesResponse = client().admin().indices().prepareStats(indexName).clear().setMerge(true).get(); | 
|  | 125 | +            var primaries = mergesResponse.getIndices().get(indexName).getPrimaries(); | 
|  | 126 | +            if (primaries.merge.getTotal() >= minMerges) { | 
|  | 127 | +                break; | 
|  | 128 | +            } | 
|  | 129 | +        } | 
|  | 130 | + | 
|  | 131 | +        forceMerge(); | 
|  | 132 | +        refresh(indexName); | 
|  | 133 | + | 
|  | 134 | +        // after a force merge there should only be 1 segment per shard | 
|  | 135 | +        var shardsWithMultipleSegments = getShardSegments().stream() | 
|  | 136 | +            .filter(shardSegments -> shardSegments.getSegments().size() > 1) | 
|  | 137 | +            .toList(); | 
|  | 138 | +        assertTrue("there are shards with multiple segments " + shardsWithMultipleSegments, shardsWithMultipleSegments.isEmpty()); | 
|  | 139 | + | 
|  | 140 | +        final long expectedTotalDocs = totalDocs; | 
|  | 141 | +        assertHitCount(prepareSearch(indexName).setQuery(QueryBuilders.matchAllQuery()).setTrackTotalHits(true), expectedTotalDocs); | 
|  | 142 | + | 
|  | 143 | +        IndicesStatsResponse indicesStats = client().admin().indices().prepareStats(indexName).setMerge(true).get(); | 
|  | 144 | +        long mergeCount = indicesStats.getIndices().get(indexName).getPrimaries().merge.getTotal(); | 
|  | 145 | +        NodesStatsResponse nodesStatsResponse = client().admin().cluster().prepareNodesStats().setThreadPool(true).get(); | 
|  | 146 | +        assertThat(nodesStatsResponse.getNodes().size(), equalTo(1)); | 
|  | 147 | + | 
|  | 148 | +        NodeStats nodeStats = nodesStatsResponse.getNodes().get(0); | 
|  | 149 | +        if (useThreadPoolMerging) { | 
|  | 150 | +            assertThat( | 
|  | 151 | +                nodeStats.getThreadPool().stats().stream().filter(s -> ThreadPool.Names.MERGE.equals(s.name())).findAny().get().completed(), | 
|  | 152 | +                equalTo(mergeCount) | 
|  | 153 | +            ); | 
|  | 154 | +        } else { | 
|  | 155 | +            assertTrue(nodeStats.getThreadPool().stats().stream().filter(s -> ThreadPool.Names.MERGE.equals(s.name())).findAny().isEmpty()); | 
|  | 156 | +        } | 
|  | 157 | +    } | 
| 86 | 158 | } | 
0 commit comments