From 2cc9bb70c5aea4a37b3cd52b34cf87370714c483 Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Mon, 1 Dec 2025 16:16:16 -0600 Subject: [PATCH 1/6] add autotagging support to the scroll API Signed-off-by: Lamine Idjeraoui --- .../plugin/wlm/WlmAutoTaggingIT.java | 37 ++++++++++++++++++ .../plugin/wlm/AutoTaggingActionFilter.java | 39 ++++++++++++++++++- .../wlm/AutoTaggingActionFilterTests.java | 21 ++++++++++ .../search/AbstractSearchAsyncAction.java | 4 +- .../action/search/ParsedScrollId.java | 8 +++- .../action/search/SearchScrollRequest.java | 6 ++- .../action/search/TransportSearchHelper.java | 33 +++++++++++++++- .../search/TransportSearchScrollAction.java | 31 ++++++++++++++- .../action/search/ParsedScrollIdTests.java | 7 +++- .../search/SearchScrollAsyncActionTests.java | 2 +- 10 files changed, 178 insertions(+), 10 deletions(-) diff --git a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java index 4f8dfa89027ee..835eb2b27664a 100644 --- a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java +++ b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java @@ -439,6 +439,43 @@ public void testDeleteRuleForNonexistentId() throws Exception { assertTrue("Expected error message for nonexistent rule ID", exception.getMessage().contains("no such index")); } + public void testScrollRequestsAreAlsoTagged() throws Exception { + String workloadGroupId = "wlm_auto_tag_scroll"; + String ruleId = "wlm_auto_tag_scroll_rule"; + String indexName = "scroll_tagged_index"; + + setWlmMode("enabled"); + + WorkloadGroup workloadGroup = createWorkloadGroup("scroll_tagging_group", workloadGroupId); + updateWorkloadGroupInClusterState(PUT, workloadGroup); + + FeatureType featureType = AutoTaggingRegistry.getFeatureType(WorkloadGroupFeatureType.NAME); + createRule(ruleId, "scroll tagging rule", indexName, featureType, workloadGroupId); + + indexDocument(indexName); + + assertBusy(() -> { + int completionsBefore = getCompletions(workloadGroupId); + + SearchResponse initial = client().prepareSearch(indexName) + .setQuery(QueryBuilders.matchAllQuery()) + .setScroll(TimeValue.timeValueMinutes(1)) + .setSize(1) + .get(); + + String scrollId = initial.getScrollId(); + assertNotNull("scrollId must not be null", scrollId); + + int afterInitialSearch = getCompletions(workloadGroupId); + assertTrue("Expected completions to increase after initial search with scroll", afterInitialSearch > completionsBefore); + + client().prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1)).get(); + + int afterScroll = getCompletions(workloadGroupId); + assertTrue("Expected completions to increase after scroll request as well", afterScroll > afterInitialSearch); + }); + } + // Helper functions private void createRule(String ruleId, String ruleName, String indexPattern, FeatureType featureType, String workloadGroupId) throws Exception { diff --git a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java index c6294ed7ac242..2b57ccd74ad4e 100644 --- a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java +++ b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java @@ -11,14 +11,18 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.IndicesRequest; import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.action.support.ActionFilter; import org.opensearch.action.support.ActionFilterChain; import org.opensearch.action.support.ActionRequestMetadata; +import org.opensearch.cluster.metadata.OptionallyResolvedIndices; +import org.opensearch.cluster.metadata.ResolvedIndices; import org.opensearch.core.action.ActionListener; import org.opensearch.core.action.ActionResponse; import org.opensearch.plugin.wlm.rule.attribute_extractor.IndicesExtractor; import org.opensearch.plugin.wlm.spi.AttributeExtractorExtension; import org.opensearch.rule.InMemoryRuleProcessingService; +import org.opensearch.rule.RuleAttribute; import org.opensearch.rule.attribute_extractor.AttributeExtractor; import org.opensearch.rule.autotagging.Attribute; import org.opensearch.rule.autotagging.FeatureType; @@ -31,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static org.opensearch.plugin.wlm.WorkloadManagementPlugin.PRINCIPAL_ATTRIBUTE_NAME; @@ -80,14 +85,44 @@ public void app ActionListener listener, ActionFilterChain chain ) { - final boolean isValidRequest = request instanceof SearchRequest; + final boolean isSearchRequest = request instanceof SearchRequest; + final boolean isSearchScrollRequest = request instanceof SearchScrollRequest; + final boolean isValidRequest = isSearchRequest || isSearchScrollRequest; if (!isValidRequest || wlmClusterSettingValuesProvider.getWlmMode() == WlmMode.DISABLED) { chain.proceed(task, action, request, listener); return; } List> attributeExtractors = new ArrayList<>(); - attributeExtractors.add(new IndicesExtractor((IndicesRequest) request)); + final OptionallyResolvedIndices optionallyResolved = actionRequestMetadata.resolvedIndices(); + final boolean hasResolvedIndices = optionallyResolved instanceof ResolvedIndices; + + if (hasResolvedIndices) { + final ResolvedIndices resolved = (ResolvedIndices) optionallyResolved; + final Set names = resolved.local().names(); + + attributeExtractors.add(new AttributeExtractor<>() { + @Override + public Attribute getAttribute() { + return RuleAttribute.INDEX_PATTERN; + } + + @Override + public Iterable extract() { + return names; + } + + @Override + public LogicalOperator getLogicalOperator() { + return LogicalOperator.AND; + } + }); + } else if (isSearchRequest) { + attributeExtractors.add(new IndicesExtractor((IndicesRequest) request)); + } else { + chain.proceed(task, action, request, listener); + return; + } if (featureType.getAllowedAttributesRegistry().containsKey(PRINCIPAL_ATTRIBUTE_NAME)) { Attribute attribute = featureType.getAllowedAttributesRegistry().get(PRINCIPAL_ATTRIBUTE_NAME); diff --git a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java index ed5e8e25843ea..2766aebb6ec28 100644 --- a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java +++ b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java @@ -11,8 +11,10 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.action.support.ActionFilterChain; import org.opensearch.action.support.ActionRequestMetadata; +import org.opensearch.cluster.metadata.ResolvedIndices; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.core.action.ActionResponse; @@ -93,6 +95,25 @@ public void testApplyForInValidRequest() { verify(ruleProcessingService, times(0)).evaluateLabel(anyList()); } + public void testApplyForScrollRequestWithResolvedIndices() { + SearchScrollRequest request = mock(SearchScrollRequest.class); + ActionFilterChain mockFilterChain = mock(TestActionFilterChain.class); + + @SuppressWarnings("unchecked") + ActionRequestMetadata metadata = mock(ActionRequestMetadata.class); + ResolvedIndices resolved = ResolvedIndices.of("logs-scroll-index"); + when(metadata.resolvedIndices()).thenReturn(resolved); + + try (ThreadContext.StoredContext context = threadPool.getThreadContext().stashContext()) { + when(ruleProcessingService.evaluateLabel(anyList())).thenReturn(Optional.of("ScrollQG_ID")); + + autoTaggingActionFilter.apply(mock(Task.class), "Test", request, metadata, null, mockFilterChain); + + assertEquals("ScrollQG_ID", threadPool.getThreadContext().getHeader(WorkloadGroupTask.WORKLOAD_GROUP_ID_HEADER)); + verify(ruleProcessingService, times(1)).evaluateLabel(anyList()); + } + } + public enum WLMFeatureType implements FeatureType { WLM; diff --git a/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java index 59bb88b0f6f67..00b6280af7323 100644 --- a/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java @@ -774,7 +774,9 @@ public void sendSearchResponse(InternalSearchResponse internalSearchResponse, At raisePhaseFailure(new SearchPhaseExecutionException("", "Shard failures", null, failures)); } else { final Version minNodeVersion = clusterState.nodes().getMinNodeVersion(); - final String scrollId = request.scroll() != null ? TransportSearchHelper.buildScrollId(queryResults, minNodeVersion) : null; + final String scrollId = request.scroll() != null + ? TransportSearchHelper.buildScrollId(queryResults, request.indices(), minNodeVersion) + : null; final String searchContextId; if (buildPointInTimeFromSearchResults()) { searchContextId = SearchContextId.encode(queryResults.asList(), aliasFilter, minNodeVersion); diff --git a/server/src/main/java/org/opensearch/action/search/ParsedScrollId.java b/server/src/main/java/org/opensearch/action/search/ParsedScrollId.java index b723b97b5c413..82009af3b0cd1 100644 --- a/server/src/main/java/org/opensearch/action/search/ParsedScrollId.java +++ b/server/src/main/java/org/opensearch/action/search/ParsedScrollId.java @@ -53,11 +53,13 @@ public class ParsedScrollId { private final String type; private final SearchContextIdForNode[] context; + private final String[] originalIndices; - ParsedScrollId(String source, String type, SearchContextIdForNode[] context) { + ParsedScrollId(String source, String type, SearchContextIdForNode[] context, String[] originalIndices) { this.source = source; this.type = type; this.context = context; + this.originalIndices = originalIndices; } public String getSource() { @@ -72,6 +74,10 @@ public SearchContextIdForNode[] getContext() { return context; } + public String[] getOriginalIndices() { + return originalIndices; + } + public boolean hasLocalIndices() { return Arrays.stream(context).anyMatch(c -> c.getClusterAlias() == null); } diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java index 044efdc36d04f..a25a3ff719c52 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java @@ -61,6 +61,7 @@ public class SearchScrollRequest extends ActionRequest implements ToXContentObje private String scrollId; private Scroll scroll; + private transient ParsedScrollId parsedScrollId; public SearchScrollRequest() {} @@ -103,7 +104,10 @@ public SearchScrollRequest scrollId(String scrollId) { } public ParsedScrollId parseScrollId() { - return TransportSearchHelper.parseScrollId(scrollId); + if (parsedScrollId == null && scrollId != null) { + parsedScrollId = TransportSearchHelper.parseScrollId(scrollId); + } + return parsedScrollId; } /** diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java index 5c260e02e7275..900b1223c6916 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java @@ -60,7 +60,13 @@ static InternalScrollSearchRequest internalScrollSearchRequest(ShardSearchContex return new InternalScrollSearchRequest(request, id); } + public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_3_2; + static String buildScrollId(AtomicArray searchPhaseResults, Version version) { + return buildScrollId(searchPhaseResults, null, version); + } + + static String buildScrollId(AtomicArray searchPhaseResults, String[] originalIndices, Version version) { try { BytesStreamOutput out = new BytesStreamOutput(); out.writeString(INCLUDE_CONTEXT_UUID); @@ -78,6 +84,19 @@ static String buildScrollId(AtomicArray searchPhase out.writeString(searchShardTarget.getNodeId()); } } + + if (version.onOrAfter(INDICES_IN_SCROLL_ID_VERSION)) { + // To keep autotagging consistent between the initial SearchRequest + // and subsequent SearchScrollRequests, we store exactly the same + // index targets that were visible to the indices attribute during + // the "search" phase + if (originalIndices != null && originalIndices.length > 0) { + out.writeVInt(originalIndices.length); + for (String index : originalIndices) { + out.writeString(index); + } + } + } byte[] bytes = BytesReference.toBytes(out.bytes()); return Base64.getUrlEncoder().encodeToString(bytes); } catch (IOException e) { @@ -114,10 +133,22 @@ static ParsedScrollId parseScrollId(String scrollId) { } context[i] = new SearchContextIdForNode(clusterAlias, target, new ShardSearchContextId(contextUUID, id)); } + + String[] originalIndices; + if (in.getPosition() < bytes.length) { + final int numOriginalIndices = in.readVInt(); + originalIndices = new String[numOriginalIndices]; + for (int i = 0; i < numOriginalIndices; i++) { + originalIndices[i] = in.readString(); + } + } else { + originalIndices = new String[0]; + } + if (in.getPosition() != bytes.length) { throw new IllegalArgumentException("Not all bytes were read"); } - return new ParsedScrollId(scrollId, type, context); + return new ParsedScrollId(scrollId, type, context, originalIndices); } catch (Exception e) { throw new IllegalArgumentException("Cannot parse scroll id", e); } diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java index b0f98a4c1703b..3db9e93fdd092 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java @@ -34,6 +34,9 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.action.support.TransportIndicesResolvingAction; +import org.opensearch.cluster.metadata.OptionallyResolvedIndices; +import org.opensearch.cluster.metadata.ResolvedIndices; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.core.action.ActionListener; @@ -48,7 +51,9 @@ * * @opensearch.internal */ -public class TransportSearchScrollAction extends HandledTransportAction { +public class TransportSearchScrollAction extends HandledTransportAction + implements + TransportIndicesResolvingAction { private final ClusterService clusterService; private final SearchTransportService searchTransportService; @@ -79,7 +84,7 @@ protected void doExecute(Task task, SearchScrollRequest request, ActionListener< ((WorkloadGroupTask) task).setWorkloadGroupId(threadPool.getThreadContext()); } - ParsedScrollId scrollId = TransportSearchHelper.parseScrollId(request.scrollId()); + ParsedScrollId scrollId = request.parseScrollId(); Runnable action; switch (scrollId.getType()) { case ParsedScrollId.QUERY_THEN_FETCH_TYPE: @@ -114,4 +119,26 @@ protected void doExecute(Task task, SearchScrollRequest request, ActionListener< listener.onFailure(e); } } + + @Override + public OptionallyResolvedIndices resolveIndices(SearchScrollRequest request) { + try { + final String scrollIdString = request.scrollId(); + if (scrollIdString == null || scrollIdString.isEmpty()) { + return OptionallyResolvedIndices.unknown(); + } + + final ParsedScrollId parsed = request.parseScrollId(); + if (parsed == null) { + return OptionallyResolvedIndices.unknown(); + } + final String[] originalIndices = parsed.getOriginalIndices(); + if (originalIndices == null || originalIndices.length == 0) { + return OptionallyResolvedIndices.unknown(); + } + return ResolvedIndices.of(originalIndices); + } catch (Exception e) { + return OptionallyResolvedIndices.unknown(); + } + } } diff --git a/server/src/test/java/org/opensearch/action/search/ParsedScrollIdTests.java b/server/src/test/java/org/opensearch/action/search/ParsedScrollIdTests.java index 2d90bf9ba1bdd..0985cb5308802 100644 --- a/server/src/test/java/org/opensearch/action/search/ParsedScrollIdTests.java +++ b/server/src/test/java/org/opensearch/action/search/ParsedScrollIdTests.java @@ -50,7 +50,12 @@ public void testHasLocalIndices() { new ShardSearchContextId(randomAlphaOfLength(8), randomLong()) ); } - final ParsedScrollId parsedScrollId = new ParsedScrollId(randomAlphaOfLength(8), randomAlphaOfLength(8), searchContextIdForNodes); + final ParsedScrollId parsedScrollId = new ParsedScrollId( + randomAlphaOfLength(8), + randomAlphaOfLength(8), + searchContextIdForNodes, + new String[0] + ); assertEquals(hasLocal, parsedScrollId.hasLocalIndices()); } diff --git a/server/src/test/java/org/opensearch/action/search/SearchScrollAsyncActionTests.java b/server/src/test/java/org/opensearch/action/search/SearchScrollAsyncActionTests.java index 12ab735c4d324..f5ceef0885520 100644 --- a/server/src/test/java/org/opensearch/action/search/SearchScrollAsyncActionTests.java +++ b/server/src/test/java/org/opensearch/action/search/SearchScrollAsyncActionTests.java @@ -481,7 +481,7 @@ protected void onFirstPhaseResult(int shardId, SearchAsyncActionTests.TestSearch private static ParsedScrollId getParsedScrollId(SearchContextIdForNode... idsForNodes) { List searchContextIdForNodes = Arrays.asList(idsForNodes); Collections.shuffle(searchContextIdForNodes, random()); - return new ParsedScrollId("", "test", searchContextIdForNodes.toArray(new SearchContextIdForNode[0])); + return new ParsedScrollId("", "test", searchContextIdForNodes.toArray(new SearchContextIdForNode[0]), new String[0]); } private ActionListener dummyListener() { From 7497b6a3e9de2a99adfe63b68e4977c53e8275d5 Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Tue, 13 Jan 2026 18:28:11 -0600 Subject: [PATCH 2/6] addTransportOriginalIndicesAction Signed-off-by: Lamine Idjeraoui --- .../plugin/wlm/AutoTaggingActionFilter.java | 51 ++++++++----------- .../wlm/AutoTaggingActionFilterTests.java | 39 ++++++++++---- .../search/TransportSearchScrollAction.java | 33 +++++------- .../action/support/ActionRequestMetadata.java | 9 ++++ .../TransportOriginalIndicesAction.java | 31 +++++++++++ 5 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java diff --git a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java index 2b57ccd74ad4e..5ecc54db49780 100644 --- a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java +++ b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java @@ -15,8 +15,6 @@ import org.opensearch.action.support.ActionFilter; import org.opensearch.action.support.ActionFilterChain; import org.opensearch.action.support.ActionRequestMetadata; -import org.opensearch.cluster.metadata.OptionallyResolvedIndices; -import org.opensearch.cluster.metadata.ResolvedIndices; import org.opensearch.core.action.ActionListener; import org.opensearch.core.action.ActionResponse; import org.opensearch.plugin.wlm.rule.attribute_extractor.IndicesExtractor; @@ -32,10 +30,10 @@ import org.opensearch.wlm.WorkloadGroupTask; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import static org.opensearch.plugin.wlm.WorkloadManagementPlugin.PRINCIPAL_ATTRIBUTE_NAME; @@ -94,34 +92,29 @@ public void app return; } List> attributeExtractors = new ArrayList<>(); - final OptionallyResolvedIndices optionallyResolved = actionRequestMetadata.resolvedIndices(); - final boolean hasResolvedIndices = optionallyResolved instanceof ResolvedIndices; - - if (hasResolvedIndices) { - final ResolvedIndices resolved = (ResolvedIndices) optionallyResolved; - final Set names = resolved.local().names(); - - attributeExtractors.add(new AttributeExtractor<>() { - @Override - public Attribute getAttribute() { - return RuleAttribute.INDEX_PATTERN; - } - - @Override - public Iterable extract() { - return names; - } - - @Override - public LogicalOperator getLogicalOperator() { - return LogicalOperator.AND; - } - }); - } else if (isSearchRequest) { + if (isSearchRequest) { attributeExtractors.add(new IndicesExtractor((IndicesRequest) request)); } else { - chain.proceed(task, action, request, listener); - return; + // Scroll: recover the original user-provided expressions from metadata + final String[] originalIndices = actionRequestMetadata.originalIndices(); + if (originalIndices != null && originalIndices.length > 0) { + attributeExtractors.add(new AttributeExtractor<>() { + @Override + public Attribute getAttribute() { + return RuleAttribute.INDEX_PATTERN; + } + + @Override + public Iterable extract() { + return Arrays.asList(originalIndices); + } + + @Override + public LogicalOperator getLogicalOperator() { + return LogicalOperator.AND; + } + }); + } } if (featureType.getAllowedAttributesRegistry().containsKey(PRINCIPAL_ATTRIBUTE_NAME)) { diff --git a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java index 2766aebb6ec28..52832dc69b8c2 100644 --- a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java +++ b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java @@ -14,12 +14,13 @@ import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.action.support.ActionFilterChain; import org.opensearch.action.support.ActionRequestMetadata; -import org.opensearch.cluster.metadata.ResolvedIndices; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.core.action.ActionResponse; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.rule.InMemoryRuleProcessingService; +import org.opensearch.rule.RuleAttribute; +import org.opensearch.rule.attribute_extractor.AttributeExtractor; import org.opensearch.rule.autotagging.Attribute; import org.opensearch.rule.autotagging.FeatureType; import org.opensearch.rule.storage.AttributeValueStoreFactory; @@ -31,16 +32,20 @@ import org.opensearch.wlm.WorkloadGroupTask; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; -import static org.mockito.Mockito.anyList; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.anyList; public class AutoTaggingActionFilterTests extends OpenSearchTestCase { @@ -95,19 +100,33 @@ public void testApplyForInValidRequest() { verify(ruleProcessingService, times(0)).evaluateLabel(anyList()); } - public void testApplyForScrollRequestWithResolvedIndices() { + public void testApplyForScrollRequestWithOriginalIndices() { SearchScrollRequest request = mock(SearchScrollRequest.class); - ActionFilterChain mockFilterChain = mock(TestActionFilterChain.class); + ActionFilterChain chain = mock(TestActionFilterChain.class); @SuppressWarnings("unchecked") ActionRequestMetadata metadata = mock(ActionRequestMetadata.class); - ResolvedIndices resolved = ResolvedIndices.of("logs-scroll-index"); - when(metadata.resolvedIndices()).thenReturn(resolved); + when(metadata.originalIndices()).thenReturn(new String[] { "logs-scroll-index" }); - try (ThreadContext.StoredContext context = threadPool.getThreadContext().stashContext()) { - when(ruleProcessingService.evaluateLabel(anyList())).thenReturn(Optional.of("ScrollQG_ID")); + try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { + doAnswer(inv -> { + @SuppressWarnings("unchecked") + List> extractors = inv.getArgument(0); + + assertNotNull(extractors); + assertEquals(1, extractors.size()); + + AttributeExtractor ex = extractors.get(0); + assertEquals(RuleAttribute.INDEX_PATTERN, ex.getAttribute()); + + List values = new ArrayList<>(); + ex.extract().forEach(values::add); + assertEquals(List.of("logs-scroll-index"), values); + + return Optional.of("ScrollQG_ID"); + }).when(ruleProcessingService).evaluateLabel(any()); - autoTaggingActionFilter.apply(mock(Task.class), "Test", request, metadata, null, mockFilterChain); + autoTaggingActionFilter.apply(mock(Task.class), "Test", request, metadata, null, chain); assertEquals("ScrollQG_ID", threadPool.getThreadContext().getHeader(WorkloadGroupTask.WORKLOAD_GROUP_ID_HEADER)); verify(ruleProcessingService, times(1)).evaluateLabel(anyList()); diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java index 3db9e93fdd092..25fb025eaaa4e 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java @@ -34,9 +34,7 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; -import org.opensearch.action.support.TransportIndicesResolvingAction; -import org.opensearch.cluster.metadata.OptionallyResolvedIndices; -import org.opensearch.cluster.metadata.ResolvedIndices; +import org.opensearch.action.support.TransportOriginalIndicesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.core.action.ActionListener; @@ -53,7 +51,7 @@ */ public class TransportSearchScrollAction extends HandledTransportAction implements - TransportIndicesResolvingAction { + TransportOriginalIndicesAction { private final ClusterService clusterService; private final SearchTransportService searchTransportService; @@ -121,24 +119,17 @@ protected void doExecute(Task task, SearchScrollRequest request, ActionListener< } @Override - public OptionallyResolvedIndices resolveIndices(SearchScrollRequest request) { + public String[] originalIndices(SearchScrollRequest request) { + final String scrollIdString = request.scrollId(); + if (scrollIdString == null || scrollIdString.isEmpty()) { + return null; + } try { - final String scrollIdString = request.scrollId(); - if (scrollIdString == null || scrollIdString.isEmpty()) { - return OptionallyResolvedIndices.unknown(); - } - - final ParsedScrollId parsed = request.parseScrollId(); - if (parsed == null) { - return OptionallyResolvedIndices.unknown(); - } - final String[] originalIndices = parsed.getOriginalIndices(); - if (originalIndices == null || originalIndices.length == 0) { - return OptionallyResolvedIndices.unknown(); - } - return ResolvedIndices.of(originalIndices); - } catch (Exception e) { - return OptionallyResolvedIndices.unknown(); + ParsedScrollId parsed = request.parseScrollId(); + final String[] originalIndices = (parsed == null) ? null : parsed.getOriginalIndices(); + return (originalIndices == null || originalIndices.length == 0) ? null : originalIndices.clone(); + } catch (IllegalArgumentException e) { + return null; } } } diff --git a/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java b/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java index 2f9be658a5aea..56a6ed81bc340 100644 --- a/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java +++ b/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java @@ -58,4 +58,13 @@ public OptionallyResolvedIndices resolvedIndices() { TransportIndicesResolvingAction indicesResolvingAction = (TransportIndicesResolvingAction) this.transportAction; return indicesResolvingAction.resolveIndices(request); } + + public String[] originalIndices() { + if (!(transportAction instanceof TransportOriginalIndicesAction)) { + return null; + } + @SuppressWarnings("unchecked") + TransportOriginalIndicesAction original = (TransportOriginalIndicesAction) this.transportAction; + return original.originalIndices(request); + } } diff --git a/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java b/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java new file mode 100644 index 0000000000000..545f9e05dd404 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java @@ -0,0 +1,31 @@ +/* + * 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.action.support; + +import org.opensearch.action.ActionRequest; + +/** + * Optional contract for transport actions that can expose the original index expressions + * as provided by the user (e.g., aliases, wildcards, concrete names, etc.). + *

+ * This is useful for internal follow-up requests (such as scroll) that no longer carry an + * {@link org.opensearch.action.IndicesRequest}, but still need to preserve the user-facing + * index expressions for downstream components (e.g., request metadata, filters). + */ +public interface TransportOriginalIndicesAction { + + /** + * Returns the original user-provided index expressions (aliases/wildcards/concrete), + * if they can be determined for this request. Otherwise, returns null/empty. + * + * Implementations should return the expressions exactly as supplied by the user (patterns/aliases), + * NOT the expanded concrete index names resolved during execution. + */ + String[] originalIndices(Request request); +} From 7c00443c32716e0452fe79a3f6c1ab922b57697e Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Thu, 22 Jan 2026 14:57:55 -0600 Subject: [PATCH 3/6] use writeStringArray/readStringArray Signed-off-by: Lamine Idjeraoui --- .../action/search/TransportSearchHelper.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java index 900b1223c6916..202b363e75eba 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java @@ -35,6 +35,7 @@ import org.opensearch.Version; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.common.Strings; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.common.io.stream.BytesStreamInput; import org.opensearch.search.SearchPhaseResult; @@ -60,7 +61,7 @@ static InternalScrollSearchRequest internalScrollSearchRequest(ShardSearchContex return new InternalScrollSearchRequest(request, id); } - public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_3_2; + public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_3_0; static String buildScrollId(AtomicArray searchPhaseResults, Version version) { return buildScrollId(searchPhaseResults, null, version); @@ -90,12 +91,7 @@ static String buildScrollId(AtomicArray searchPhase // and subsequent SearchScrollRequests, we store exactly the same // index targets that were visible to the indices attribute during // the "search" phase - if (originalIndices != null && originalIndices.length > 0) { - out.writeVInt(originalIndices.length); - for (String index : originalIndices) { - out.writeString(index); - } - } + out.writeStringArray(originalIndices == null ? Strings.EMPTY_ARRAY : originalIndices); } byte[] bytes = BytesReference.toBytes(out.bytes()); return Base64.getUrlEncoder().encodeToString(bytes); @@ -134,16 +130,7 @@ static ParsedScrollId parseScrollId(String scrollId) { context[i] = new SearchContextIdForNode(clusterAlias, target, new ShardSearchContextId(contextUUID, id)); } - String[] originalIndices; - if (in.getPosition() < bytes.length) { - final int numOriginalIndices = in.readVInt(); - originalIndices = new String[numOriginalIndices]; - for (int i = 0; i < numOriginalIndices; i++) { - originalIndices[i] = in.readString(); - } - } else { - originalIndices = new String[0]; - } + final String[] originalIndices = in.getPosition() < bytes.length ? in.readStringArray() : Strings.EMPTY_ARRAY; if (in.getPosition() != bytes.length) { throw new IllegalArgumentException("Not all bytes were read"); From ce35d03f6d475bf55bf5e8fd4599668b6aab85fe Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Sun, 1 Feb 2026 13:20:58 -0600 Subject: [PATCH 4/6] uee writeOptionalStringArray remove TransportOriginalIndicesAction Signed-off-by: Lamine Idjeraoui --- .../plugin/wlm/WlmAutoTaggingIT.java | 29 ++++++++++++----- .../plugin/wlm/AutoTaggingActionFilter.java | 6 ++-- .../wlm/AutoTaggingActionFilterTests.java | 2 +- .../action/search/SearchScrollRequest.java | 11 +++++++ .../action/search/TransportSearchHelper.java | 13 ++++---- .../search/TransportSearchScrollAction.java | 20 +----------- .../action/support/ActionRequestMetadata.java | 9 ------ .../TransportOriginalIndicesAction.java | 31 ------------------- 8 files changed, 44 insertions(+), 77 deletions(-) delete mode 100644 server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java diff --git a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java index 835eb2b27664a..c1cb73b07dfe6 100644 --- a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java +++ b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java @@ -462,17 +462,32 @@ public void testScrollRequestsAreAlsoTagged() throws Exception { .setScroll(TimeValue.timeValueMinutes(1)) .setSize(1) .get(); - String scrollId = initial.getScrollId(); assertNotNull("scrollId must not be null", scrollId); - int afterInitialSearch = getCompletions(workloadGroupId); - assertTrue("Expected completions to increase after initial search with scroll", afterInitialSearch > completionsBefore); - - client().prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1)).get(); + try { + int afterInitialSearch = getCompletions(workloadGroupId); + assertTrue( + "Expected completions to increase after initial search with scroll", + afterInitialSearch > completionsBefore + ); + + SearchResponse scrollResp = client().prepareSearchScroll(scrollId) + .setScroll(TimeValue.timeValueMinutes(1)) + .get(); + String nextScrollId = scrollResp.getScrollId(); + if (nextScrollId != null && !nextScrollId.isEmpty()) { + scrollId = nextScrollId; + } - int afterScroll = getCompletions(workloadGroupId); - assertTrue("Expected completions to increase after scroll request as well", afterScroll > afterInitialSearch); + int afterScroll = getCompletions(workloadGroupId); + assertTrue( + "Expected completions to increase after scroll request as well", + afterScroll > afterInitialSearch + ); + } finally { + clearScroll(scrollId); + } }); } diff --git a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java index 5ecc54db49780..bea19e17073ec 100644 --- a/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java +++ b/plugins/workload-management/src/main/java/org/opensearch/plugin/wlm/AutoTaggingActionFilter.java @@ -95,9 +95,9 @@ public void app if (isSearchRequest) { attributeExtractors.add(new IndicesExtractor((IndicesRequest) request)); } else { - // Scroll: recover the original user-provided expressions from metadata - final String[] originalIndices = actionRequestMetadata.originalIndices(); - if (originalIndices != null && originalIndices.length > 0) { + // Scroll: recover the original user-provided indices from ParsedScrollId + final String[] originalIndices = ((SearchScrollRequest) request).originalIndicesOrEmpty(); + if (originalIndices.length > 0) { attributeExtractors.add(new AttributeExtractor<>() { @Override public Attribute getAttribute() { diff --git a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java index 52832dc69b8c2..71b38f8635f70 100644 --- a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java +++ b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java @@ -106,7 +106,7 @@ public void testApplyForScrollRequestWithOriginalIndices() { @SuppressWarnings("unchecked") ActionRequestMetadata metadata = mock(ActionRequestMetadata.class); - when(metadata.originalIndices()).thenReturn(new String[] { "logs-scroll-index" }); + when(request.originalIndicesOrEmpty()).thenReturn(new String[] { "logs-scroll-index" }); try (ThreadContext.StoredContext ctx = threadPool.getThreadContext().stashContext()) { doAnswer(inv -> { diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java index a25a3ff719c52..991c29508bd60 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java @@ -36,6 +36,7 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.tasks.TaskId; @@ -110,6 +111,16 @@ public ParsedScrollId parseScrollId() { return parsedScrollId; } + public String[] originalIndicesOrEmpty() { + try { + ParsedScrollId parsed = parseScrollId(); + String[] orig = parsed == null ? null : parsed.getOriginalIndices(); + return orig == null || orig.length == 0 ? Strings.EMPTY_ARRAY : orig; + } catch (IllegalArgumentException e) { + return Strings.EMPTY_ARRAY; + } + } + /** * If set, will enable scrolling of the search request. */ diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java index 202b363e75eba..0d521413d6e63 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java @@ -57,12 +57,12 @@ final class TransportSearchHelper { private static final String INCLUDE_CONTEXT_UUID = "include_context_uuid"; + public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_4_0; + static InternalScrollSearchRequest internalScrollSearchRequest(ShardSearchContextId id, SearchScrollRequest request) { return new InternalScrollSearchRequest(request, id); } - public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_3_0; - static String buildScrollId(AtomicArray searchPhaseResults, Version version) { return buildScrollId(searchPhaseResults, null, version); } @@ -88,10 +88,9 @@ static String buildScrollId(AtomicArray searchPhase if (version.onOrAfter(INDICES_IN_SCROLL_ID_VERSION)) { // To keep autotagging consistent between the initial SearchRequest - // and subsequent SearchScrollRequests, we store exactly the same - // index targets that were visible to the indices attribute during - // the "search" phase - out.writeStringArray(originalIndices == null ? Strings.EMPTY_ARRAY : originalIndices); + // and subsequent SearchScrollRequests, we store exactly the original indices + // received during the "search" phase + out.writeOptionalStringArray(originalIndices); } byte[] bytes = BytesReference.toBytes(out.bytes()); return Base64.getUrlEncoder().encodeToString(bytes); @@ -130,7 +129,7 @@ static ParsedScrollId parseScrollId(String scrollId) { context[i] = new SearchContextIdForNode(clusterAlias, target, new ShardSearchContextId(contextUUID, id)); } - final String[] originalIndices = in.getPosition() < bytes.length ? in.readStringArray() : Strings.EMPTY_ARRAY; + final String[] originalIndices = in.getPosition() < bytes.length ? in.readOptionalStringArray() : Strings.EMPTY_ARRAY; if (in.getPosition() != bytes.length) { throw new IllegalArgumentException("Not all bytes were read"); diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java index 25fb025eaaa4e..c6383acb3d767 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java @@ -34,7 +34,6 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; -import org.opensearch.action.support.TransportOriginalIndicesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.core.action.ActionListener; @@ -49,9 +48,7 @@ * * @opensearch.internal */ -public class TransportSearchScrollAction extends HandledTransportAction - implements - TransportOriginalIndicesAction { +public class TransportSearchScrollAction extends HandledTransportAction { private final ClusterService clusterService; private final SearchTransportService searchTransportService; @@ -117,19 +114,4 @@ protected void doExecute(Task task, SearchScrollRequest request, ActionListener< listener.onFailure(e); } } - - @Override - public String[] originalIndices(SearchScrollRequest request) { - final String scrollIdString = request.scrollId(); - if (scrollIdString == null || scrollIdString.isEmpty()) { - return null; - } - try { - ParsedScrollId parsed = request.parseScrollId(); - final String[] originalIndices = (parsed == null) ? null : parsed.getOriginalIndices(); - return (originalIndices == null || originalIndices.length == 0) ? null : originalIndices.clone(); - } catch (IllegalArgumentException e) { - return null; - } - } } diff --git a/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java b/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java index 56a6ed81bc340..2f9be658a5aea 100644 --- a/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java +++ b/server/src/main/java/org/opensearch/action/support/ActionRequestMetadata.java @@ -58,13 +58,4 @@ public OptionallyResolvedIndices resolvedIndices() { TransportIndicesResolvingAction indicesResolvingAction = (TransportIndicesResolvingAction) this.transportAction; return indicesResolvingAction.resolveIndices(request); } - - public String[] originalIndices() { - if (!(transportAction instanceof TransportOriginalIndicesAction)) { - return null; - } - @SuppressWarnings("unchecked") - TransportOriginalIndicesAction original = (TransportOriginalIndicesAction) this.transportAction; - return original.originalIndices(request); - } } diff --git a/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java b/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java deleted file mode 100644 index 545f9e05dd404..0000000000000 --- a/server/src/main/java/org/opensearch/action/support/TransportOriginalIndicesAction.java +++ /dev/null @@ -1,31 +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.action.support; - -import org.opensearch.action.ActionRequest; - -/** - * Optional contract for transport actions that can expose the original index expressions - * as provided by the user (e.g., aliases, wildcards, concrete names, etc.). - *

- * This is useful for internal follow-up requests (such as scroll) that no longer carry an - * {@link org.opensearch.action.IndicesRequest}, but still need to preserve the user-facing - * index expressions for downstream components (e.g., request metadata, filters). - */ -public interface TransportOriginalIndicesAction { - - /** - * Returns the original user-provided index expressions (aliases/wildcards/concrete), - * if they can be determined for this request. Otherwise, returns null/empty. - * - * Implementations should return the expressions exactly as supplied by the user (patterns/aliases), - * NOT the expanded concrete index names resolved during execution. - */ - String[] originalIndices(Request request); -} From 71f2255066d4874a81fafd905435e49e8fa12116 Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Mon, 2 Feb 2026 21:04:48 -0600 Subject: [PATCH 5/6] spotless Signed-off-by: Lamine Idjeraoui --- .../opensearch/plugin/wlm/WlmAutoTaggingIT.java | 16 ++++------------ .../plugin/wlm/AutoTaggingActionFilterTests.java | 6 +++--- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java index c1cb73b07dfe6..ae3c8a1acf1d6 100644 --- a/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java +++ b/plugins/workload-management/src/internalClusterTest/java/org/opensearch/plugin/wlm/WlmAutoTaggingIT.java @@ -467,24 +467,16 @@ public void testScrollRequestsAreAlsoTagged() throws Exception { try { int afterInitialSearch = getCompletions(workloadGroupId); - assertTrue( - "Expected completions to increase after initial search with scroll", - afterInitialSearch > completionsBefore - ); - - SearchResponse scrollResp = client().prepareSearchScroll(scrollId) - .setScroll(TimeValue.timeValueMinutes(1)) - .get(); + assertTrue("Expected completions to increase after initial search with scroll", afterInitialSearch > completionsBefore); + + SearchResponse scrollResp = client().prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1)).get(); String nextScrollId = scrollResp.getScrollId(); if (nextScrollId != null && !nextScrollId.isEmpty()) { scrollId = nextScrollId; } int afterScroll = getCompletions(workloadGroupId); - assertTrue( - "Expected completions to increase after scroll request as well", - afterScroll > afterInitialSearch - ); + assertTrue("Expected completions to increase after scroll request as well", afterScroll > afterInitialSearch); } finally { clearScroll(scrollId); } diff --git a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java index 71b38f8635f70..40995d70c0848 100644 --- a/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java +++ b/plugins/workload-management/src/test/java/org/opensearch/plugin/wlm/AutoTaggingActionFilterTests.java @@ -39,13 +39,13 @@ import java.util.Optional; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.when; public class AutoTaggingActionFilterTests extends OpenSearchTestCase { From a4a387a42d3e70c69190c3e4fe7116e354a9f260 Mon Sep 17 00:00:00 2001 From: Lamine Idjeraoui Date: Sun, 8 Feb 2026 18:19:07 -0600 Subject: [PATCH 6/6] update version to V_3_6_0 update change log Signed-off-by: Lamine Idjeraoui --- CHANGELOG.md | 1 + .../org/opensearch/action/search/TransportSearchHelper.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e801a89c0fa..58630dda401bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Add getWrappedScorer method to ProfileScorer for plugin access to wrapped scorers ([#20548](https://github.com/opensearch-project/OpenSearch/issues/20548)) - Support expected cluster name with validation in CCS Sniff mode ([#20532](https://github.com/opensearch-project/OpenSearch/pull/20532)) - Add security policy to allow `accessUnixDomainSocket` in `transport-grpc` module ([#20463](https://github.com/opensearch-project/OpenSearch/pull/20463)) +- [Workload Management] Enhance Scroll API support for autotagging ([#20151](https://github.com/opensearch-project/OpenSearch/pull/20151)) ### Changed - Move Randomness from server to libs/common ([#20570](https://github.com/opensearch-project/OpenSearch/pull/20570)) diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java index 0d521413d6e63..779a0ac0c6590 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java @@ -57,7 +57,7 @@ final class TransportSearchHelper { private static final String INCLUDE_CONTEXT_UUID = "include_context_uuid"; - public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_4_0; + public static final Version INDICES_IN_SCROLL_ID_VERSION = Version.V_3_6_0; static InternalScrollSearchRequest internalScrollSearchRequest(ShardSearchContextId id, SearchScrollRequest request) { return new InternalScrollSearchRequest(request, id);