From 7563e71e90f55f385a8cfdc1b3880d7e5e7e9ba1 Mon Sep 17 00:00:00 2001
From: David Turner
Date: Thu, 23 Jan 2025 09:14:16 +0000
Subject: [PATCH 01/29] Release ref on cancellation in
`GetSnapshotInfoExecutor` (#120529)
Not a bug today because we also throttle calls to `getSnapshotInfo` so
we never run out of refs before processing all the cancelled tasks, but
still we should release `ref` on every path through this method.
---
.../cluster/snapshots/get/TransportGetSnapshotsAction.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java
index b5eeb2b0f7f2..d9fef7e0af8a 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java
@@ -721,7 +721,9 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction listener) {
enqueueTask(listener.delegateFailure((l, ref) -> {
if (isCancelledSupplier.getAsBoolean()) {
- l.onFailure(new TaskCancelledException("task cancelled"));
+ try (ref) {
+ l.onFailure(new TaskCancelledException("task cancelled"));
+ }
} else {
repository.getSnapshotInfo(snapshotId, ActionListener.releaseAfter(l, ref));
}
From a1fd7bc374858a0ce60bbc8bef1d271f18afdbda Mon Sep 17 00:00:00 2001
From: David Turner
Date: Thu, 23 Jan 2025 09:35:45 +0000
Subject: [PATCH 02/29] Fix trappy timeouts in persistent tasks requests
(#120514)
Ensure that callers constructing these master-node requests pass in an
explicit timeout.
Relates #107984
---
.../PersistentTaskCreationFailureIT.java | 2 +-
...PersistentTaskInitializationFailureIT.java | 2 +-
.../PersistentTasksExecutorFullRestartIT.java | 2 +-
.../persistent/PersistentTasksExecutorIT.java | 87 ++++++++++++++++---
.../decider/EnableAssignmentDeciderIT.java | 2 +-
.../TransportPostFeatureUpgradeAction.java | 3 +-
.../selection/HealthNodeTaskExecutor.java | 3 +-
.../persistent/AllocatedPersistentTask.java | 10 ++-
.../CompletionPersistentTaskAction.java | 5 +-
.../PersistentTasksNodeService.java | 5 +-
.../persistent/PersistentTasksService.java | 73 +++++++---------
.../RemovePersistentTaskAction.java | 5 +-
.../persistent/StartPersistentTaskAction.java | 5 +-
.../UpdatePersistentTaskStatusAction.java | 5 +-
.../HealthNodeTaskExecutorTests.java | 5 +-
.../CancelPersistentTaskRequestTests.java | 2 +-
.../CompletionPersistentTaskRequestTests.java | 4 +-
.../PersistentTasksNodeServiceTests.java | 14 +--
.../StartPersistentActionRequestTests.java | 2 +-
.../UpdatePersistentTaskRequestTests.java | 2 +-
.../ccr/action/ShardFollowTaskCleaner.java | 2 +
.../downsample/TransportDownsampleAction.java | 2 +-
.../ReindexDataStreamTransportAction.java | 3 +-
.../integration/MlDistributedFailureIT.java | 1 +
.../action/TransportPutRollupJobAction.java | 2 +-
.../action/PutJobStateMachineTests.java | 9 +-
.../xpack/security/Security.java | 3 +-
.../xpack/shutdown/NodeShutdownTasksIT.java | 18 ++--
.../TransportStopTransformActionTests.java | 18 +++-
.../transforms/TransformTaskTests.java | 3 +-
30 files changed, 188 insertions(+), 111 deletions(-)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskCreationFailureIT.java b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskCreationFailureIT.java
index 8a4d1ceda784..6452968f2467 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskCreationFailureIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskCreationFailureIT.java
@@ -113,7 +113,7 @@ public class PersistentTaskCreationFailureIT extends ESIntegTestCase {
UUIDs.base64UUID(),
FailingCreationPersistentTaskExecutor.TASK_NAME,
new FailingCreationTaskParams(),
- null,
+ TEST_REQUEST_TIMEOUT,
l.map(ignored -> null)
)
);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskInitializationFailureIT.java b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskInitializationFailureIT.java
index 2f0896925859..6e739b12f406 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskInitializationFailureIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTaskInitializationFailureIT.java
@@ -50,7 +50,7 @@ public class PersistentTaskInitializationFailureIT extends ESIntegTestCase {
UUIDs.base64UUID(),
FailingInitializationPersistentTaskExecutor.TASK_NAME,
new FailingInitializationTaskParams(),
- null,
+ TEST_REQUEST_TIMEOUT,
startPersistentTaskFuture
);
startPersistentTaskFuture.actionGet();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorFullRestartIT.java b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorFullRestartIT.java
index b710a05ec378..684f6b71d0ef 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorFullRestartIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorFullRestartIT.java
@@ -45,7 +45,7 @@ public class PersistentTasksExecutorFullRestartIT extends ESIntegTestCase {
PlainActionFuture> future = new PlainActionFuture<>();
futures.add(future);
taskIds[i] = UUIDs.base64UUID();
- service.sendStartRequest(taskIds[i], TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ service.sendStartRequest(taskIds[i], TestPersistentTasksExecutor.NAME, new TestParams("Blah"), TEST_REQUEST_TIMEOUT, future);
}
for (int i = 0; i < numberOfTasks; i++) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorIT.java
index 203ad831bd2e..8af2111afd5b 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/persistent/PersistentTasksExecutorIT.java
@@ -69,7 +69,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
public void testPersistentActionFailure() throws Exception {
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
long allocationId = future.get().getAllocationId();
waitForTaskToStart();
TaskInfo firstRunningTask = clusterAdmin().prepareListTasks()
@@ -100,7 +106,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
String taskId = UUIDs.base64UUID();
- persistentTasksService.sendStartRequest(taskId, TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ persistentTasksService.sendStartRequest(
+ taskId,
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
long allocationId = future.get().getAllocationId();
waitForTaskToStart();
TaskInfo firstRunningTask = clusterAdmin().prepareListTasks()
@@ -119,7 +131,14 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
logger.info("Simulating errant completion notification");
// try sending completion request with incorrect allocation id
PlainActionFuture> failedCompletionNotificationFuture = new PlainActionFuture<>();
- persistentTasksService.sendCompletionRequest(taskId, Long.MAX_VALUE, null, null, null, failedCompletionNotificationFuture);
+ persistentTasksService.sendCompletionRequest(
+ taskId,
+ Long.MAX_VALUE,
+ null,
+ null,
+ TEST_REQUEST_TIMEOUT,
+ failedCompletionNotificationFuture
+ );
assertFutureThrows(failedCompletionNotificationFuture, ResourceNotFoundException.class);
// Make sure that the task is still running
assertThat(
@@ -141,7 +160,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
PlainActionFuture> future = new PlainActionFuture<>();
TestParams testParams = new TestParams("Blah");
testParams.setExecutorNodeAttr("test");
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, testParams, null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ testParams,
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
String taskId = future.get().getId();
Settings nodeSettings = Settings.builder().put(nodeSettings(0, Settings.EMPTY)).put("node.attr.test_attr", "test").build();
@@ -165,7 +190,7 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
// Remove the persistent task
PlainActionFuture> removeFuture = new PlainActionFuture<>();
- persistentTasksService.sendRemoveRequest(taskId, null, removeFuture);
+ persistentTasksService.sendRemoveRequest(taskId, TEST_REQUEST_TIMEOUT, removeFuture);
assertEquals(removeFuture.get().getId(), taskId);
}
@@ -182,7 +207,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
TestParams testParams = new TestParams("Blah");
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, testParams, null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ testParams,
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
String taskId = future.get().getId();
assertThat(clusterAdmin().prepareListTasks().setActions(TestPersistentTasksExecutor.NAME + "[c]").get().getTasks(), empty());
@@ -197,14 +228,20 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
// Remove the persistent task
PlainActionFuture> removeFuture = new PlainActionFuture<>();
- persistentTasksService.sendRemoveRequest(taskId, null, removeFuture);
+ persistentTasksService.sendRemoveRequest(taskId, TEST_REQUEST_TIMEOUT, removeFuture);
assertEquals(removeFuture.get().getId(), taskId);
}
public void testPersistentActionStatusUpdate() throws Exception {
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
String taskId = future.get().getId();
waitForTaskToStart();
TaskInfo firstRunningTask = clusterAdmin().prepareListTasks()
@@ -250,7 +287,7 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
assertFutureThrows(future1, IllegalStateException.class, "timed out after 10ms");
PlainActionFuture> failedUpdateFuture = new PlainActionFuture<>();
- persistentTasksService.sendUpdateStateRequest(taskId, -2, new State("should fail"), null, failedUpdateFuture);
+ persistentTasksService.sendUpdateStateRequest(taskId, -2, new State("should fail"), TEST_REQUEST_TIMEOUT, failedUpdateFuture);
assertFutureThrows(
failedUpdateFuture,
ResourceNotFoundException.class,
@@ -275,11 +312,23 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
String taskId = UUIDs.base64UUID();
- persistentTasksService.sendStartRequest(taskId, TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ persistentTasksService.sendStartRequest(
+ taskId,
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
future.get();
PlainActionFuture> future2 = new PlainActionFuture<>();
- persistentTasksService.sendStartRequest(taskId, TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future2);
+ persistentTasksService.sendStartRequest(
+ taskId,
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future2
+ );
assertFutureThrows(future2, ResourceAlreadyExistsException.class);
waitForTaskToStart();
@@ -315,7 +364,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
PlainActionFuture> future = new PlainActionFuture<>();
TestParams testParams = new TestParams("Blah");
testParams.setExecutorNodeAttr("test");
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, testParams, null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ testParams,
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
PersistentTask task = future.get();
String taskId = task.getId();
@@ -366,7 +421,13 @@ public class PersistentTasksExecutorIT extends ESIntegTestCase {
persistentTasksClusterService.setRecheckInterval(TimeValue.timeValueMillis(1));
PersistentTasksService persistentTasksService = internalCluster().getInstance(PersistentTasksService.class);
PlainActionFuture> future = new PlainActionFuture<>();
- persistentTasksService.sendStartRequest(UUIDs.base64UUID(), TestPersistentTasksExecutor.NAME, new TestParams("Blah"), null, future);
+ persistentTasksService.sendStartRequest(
+ UUIDs.base64UUID(),
+ TestPersistentTasksExecutor.NAME,
+ new TestParams("Blah"),
+ TEST_REQUEST_TIMEOUT,
+ future
+ );
String taskId = future.get().getId();
long allocationId = future.get().getAllocationId();
waitForTaskToStart();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/persistent/decider/EnableAssignmentDeciderIT.java b/server/src/internalClusterTest/java/org/elasticsearch/persistent/decider/EnableAssignmentDeciderIT.java
index 7038dca99249..b75156763c9e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/persistent/decider/EnableAssignmentDeciderIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/persistent/decider/EnableAssignmentDeciderIT.java
@@ -52,7 +52,7 @@ public class EnableAssignmentDeciderIT extends ESIntegTestCase {
"task_" + i,
TestPersistentTasksExecutor.NAME,
new TestParams(randomAlphaOfLength(10)),
- null,
+ TEST_REQUEST_TIMEOUT,
ActionListener.running(latch::countDown)
);
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java
index 57ebe8ef626f..ecf7bab6a21c 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportPostFeatureUpgradeAction.java
@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.util.concurrent.EsExecutors;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.indices.SystemIndices;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.persistent.PersistentTasksService;
@@ -95,7 +96,7 @@ public class TransportPostFeatureUpgradeAction extends TransportMasterNodeAction
SYSTEM_INDEX_UPGRADE_TASK_NAME,
SYSTEM_INDEX_UPGRADE_TASK_NAME,
new SystemIndexMigrationTaskParams(),
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be configurable? longer by default? infinite? */,
ActionListener.wrap(startedTask -> {
listener.onResponse(new PostFeatureUpgradeResponse(true, featuresToMigrate, null, null));
}, ex -> {
diff --git a/server/src/main/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutor.java b/server/src/main/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutor.java
index 5991bc248ba7..7c37a0ce5d92 100644
--- a/server/src/main/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutor.java
+++ b/server/src/main/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutor.java
@@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.features.FeatureService;
import org.elasticsearch.node.NodeClosedException;
import org.elasticsearch.persistent.AllocatedPersistentTask;
@@ -162,7 +163,7 @@ public final class HealthNodeTaskExecutor extends PersistentTasksExecutor logger.debug("Created the health node task"), e -> {
if (e instanceof NodeClosedException) {
logger.debug("Failed to create health node task because node is shutting down", e);
diff --git a/server/src/main/java/org/elasticsearch/persistent/AllocatedPersistentTask.java b/server/src/main/java/org/elasticsearch/persistent/AllocatedPersistentTask.java
index 7f00562758a8..cda73d4fa0bc 100644
--- a/server/src/main/java/org/elasticsearch/persistent/AllocatedPersistentTask.java
+++ b/server/src/main/java/org/elasticsearch/persistent/AllocatedPersistentTask.java
@@ -65,7 +65,13 @@ public class AllocatedPersistentTask extends CancellableTask {
final PersistentTaskState state,
final ActionListener> listener
) {
- persistentTasksService.sendUpdateStateRequest(persistentTaskId, allocationId, state, null, listener);
+ persistentTasksService.sendUpdateStateRequest(
+ persistentTaskId,
+ allocationId,
+ state,
+ TimeValue.THIRTY_SECONDS /* TODO should this be longer? infinite? */,
+ listener
+ );
}
public String getPersistentTaskId() {
@@ -201,7 +207,7 @@ public class AllocatedPersistentTask extends CancellableTask {
getAllocationId(),
failure,
localAbortReason,
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be longer? infinite? */,
new ActionListener<>() {
@Override
public void onResponse(PersistentTasksCustomMetadata.PersistentTask> persistentTask) {
diff --git a/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java
index 74761144742c..be37af165b42 100644
--- a/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java
+++ b/server/src/main/java/org/elasticsearch/persistent/CompletionPersistentTaskAction.java
@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
@@ -56,8 +57,8 @@ public class CompletionPersistentTaskAction {
localAbortReason = in.readOptionalString();
}
- public Request(String taskId, long allocationId, Exception exception, String localAbortReason) {
- super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
+ public Request(TimeValue masterNodeTimeout, String taskId, long allocationId, Exception exception, String localAbortReason) {
+ super(masterNodeTimeout);
this.taskId = taskId;
this.exception = exception;
this.allocationId = allocationId;
diff --git a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksNodeService.java b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksNodeService.java
index ff6a0b901870..16fdc82074e3 100644
--- a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksNodeService.java
+++ b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksNodeService.java
@@ -18,6 +18,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.concurrent.EsExecutors;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.persistent.PersistentTasksCustomMetadata.PersistentTask;
import org.elasticsearch.tasks.Task;
@@ -310,7 +311,7 @@ public class PersistentTasksNodeService implements ClusterStateListener {
taskInProgress.getAllocationId(),
originalException,
null,
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be longer? infinite? */,
new ActionListener<>() {
@Override
public void onResponse(PersistentTask> persistentTask) {
@@ -346,7 +347,7 @@ public class PersistentTasksNodeService implements ClusterStateListener {
if (task.markAsCancelled()) {
// Cancel the local task using the task manager
String reason = "task has been removed, cancelling locally";
- persistentTasksService.sendCancelRequest(task.getId(), reason, null, new ActionListener<>() {
+ persistentTasksService.sendCancelRequest(task.getId(), reason, new ActionListener<>() {
@Override
public void onResponse(ListTasksResponse cancelTasksResponse) {
logger.trace(
diff --git a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksService.java b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksService.java
index 4e828a1280b1..b540a9160241 100644
--- a/server/src/main/java/org/elasticsearch/persistent/PersistentTasksService.java
+++ b/server/src/main/java/org/elasticsearch/persistent/PersistentTasksService.java
@@ -27,6 +27,7 @@ import org.elasticsearch.persistent.PersistentTasksCustomMetadata.PersistentTask
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.threadpool.ThreadPool;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -57,16 +58,16 @@ public class PersistentTasksService {
final String taskId,
final String taskName,
final Params taskParams,
- final @Nullable TimeValue timeout,
+ final TimeValue timeout,
final ActionListener> listener
) {
@SuppressWarnings("unchecked")
final ActionListener> wrappedListener = listener.map(t -> (PersistentTask) t);
- StartPersistentTaskAction.Request request = new StartPersistentTaskAction.Request(taskId, taskName, taskParams);
- if (timeout != null) {
- request.masterNodeTimeout(timeout);
- }
- execute(request, StartPersistentTaskAction.INSTANCE, wrappedListener);
+ execute(
+ new StartPersistentTaskAction.Request(Objects.requireNonNull(timeout), taskId, taskName, taskParams),
+ StartPersistentTaskAction.INSTANCE,
+ wrappedListener
+ );
}
/**
@@ -85,33 +86,27 @@ public class PersistentTasksService {
final @Nullable TimeValue timeout,
final ActionListener> listener
) {
- CompletionPersistentTaskAction.Request request = new CompletionPersistentTaskAction.Request(
- taskId,
- taskAllocationId,
- taskFailure,
- localAbortReason
+ execute(
+ new CompletionPersistentTaskAction.Request(
+ Objects.requireNonNull(timeout),
+ taskId,
+ taskAllocationId,
+ taskFailure,
+ localAbortReason
+ ),
+ CompletionPersistentTaskAction.INSTANCE,
+ listener
);
- if (timeout != null) {
- request.masterNodeTimeout(timeout);
- }
- execute(request, CompletionPersistentTaskAction.INSTANCE, listener);
}
/**
* Cancels a locally running task using the Task Manager API. Accepts operation timeout as optional parameter
*/
- void sendCancelRequest(
- final long taskId,
- final String reason,
- final @Nullable TimeValue timeout,
- final ActionListener listener
- ) {
+ void sendCancelRequest(final long taskId, final String reason, final ActionListener listener) {
CancelTasksRequest request = new CancelTasksRequest();
request.setTargetTaskId(new TaskId(clusterService.localNode().getId(), taskId));
request.setReason(reason);
- if (timeout != null) {
- request.setTimeout(timeout);
- }
+ // TODO set timeout?
try {
client.admin().cluster().cancelTasks(request, listener);
} catch (Exception e) {
@@ -130,33 +125,25 @@ public class PersistentTasksService {
final String taskId,
final long taskAllocationID,
final PersistentTaskState taskState,
- final @Nullable TimeValue timeout,
+ final TimeValue timeout,
final ActionListener> listener
) {
- UpdatePersistentTaskStatusAction.Request request = new UpdatePersistentTaskStatusAction.Request(
- taskId,
- taskAllocationID,
- taskState
+ execute(
+ new UpdatePersistentTaskStatusAction.Request(Objects.requireNonNull(timeout), taskId, taskAllocationID, taskState),
+ UpdatePersistentTaskStatusAction.INSTANCE,
+ listener
);
- if (timeout != null) {
- request.masterNodeTimeout(timeout);
- }
- execute(request, UpdatePersistentTaskStatusAction.INSTANCE, listener);
}
/**
* Notifies the master node to remove a persistent task from the cluster state. Accepts operation timeout as optional parameter
*/
- public void sendRemoveRequest(
- final String taskId,
- final @Nullable TimeValue timeout,
- final ActionListener> listener
- ) {
- RemovePersistentTaskAction.Request request = new RemovePersistentTaskAction.Request(taskId);
- if (timeout != null) {
- request.masterNodeTimeout(timeout);
- }
- execute(request, RemovePersistentTaskAction.INSTANCE, listener);
+ public void sendRemoveRequest(final String taskId, final TimeValue timeout, final ActionListener> listener) {
+ execute(
+ new RemovePersistentTaskAction.Request(Objects.requireNonNull(timeout), taskId),
+ RemovePersistentTaskAction.INSTANCE,
+ listener
+ );
}
/**
diff --git a/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java
index 87d712ececd2..5fa18a070b16 100644
--- a/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java
+++ b/server/src/main/java/org/elasticsearch/persistent/RemovePersistentTaskAction.java
@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
@@ -44,8 +45,8 @@ public class RemovePersistentTaskAction {
taskId = in.readString();
}
- public Request(String taskId) {
- super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
+ public Request(TimeValue masterNodeTimeout, String taskId) {
+ super(masterNodeTimeout);
this.taskId = taskId;
}
diff --git a/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java b/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java
index d79f271bbc32..91c2d41a4a80 100644
--- a/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java
+++ b/server/src/main/java/org/elasticsearch/persistent/StartPersistentTaskAction.java
@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
@@ -52,8 +53,8 @@ public class StartPersistentTaskAction {
params = in.readNamedWriteable(PersistentTaskParams.class);
}
- public Request(String taskId, String taskName, PersistentTaskParams params) {
- super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
+ public Request(TimeValue masterNodeTimeout, String taskId, String taskName, PersistentTaskParams params) {
+ super(masterNodeTimeout);
this.taskId = taskId;
this.taskName = taskName;
this.params = params;
diff --git a/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java b/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java
index 346628f6224a..b3692ecfdd55 100644
--- a/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java
+++ b/server/src/main/java/org/elasticsearch/persistent/UpdatePersistentTaskStatusAction.java
@@ -21,6 +21,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
@@ -49,8 +50,8 @@ public class UpdatePersistentTaskStatusAction {
state = in.readOptionalNamedWriteable(PersistentTaskState.class);
}
- public Request(String taskId, long allocationId, PersistentTaskState state) {
- super(TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT);
+ public Request(TimeValue masterNodeTimeout, String taskId, long allocationId, PersistentTaskState state) {
+ super(masterNodeTimeout);
this.taskId = taskId;
this.allocationId = allocationId;
this.state = state;
diff --git a/server/src/test/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutorTests.java b/server/src/test/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutorTests.java
index 3069589f9556..aee02fb288b5 100644
--- a/server/src/test/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutorTests.java
+++ b/server/src/test/java/org/elasticsearch/health/node/selection/HealthNodeTaskExecutorTests.java
@@ -42,6 +42,7 @@ import static org.elasticsearch.test.ClusterServiceUtils.createClusterService;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -102,7 +103,7 @@ public class HealthNodeTaskExecutorTests extends ESTestCase {
eq("health-node"),
eq("health-node"),
eq(new HealthNodeTaskParams()),
- eq(null),
+ isNotNull(),
any()
)
);
@@ -121,7 +122,7 @@ public class HealthNodeTaskExecutorTests extends ESTestCase {
eq("health-node"),
eq("health-node"),
eq(new HealthNodeTaskParams()),
- eq(null),
+ isNotNull(),
any()
);
}
diff --git a/server/src/test/java/org/elasticsearch/persistent/CancelPersistentTaskRequestTests.java b/server/src/test/java/org/elasticsearch/persistent/CancelPersistentTaskRequestTests.java
index 11a4810f113f..c21a2ccd66ef 100644
--- a/server/src/test/java/org/elasticsearch/persistent/CancelPersistentTaskRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/persistent/CancelPersistentTaskRequestTests.java
@@ -18,7 +18,7 @@ public class CancelPersistentTaskRequestTests extends AbstractWireSerializingTes
@Override
protected Request createTestInstance() {
- return new Request(randomAsciiOfLength(10));
+ return new Request(randomTimeValue(), randomAsciiOfLength(10));
}
@Override
diff --git a/server/src/test/java/org/elasticsearch/persistent/CompletionPersistentTaskRequestTests.java b/server/src/test/java/org/elasticsearch/persistent/CompletionPersistentTaskRequestTests.java
index b57f401ed56f..cb61d3b3862d 100644
--- a/server/src/test/java/org/elasticsearch/persistent/CompletionPersistentTaskRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/persistent/CompletionPersistentTaskRequestTests.java
@@ -17,9 +17,9 @@ public class CompletionPersistentTaskRequestTests extends AbstractWireSerializin
@Override
protected Request createTestInstance() {
if (randomBoolean()) {
- return new Request(randomAlphaOfLength(10), randomNonNegativeLong(), null, null);
+ return new Request(randomTimeValue(), randomAlphaOfLength(10), randomNonNegativeLong(), null, null);
} else {
- return new Request(randomAlphaOfLength(10), randomNonNegativeLong(), null, randomAlphaOfLength(20));
+ return new Request(randomTimeValue(), randomAlphaOfLength(10), randomNonNegativeLong(), null, randomAlphaOfLength(20));
}
}
diff --git a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java
index 7cf57325baa0..9d408a0d152d 100644
--- a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java
+++ b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java
@@ -260,12 +260,7 @@ public class PersistentTasksNodeServiceTests extends ESTestCase {
when(client.settings()).thenReturn(Settings.EMPTY);
PersistentTasksService persistentTasksService = new PersistentTasksService(null, null, client) {
@Override
- void sendCancelRequest(
- final long taskId,
- final String reason,
- final TimeValue timeout,
- final ActionListener listener
- ) {
+ void sendCancelRequest(final long taskId, final String reason, final ActionListener listener) {
capturedTaskId.set(taskId);
capturedListener.set(listener);
}
@@ -356,12 +351,7 @@ public class PersistentTasksNodeServiceTests extends ESTestCase {
when(client.settings()).thenReturn(Settings.EMPTY);
PersistentTasksService persistentTasksService = new PersistentTasksService(null, null, client) {
@Override
- void sendCancelRequest(
- final long taskId,
- final String reason,
- final TimeValue timeout,
- final ActionListener listener
- ) {
+ void sendCancelRequest(final long taskId, final String reason, final ActionListener listener) {
fail("Shouldn't be called during local abort");
}
diff --git a/server/src/test/java/org/elasticsearch/persistent/StartPersistentActionRequestTests.java b/server/src/test/java/org/elasticsearch/persistent/StartPersistentActionRequestTests.java
index 07079f6c64df..44434b6500ca 100644
--- a/server/src/test/java/org/elasticsearch/persistent/StartPersistentActionRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/persistent/StartPersistentActionRequestTests.java
@@ -30,7 +30,7 @@ public class StartPersistentActionRequestTests extends AbstractWireSerializingTe
if (randomBoolean()) {
testParams.setExecutorNodeAttr(randomAlphaOfLengthBetween(1, 20));
}
- return new Request(UUIDs.base64UUID(), randomAlphaOfLengthBetween(1, 20), testParams);
+ return new Request(randomTimeValue(), UUIDs.base64UUID(), randomAlphaOfLengthBetween(1, 20), testParams);
}
@Override
diff --git a/server/src/test/java/org/elasticsearch/persistent/UpdatePersistentTaskRequestTests.java b/server/src/test/java/org/elasticsearch/persistent/UpdatePersistentTaskRequestTests.java
index 3988b3879956..61dc7f06dcbf 100644
--- a/server/src/test/java/org/elasticsearch/persistent/UpdatePersistentTaskRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/persistent/UpdatePersistentTaskRequestTests.java
@@ -22,7 +22,7 @@ public class UpdatePersistentTaskRequestTests extends AbstractWireSerializingTes
@Override
protected Request createTestInstance() {
- return new Request(UUIDs.base64UUID(), randomLong(), new State(randomAlphaOfLength(10)));
+ return new Request(randomTimeValue(), UUIDs.base64UUID(), randomLong(), new State(randomAlphaOfLength(10)));
}
@Override
diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java
index 7a05a4e712fc..fd3ed852650f 100644
--- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java
+++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/ShardFollowTaskCleaner.java
@@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRunnable;
+import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterStateListener;
@@ -110,6 +111,7 @@ public final class ShardFollowTaskCleaner implements ClusterStateListener {
client.execute(
CompletionPersistentTaskAction.INSTANCE,
new CompletionPersistentTaskAction.Request(
+ MasterNodeRequest.INFINITE_MASTER_NODE_TIMEOUT,
persistentTask.getId(),
persistentTask.getAllocationId(),
new IndexNotFoundException(followerIndex),
diff --git a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java
index 796ff4e677aa..7c26ad60fb13 100644
--- a/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java
+++ b/x-pack/plugin/downsample/src/main/java/org/elasticsearch/xpack/downsample/TransportDownsampleAction.java
@@ -549,7 +549,7 @@ public class TransportDownsampleAction extends AcknowledgedTransportMasterNodeAc
persistentTaskId,
DownsampleShardTask.TASK_NAME,
params,
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be configurable? longer by default? infinite? */,
ActionListener.wrap(
startedTask -> persistentTasksService.waitForPersistentTaskCondition(
startedTask.getId(),
diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
index 2d7c17db054a..26301f1397a0 100644
--- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
+++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
@@ -16,6 +16,7 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.persistent.PersistentTasksService;
import org.elasticsearch.tasks.Task;
@@ -81,7 +82,7 @@ public class ReindexDataStreamTransportAction extends HandledTransportAction listener.onResponse(AcknowledgedResponse.TRUE), listener::onFailure)
);
}
diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
index dfb960794537..60dc4325fee1 100644
--- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
+++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
@@ -308,6 +308,7 @@ public class MlDistributedFailureIT extends BaseMlIntegTestCase {
}
UpdatePersistentTaskStatusAction.Request updatePersistentTaskStatusRequest = new UpdatePersistentTaskStatusAction.Request(
+ TEST_REQUEST_TIMEOUT,
task.getId(),
task.getAllocationId(),
DatafeedState.STOPPING
diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java
index f3830d5cbf68..d124d5014c7e 100644
--- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java
+++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportPutRollupJobAction.java
@@ -322,7 +322,7 @@ public class TransportPutRollupJobAction extends AcknowledgedTransportMasterNode
job.getConfig().getId(),
RollupField.TASK_NAME,
job,
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be configurable? longer by default? infinite? */,
ActionListener.wrap(rollupConfigPersistentTask -> waitForRollupStarted(job, listener, persistentTasksService), e -> {
if (e instanceof ResourceAlreadyExistsException) {
e = new ElasticsearchStatusException(
diff --git a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/PutJobStateMachineTests.java b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/PutJobStateMachineTests.java
index fed2439e513c..5868a762ed51 100644
--- a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/PutJobStateMachineTests.java
+++ b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/PutJobStateMachineTests.java
@@ -42,6 +42,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -373,10 +374,10 @@ public class PutJobStateMachineTests extends ESTestCase {
requestCaptor.getValue().onFailure(new ResourceAlreadyExistsException(job.getConfig().getRollupIndex()));
return null;
}).when(tasksService)
- .sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), eq(null), requestCaptor.capture());
+ .sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), isNotNull(), requestCaptor.capture());
TransportPutRollupJobAction.startPersistentTask(job, testListener, tasksService);
- verify(tasksService).sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), eq(null), any());
+ verify(tasksService).sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), isNotNull(), any());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -401,7 +402,7 @@ public class PutJobStateMachineTests extends ESTestCase {
requestCaptor.getValue().onResponse(response);
return null;
}).when(tasksService)
- .sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), eq(null), requestCaptor.capture());
+ .sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), isNotNull(), requestCaptor.capture());
ArgumentCaptor requestCaptor2 = ArgumentCaptor.forClass(
PersistentTasksService.WaitForPersistentTaskListener.class
@@ -413,7 +414,7 @@ public class PutJobStateMachineTests extends ESTestCase {
}).when(tasksService).waitForPersistentTaskCondition(eq(job.getConfig().getId()), any(), any(), requestCaptor2.capture());
TransportPutRollupJobAction.startPersistentTask(job, testListener, tasksService);
- verify(tasksService).sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), eq(null), any());
+ verify(tasksService).sendStartRequest(eq(job.getConfig().getId()), eq(RollupField.TASK_NAME), eq(job), isNotNull(), any());
verify(tasksService).waitForPersistentTaskCondition(eq(job.getConfig().getId()), any(), any(), any());
}
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
index fd530a338b26..6004f8ebf95c 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
@@ -63,6 +63,7 @@ import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.core.IOUtils;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.Releasable;
+import org.elasticsearch.core.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeMetadata;
import org.elasticsearch.features.FeatureService;
@@ -1273,7 +1274,7 @@ public class Security extends Plugin
SecurityMigrationTaskParams.TASK_NAME,
SecurityMigrationTaskParams.TASK_NAME,
new SecurityMigrationTaskParams(migrationsVersion, securityMigrationNeeded),
- null,
+ TimeValue.THIRTY_SECONDS /* TODO should this be configurable? longer by default? infinite? */,
ActionListener.wrap((response) -> {
logger.debug("Security migration task submitted");
}, (exception) -> {
diff --git a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java
index 46f568d286f9..784f1c1fbe23 100644
--- a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java
+++ b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java
@@ -183,12 +183,18 @@ public class NodeShutdownTasksIT extends ESIntegTestCase {
private void startTask() {
logger.info("--> sending start request");
- persistentTasksService.sendStartRequest("task_id", "task_name", new TestTaskParams(), null, ActionListener.wrap(r -> {}, e -> {
- if (e instanceof ResourceAlreadyExistsException == false) {
- logger.error("failed to create task", e);
- fail("failed to create task");
- }
- }));
+ persistentTasksService.sendStartRequest(
+ "task_id",
+ "task_name",
+ new TestTaskParams(),
+ TEST_REQUEST_TIMEOUT,
+ ActionListener.wrap(r -> {}, e -> {
+ if (e instanceof ResourceAlreadyExistsException == false) {
+ logger.error("failed to create task", e);
+ fail("failed to create task");
+ }
+ })
+ );
}
@Override
diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportStopTransformActionTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportStopTransformActionTests.java
index 08e0982b2ab8..7c3b9f655b97 100644
--- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportStopTransformActionTests.java
+++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportStopTransformActionTests.java
@@ -266,11 +266,23 @@ public class TransportStopTransformActionTests extends ESTestCase {
when(client.threadPool()).thenReturn(threadPool);
doAnswer(randomBoolean() ? withResponse() : withException(new ResourceNotFoundException("task not found"))).when(client)
- .execute(same(RemovePersistentTaskAction.INSTANCE), eq(new RemovePersistentTaskAction.Request("task-A")), any());
+ .execute(
+ same(RemovePersistentTaskAction.INSTANCE),
+ eq(new RemovePersistentTaskAction.Request(TEST_REQUEST_TIMEOUT, "task-A")),
+ any()
+ );
doAnswer(randomBoolean() ? withResponse() : withException(new ResourceNotFoundException("task not found"))).when(client)
- .execute(same(RemovePersistentTaskAction.INSTANCE), eq(new RemovePersistentTaskAction.Request("task-B")), any());
+ .execute(
+ same(RemovePersistentTaskAction.INSTANCE),
+ eq(new RemovePersistentTaskAction.Request(TEST_REQUEST_TIMEOUT, "task-B")),
+ any()
+ );
doAnswer(withException(new IllegalStateException("real issue while removing task"))).when(client)
- .execute(same(RemovePersistentTaskAction.INSTANCE), eq(new RemovePersistentTaskAction.Request("task-C")), any());
+ .execute(
+ same(RemovePersistentTaskAction.INSTANCE),
+ eq(new RemovePersistentTaskAction.Request(TEST_REQUEST_TIMEOUT, "task-C")),
+ any()
+ );
PersistentTasksService persistentTasksService = new PersistentTasksService(mock(ClusterService.class), threadPool, client);
Set transformTasks = Set.of("task-A", "task-B", "task-C");
diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java
index e381659b1e01..535484ed3a19 100644
--- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java
+++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java
@@ -80,6 +80,7 @@ import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -352,7 +353,7 @@ public class TransformTaskTests extends ESTestCase {
eq(42L),
isNull(),
eq("Node is shutting down."),
- isNull(),
+ isNotNull(),
any()
);
}
From 928040ee765096224e4411d46a011f0bce3da9da Mon Sep 17 00:00:00 2001
From: David Kyle
Date: Thu, 23 Jan 2025 10:56:18 +0000
Subject: [PATCH 03/29] [ML] Automatically rollover legacy ml indices (#120405)
Rollover ml indices created in 7.x and create new indices that version 9 can
read and write to. This is required for ml to continue to run after during
upgrade and reindex of 7.x indices
---
docs/changelog/120405.yaml | 5 +
.../org/elasticsearch/TransportVersions.java | 1 +
.../core/ml/annotations/AnnotationIndex.java | 1 +
.../xpack/core/ml/utils/MlIndexAndAlias.java | 25 +-
.../core/ml/utils/MlIndexAndAliasTests.java | 9 +-
.../xpack/ml/MachineLearning.java | 24 +-
.../xpack/ml/MlAutoUpdateService.java | 25 +-
.../xpack/ml/MlIndexRollover.java | 176 +++++++++++
.../datafeed/DatafeedConfigAutoUpdater.java | 2 +-
.../xpack/ml/MlIndexRolloverTests.java | 283 ++++++++++++++++++
.../DatafeedConfigAutoUpdaterTests.java | 6 +-
11 files changed, 536 insertions(+), 21 deletions(-)
create mode 100644 docs/changelog/120405.yaml
create mode 100644 x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexRollover.java
create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlIndexRolloverTests.java
diff --git a/docs/changelog/120405.yaml b/docs/changelog/120405.yaml
new file mode 100644
index 000000000000..9ca30a9473e7
--- /dev/null
+++ b/docs/changelog/120405.yaml
@@ -0,0 +1,5 @@
+pr: 120405
+summary: Automatically rollover legacy ml indices
+area: Machine Learning
+type: upgrade
+issues: []
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 750b23caf215..65a745f0fe36 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -161,6 +161,7 @@ public class TransportVersions {
public static final TransportVersion ESQL_SKIP_ES_INDEX_SERIALIZATION = def(8_827_00_0);
public static final TransportVersion ADD_INDEX_BLOCK_TWO_PHASE = def(8_828_00_0);
public static final TransportVersion RESOLVE_CLUSTER_NO_INDEX_EXPRESSION = def(8_829_00_0);
+ public static final TransportVersion ML_ROLLOVER_LEGACY_INDICES = def(8_830_00_0);
/*
* STOP! READ THIS FIRST! No, really,
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java
index 95753f02e396..4ab096ca5858 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java
@@ -49,6 +49,7 @@ public class AnnotationIndex {
// Exposed for testing, but always use the aliases in non-test code.
public static final String LATEST_INDEX_NAME = ".ml-annotations-000001";
+ public static final String INDEX_PATTERN = ".ml-annotations-*";
// Due to historical bugs this index may not have the correct mappings
// in some production clusters. Therefore new annotations should be
// written to the latest index. If we ever switch to another new annotations
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java
index b630bafdbc77..e85acc159059 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java
@@ -66,7 +66,6 @@ public final class MlIndexAndAlias {
private static final Logger logger = LogManager.getLogger(MlIndexAndAlias.class);
- // Visible for testing
static final Comparator INDEX_NAME_COMPARATOR = new Comparator<>() {
private final Predicate HAS_SIX_DIGIT_SUFFIX = Pattern.compile("\\d{6}").asMatchPredicate();
@@ -172,7 +171,7 @@ public final class MlIndexAndAlias {
} else {
if (indexPointedByCurrentWriteAlias.isEmpty()) {
assert concreteIndexNames.length > 0;
- String latestConcreteIndexName = Arrays.stream(concreteIndexNames).max(INDEX_NAME_COMPARATOR).get();
+ String latestConcreteIndexName = latestIndex(concreteIndexNames);
updateWriteAlias(client, alias, null, latestConcreteIndexName, loggingListener);
return;
}
@@ -279,18 +278,22 @@ public final class MlIndexAndAlias {
);
}
- private static void updateWriteAlias(
+ public static void updateWriteAlias(
Client client,
String alias,
@Nullable String currentIndex,
String newIndex,
ActionListener listener
) {
- logger.info("About to move write alias [{}] from index [{}] to index [{}]", alias, currentIndex, newIndex);
+ if (currentIndex != null) {
+ logger.info("About to move write alias [{}] from index [{}] to index [{}]", alias, currentIndex, newIndex);
+ } else {
+ logger.info("About to create write alias [{}] for index [{}]", alias, newIndex);
+ }
IndicesAliasesRequestBuilder requestBuilder = client.admin()
.indices()
.prepareAliases()
- .addAliasAction(IndicesAliasesRequest.AliasActions.add().index(newIndex).alias(alias).isHidden(true));
+ .addAliasAction(IndicesAliasesRequest.AliasActions.add().index(newIndex).alias(alias).isHidden(true).writeIndex(true));
if (currentIndex != null) {
requestBuilder.removeAlias(currentIndex, alias);
}
@@ -380,4 +383,16 @@ public final class MlIndexAndAlias {
public static boolean hasIndexTemplate(ClusterState state, String templateName) {
return state.getMetadata().templatesV2().containsKey(templateName);
}
+
+ /**
+ * Returns the latest index. Latest is the index with the highest
+ * 6 digit suffix.
+ * @param concreteIndices List of index names
+ * @return The latest index by index name version suffix
+ */
+ public static String latestIndex(String[] concreteIndices) {
+ return concreteIndices.length == 1
+ ? concreteIndices[0]
+ : Arrays.stream(concreteIndices).max(MlIndexAndAlias.INDEX_NAME_COMPARATOR).get();
+ }
}
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAliasTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAliasTests.java
index 8e20ba4bfa9b..8fc1e55ec0ac 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAliasTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAliasTests.java
@@ -298,7 +298,7 @@ public class MlIndexAndAliasTests extends ESTestCase {
assertThat(
indicesAliasesRequest.getAliasActions(),
contains(
- AliasActions.add().alias(TEST_INDEX_ALIAS).index(FIRST_CONCRETE_INDEX).isHidden(true),
+ AliasActions.add().alias(TEST_INDEX_ALIAS).index(FIRST_CONCRETE_INDEX).isHidden(true).writeIndex(true),
AliasActions.remove().alias(TEST_INDEX_ALIAS).index(LEGACY_INDEX_WITHOUT_SUFFIX)
)
);
@@ -318,7 +318,7 @@ public class MlIndexAndAliasTests extends ESTestCase {
IndicesAliasesRequest indicesAliasesRequest = aliasesRequestCaptor.getValue();
assertThat(
indicesAliasesRequest.getAliasActions(),
- contains(AliasActions.add().alias(TEST_INDEX_ALIAS).index(expectedWriteIndexName).isHidden(true))
+ contains(AliasActions.add().alias(TEST_INDEX_ALIAS).index(expectedWriteIndexName).isHidden(true).writeIndex(true))
);
}
@@ -364,6 +364,11 @@ public class MlIndexAndAliasTests extends ESTestCase {
assertThat(Stream.of(".a-000002", ".b-000001").max(comparator).get(), equalTo(".a-000002"));
}
+ public void testLatestIndex() {
+ var names = new String[] { "index-000001", "index-000002", "index-000003" };
+ assertThat(MlIndexAndAlias.latestIndex(names), equalTo("index-000003"));
+ }
+
private void createIndexAndAliasIfNecessary(ClusterState clusterState) {
MlIndexAndAlias.createIndexAndAliasIfNecessary(
client,
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java
index 08c876dfdcc5..043a27b7cd14 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java
@@ -183,6 +183,7 @@ import org.elasticsearch.xpack.core.ml.action.UpdateTrainedModelDeploymentAction
import org.elasticsearch.xpack.core.ml.action.UpgradeJobModelSnapshotAction;
import org.elasticsearch.xpack.core.ml.action.ValidateDetectorAction;
import org.elasticsearch.xpack.core.ml.action.ValidateJobConfigAction;
+import org.elasticsearch.xpack.core.ml.annotations.AnnotationIndex;
import org.elasticsearch.xpack.core.ml.datafeed.DatafeedState;
import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsTaskState;
import org.elasticsearch.xpack.core.ml.dataframe.analyses.MlDataFrameAnalysisNamedXContentProvider;
@@ -1222,7 +1223,25 @@ public class MachineLearning extends Plugin
MlAutoUpdateService mlAutoUpdateService = new MlAutoUpdateService(
threadPool,
- List.of(new DatafeedConfigAutoUpdater(datafeedConfigProvider, indexNameExpressionResolver))
+ List.of(
+ new DatafeedConfigAutoUpdater(datafeedConfigProvider, indexNameExpressionResolver),
+ new MlIndexRollover(
+ List.of(
+ new MlIndexRollover.IndexPatternAndAlias(
+ AnomalyDetectorsIndex.jobStateIndexPattern(),
+ AnomalyDetectorsIndex.jobStateIndexWriteAlias()
+ ),
+ new MlIndexRollover.IndexPatternAndAlias(MlStatsIndex.indexPattern(), MlStatsIndex.writeAlias()),
+ new MlIndexRollover.IndexPatternAndAlias(AnnotationIndex.INDEX_PATTERN, AnnotationIndex.WRITE_ALIAS_NAME)
+ // TODO notifications = https://github.com/elastic/elasticsearch/pull/120064
+ // TODO anomaly results
+ // TODO .ml-inference-XXXXXX - requires alias
+ // TODO .ml-inference-native-XXXXXX - requires alias (index added in 8.0)
+ ),
+ indexNameExpressionResolver,
+ client
+ )
+ )
);
clusterService.addListener(mlAutoUpdateService);
// this object registers as a license state listener, and is never removed, so there's no need to retain another reference to it
@@ -2025,6 +2044,9 @@ public class MachineLearning extends Plugin
new AssociatedIndexDescriptor(MlStatsIndex.indexPattern(), "ML stats index"),
new AssociatedIndexDescriptor(".ml-notifications*", "ML notifications indices"),
new AssociatedIndexDescriptor(".ml-annotations*", "ML annotations indices")
+ // TODO should the inference indices be included here?
+ // new AssociatedIndexDescriptor(".ml-inference-*", "ML Data Frame Analytics")
+ // new AssociatedIndexDescriptor(".ml-inference-native*", "ML indices for trained models")
);
@Override
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlAutoUpdateService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlAutoUpdateService.java
index 94800daebf29..05c4d70e013e 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlAutoUpdateService.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlAutoUpdateService.java
@@ -30,7 +30,7 @@ public class MlAutoUpdateService implements ClusterStateListener {
String getName();
- void runUpdate();
+ void runUpdate(ClusterState latestState);
}
private final List updateActions;
@@ -47,27 +47,34 @@ public class MlAutoUpdateService implements ClusterStateListener {
@Override
public void clusterChanged(ClusterChangedEvent event) {
- if (event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
- return;
- }
if (event.localNodeMaster() == false) {
return;
}
+ if (event.state().blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) {
+ return;
+ }
- TransportVersion minTransportVersion = event.state().getMinTransportVersion();
+ if (completedUpdates.size() == updateActions.size()) {
+ return; // all work complete
+ }
+
+ final var latestState = event.state();
+ TransportVersion minTransportVersion = latestState.getMinTransportVersion();
final List toRun = updateActions.stream()
.filter(action -> action.isMinTransportVersionSupported(minTransportVersion))
.filter(action -> completedUpdates.contains(action.getName()) == false)
- .filter(action -> action.isAbleToRun(event.state()))
+ .filter(action -> action.isAbleToRun(latestState))
.filter(action -> currentlyUpdating.add(action.getName()))
.toList();
- threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME).execute(() -> toRun.forEach(this::runUpdate));
+ // TODO run updates serially
+ threadPool.executor(MachineLearning.UTILITY_THREAD_POOL_NAME)
+ .execute(() -> toRun.forEach((action) -> this.runUpdate(action, latestState)));
}
- private void runUpdate(UpdateAction action) {
+ private void runUpdate(UpdateAction action, ClusterState latestState) {
try {
logger.debug(() -> "[" + action.getName() + "] starting executing update action");
- action.runUpdate();
+ action.runUpdate(latestState);
this.completedUpdates.add(action.getName());
logger.debug(() -> "[" + action.getName() + "] succeeded executing update action");
} catch (Exception ex) {
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexRollover.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexRollover.java
new file mode 100644
index 000000000000..7dbafdc2676b
--- /dev/null
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlIndexRollover.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.ml;
+
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.TransportVersion;
+import org.elasticsearch.TransportVersions;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.action.support.PlainActionFuture;
+import org.elasticsearch.action.support.SubscribableListener;
+import org.elasticsearch.client.internal.Client;
+import org.elasticsearch.client.internal.OriginSettingClient;
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
+import org.elasticsearch.cluster.routing.IndexRoutingTable;
+import org.elasticsearch.index.IndexVersion;
+import org.elasticsearch.index.IndexVersions;
+import org.elasticsearch.logging.LogManager;
+import org.elasticsearch.logging.Logger;
+import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.elasticsearch.xpack.core.ClientHelper.ML_ORIGIN;
+
+/**
+ * If any of the indices listed in {@code indicesToRollover} are legacy indices
+ * then call rollover to produce a new index with the current version. If the
+ * index does not have an alias the alias is created first.
+ * If none of the {@code indicesToRollover} exist or they are all non-legacy
+ * indices then nothing will be updated.
+ */
+public class MlIndexRollover implements MlAutoUpdateService.UpdateAction {
+
+ private static final Logger logger = LogManager.getLogger(MlIndexRollover.class);
+
+ public record IndexPatternAndAlias(String indexPattern, String alias) {}
+
+ private final IndexNameExpressionResolver expressionResolver;
+ private final OriginSettingClient client;
+ private final List indicesToRollover;
+
+ public MlIndexRollover(List indicesToRollover, IndexNameExpressionResolver expressionResolver, Client client) {
+ this.expressionResolver = expressionResolver;
+ this.client = new OriginSettingClient(client, ML_ORIGIN);
+ this.indicesToRollover = indicesToRollover;
+ }
+
+ @Override
+ public boolean isMinTransportVersionSupported(TransportVersion minTransportVersion) {
+ // Wait for all nodes to be upgraded to ensure that the
+ // newly created index will be of the latest version.
+ return minTransportVersion.onOrAfter(TransportVersions.ML_ROLLOVER_LEGACY_INDICES);
+ }
+
+ @Override
+ public boolean isAbleToRun(ClusterState latestState) {
+ for (var indexPatternAndAlias : indicesToRollover) {
+ String[] indices = expressionResolver.concreteIndexNames(
+ latestState,
+ IndicesOptions.lenientExpandOpenHidden(),
+ indexPatternAndAlias.indexPattern
+ );
+ if (indices.length == 0) {
+ // The index does not exist but the MlAutoUpdateService will
+ // need to run this action and mark it as done.
+ // Ignore the missing index and continue the loop
+ continue;
+ }
+
+ String latestIndex = MlIndexAndAlias.latestIndex(indices);
+ IndexRoutingTable routingTable = latestState.getRoutingTable().index(latestIndex);
+ if (routingTable == null || routingTable.allPrimaryShardsActive() == false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "ml_legacy_index_rollover";
+ }
+
+ @Override
+ public void runUpdate(ClusterState latestState) {
+ List failures = new ArrayList<>();
+
+ for (var indexPatternAndAlias : indicesToRollover) {
+ PlainActionFuture rolloverIndices = new PlainActionFuture<>();
+ rolloverLegacyIndices(latestState, indexPatternAndAlias.indexPattern(), indexPatternAndAlias.alias(), rolloverIndices);
+ try {
+ rolloverIndices.actionGet();
+ } catch (Exception ex) {
+ logger.warn(() -> "failed rolling over legacy index [" + indexPatternAndAlias.indexPattern() + "]", ex);
+ if (ex instanceof ElasticsearchException elasticsearchException) {
+ failures.add(
+ new ElasticsearchStatusException("Failed rollover", elasticsearchException.status(), elasticsearchException)
+ );
+ } else {
+ failures.add(new ElasticsearchStatusException("Failed rollover", RestStatus.REQUEST_TIMEOUT, ex));
+ }
+
+ break;
+ }
+ }
+
+ if (failures.isEmpty()) {
+ logger.info("ML legacy indies rolled over");
+ return;
+ }
+
+ ElasticsearchException exception = new ElasticsearchException("some error");
+ failures.forEach(exception::addSuppressed);
+ throw exception;
+ }
+
+ private void rolloverLegacyIndices(ClusterState clusterState, String indexPattern, String alias, ActionListener listener) {
+ var concreteIndices = expressionResolver.concreteIndexNames(clusterState, IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED, indexPattern);
+
+ if (concreteIndices.length == 0) {
+ // no matching indices
+ listener.onResponse(Boolean.FALSE);
+ return;
+ }
+
+ String latestIndex = MlIndexAndAlias.latestIndex(concreteIndices);
+ boolean isCompatibleIndexVersion = isCompatibleIndexVersion(clusterState.metadata().index(latestIndex).getCreationVersion());
+ boolean hasAlias = clusterState.getMetadata().hasAlias(alias);
+
+ if (isCompatibleIndexVersion && hasAlias) {
+ // v8 index with alias, no action required
+ listener.onResponse(Boolean.FALSE);
+ return;
+ }
+
+ SubscribableListener.newForked(l -> {
+ if (hasAlias == false) {
+ MlIndexAndAlias.updateWriteAlias(client, alias, null, latestIndex, l);
+ } else {
+ l.onResponse(Boolean.TRUE);
+ }
+ }).andThen((l, success) -> {
+ if (isCompatibleIndexVersion == false) {
+ logger.info("rolling over legacy index [{}] with alias [{}]", latestIndex, alias);
+ rollover(alias, l);
+ } else {
+ l.onResponse(Boolean.TRUE);
+ }
+ }).addListener(listener);
+ }
+
+ private void rollover(String alias, ActionListener listener) {
+ client.admin().indices().rolloverIndex(new RolloverRequest(alias, null), listener.delegateFailure((l, response) -> {
+ l.onResponse(Boolean.TRUE);
+ }));
+ }
+
+ /**
+ * True if the version is read *and* write compatible not just read only compatible
+ */
+ static boolean isCompatibleIndexVersion(IndexVersion version) {
+ return version.onOrAfter(IndexVersions.MINIMUM_COMPATIBLE);
+ }
+}
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdater.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdater.java
index e61ffba9b316..9fe9a5226f28 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdater.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdater.java
@@ -77,7 +77,7 @@ public class DatafeedConfigAutoUpdater implements MlAutoUpdateService.UpdateActi
}
@Override
- public void runUpdate() {
+ public void runUpdate(ClusterState latestState) {
PlainActionFuture> getdatafeeds = new PlainActionFuture<>();
provider.expandDatafeedConfigs("_all", true, null, getdatafeeds);
List datafeedConfigBuilders = getdatafeeds.actionGet();
diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlIndexRolloverTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlIndexRolloverTests.java
new file mode 100644
index 000000000000..aa59028a4cc0
--- /dev/null
+++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlIndexRolloverTests.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.ml;
+
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
+import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
+import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
+import org.elasticsearch.action.admin.indices.rollover.RolloverAction;
+import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
+import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
+import org.elasticsearch.client.internal.Client;
+import org.elasticsearch.cluster.ClusterName;
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.metadata.AliasMetadata;
+import org.elasticsearch.cluster.metadata.IndexMetadata;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
+import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.cluster.routing.IndexRoutingTable;
+import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
+import org.elasticsearch.cluster.routing.RecoverySource;
+import org.elasticsearch.cluster.routing.RoutingTable;
+import org.elasticsearch.cluster.routing.ShardRouting;
+import org.elasticsearch.cluster.routing.UnassignedInfo;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.index.Index;
+import org.elasticsearch.index.IndexVersion;
+import org.elasticsearch.index.IndexVersions;
+import org.elasticsearch.index.shard.ShardId;
+import org.elasticsearch.indices.TestIndexNameExpressionResolver;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.threadpool.ThreadPool;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class MlIndexRolloverTests extends ESTestCase {
+
+ private final IndexNameExpressionResolver indexNameExpressionResolver = TestIndexNameExpressionResolver.newInstance();
+
+ public void testIsAbleToRun_IndicesDoNotExist() {
+ RoutingTable.Builder routingTable = RoutingTable.builder();
+ var rollover = new MlIndexRollover(
+ List.of(
+ new MlIndexRollover.IndexPatternAndAlias("my-index1-*", "my-index1-alias"),
+ new MlIndexRollover.IndexPatternAndAlias("my-index2-*", "my-index2-alias")
+ ),
+ indexNameExpressionResolver,
+ mock(Client.class)
+ );
+
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.routingTable(routingTable.build());
+ assertTrue(rollover.isAbleToRun(csBuilder.build()));
+ }
+
+ public void testIsAbleToRun_IndicesHaveNoRouting() {
+ IndexMetadata.Builder indexMetadata = IndexMetadata.builder("my-index-000001");
+ indexMetadata.settings(
+ Settings.builder()
+ .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current())
+ .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+ .put(IndexMetadata.SETTING_INDEX_UUID, "_uuid")
+ );
+
+ Metadata.Builder metadata = Metadata.builder();
+ metadata.put(indexMetadata);
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.routingTable(RoutingTable.builder().build()); // no routing to index
+ csBuilder.metadata(metadata);
+
+ var rollover = new MlIndexRollover(
+ List.of(new MlIndexRollover.IndexPatternAndAlias("my-index-*", "my-index-alias")),
+ indexNameExpressionResolver,
+ mock(Client.class)
+ );
+
+ assertFalse(rollover.isAbleToRun(csBuilder.build()));
+ }
+
+ public void testIsAbleToRun_IndicesHaveNoActiveShards() {
+ String indexName = "my-index-000001";
+ IndexMetadata.Builder indexMetadata = IndexMetadata.builder(indexName);
+ indexMetadata.settings(
+ Settings.builder()
+ .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current())
+ .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+ .put(IndexMetadata.SETTING_INDEX_UUID, "_uuid")
+ );
+ Index index = new Index(indexName, "_uuid");
+ ShardId shardId = new ShardId(index, 0);
+ ShardRouting shardRouting = ShardRouting.newUnassigned(
+ shardId,
+ true,
+ RecoverySource.EmptyStoreRecoverySource.INSTANCE,
+ new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, ""),
+ ShardRouting.Role.DEFAULT
+ );
+ shardRouting = shardRouting.initialize("node_id", null, 0L);
+ var routingTable = RoutingTable.builder()
+ .add(IndexRoutingTable.builder(index).addIndexShard(IndexShardRoutingTable.builder(shardId).addShard(shardRouting)))
+ .build();
+
+ Metadata.Builder metadata = Metadata.builder();
+ metadata.put(indexMetadata);
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.routingTable(routingTable);
+ csBuilder.metadata(metadata);
+
+ var rollover = new MlIndexRollover(
+ List.of(new MlIndexRollover.IndexPatternAndAlias("my-index-*", "my-index-alias")),
+ indexNameExpressionResolver,
+ mock(Client.class)
+ );
+
+ assertFalse(rollover.isAbleToRun(csBuilder.build()));
+ }
+
+ public void testRunUpdate_NoMatchingIndices() {
+ RoutingTable.Builder routingTable = RoutingTable.builder();
+
+ var client = mock(Client.class);
+ var rollover = new MlIndexRollover(
+ List.of(
+ new MlIndexRollover.IndexPatternAndAlias("my-index1-*", "my-index1-alias"),
+ new MlIndexRollover.IndexPatternAndAlias("my-index2-*", "my-index2-alias")
+ ),
+ indexNameExpressionResolver,
+ client
+ );
+
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.routingTable(routingTable.build());
+ rollover.runUpdate(csBuilder.build());
+ verify(client).settings();
+ verify(client).threadPool();
+ verifyNoMoreInteractions(client);
+ }
+
+ public void testRunUpdate_UpToDateIndicesWithAlias() {
+ String indexName = "my-index-000001";
+ String indexAlias = "my-index-write";
+ IndexMetadata.Builder indexMetadata = IndexMetadata.builder(indexName);
+ indexMetadata.settings(
+ Settings.builder()
+ .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current())
+ .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+ .put(IndexMetadata.SETTING_INDEX_UUID, "_uuid")
+ );
+ indexMetadata.putAlias(AliasMetadata.builder(indexAlias).build());
+
+ Metadata.Builder metadata = Metadata.builder();
+ metadata.put(indexMetadata);
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.metadata(metadata);
+
+ var client = mock(Client.class);
+ var rollover = new MlIndexRollover(
+ List.of(new MlIndexRollover.IndexPatternAndAlias("my-index-*", indexAlias)),
+ indexNameExpressionResolver,
+ client
+ );
+
+ rollover.runUpdate(csBuilder.build());
+ // everything up to date so no action for the client
+ verify(client).settings();
+ verify(client).threadPool();
+ verifyNoMoreInteractions(client);
+ }
+
+ public void testRunUpdate_LegacyIndexWithAlias() {
+ String indexName = "my-index-000001";
+ String indexAlias = "my-index-write";
+ IndexMetadata.Builder indexMetadata = IndexMetadata.builder(indexName);
+ indexMetadata.settings(
+ Settings.builder()
+ .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_7_17_0) // cannot read and write to a 7.x index
+ .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+ .put(IndexMetadata.SETTING_INDEX_UUID, "_uuid")
+ );
+ indexMetadata.putAlias(AliasMetadata.builder(indexAlias).build());
+
+ Metadata.Builder metadata = Metadata.builder();
+ metadata.put(indexMetadata);
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.metadata(metadata);
+
+ var client = mockClientWithRolloverAndAlias();
+ var rollover = new MlIndexRollover(
+ List.of(new MlIndexRollover.IndexPatternAndAlias("my-index-*", indexAlias)),
+ indexNameExpressionResolver,
+ client
+ );
+
+ rollover.runUpdate(csBuilder.build());
+ verify(client).settings();
+ verify(client, times(3)).threadPool();
+ verify(client).execute(same(RolloverAction.INSTANCE), any(), any()); // index rolled over
+ verifyNoMoreInteractions(client);
+ }
+
+ public void testRunUpdate_LegacyIndexWithoutAlias() {
+ String indexName = "my-index-000001";
+ String indexAlias = "my-index-write";
+ IndexMetadata.Builder indexMetadata = IndexMetadata.builder(indexName);
+ indexMetadata.settings(
+ Settings.builder()
+ .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_7_17_0)
+ .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
+ .put(IndexMetadata.SETTING_INDEX_UUID, "_uuid")
+ );
+ // index is missing alias
+
+ Metadata.Builder metadata = Metadata.builder();
+ metadata.put(indexMetadata);
+ ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
+ csBuilder.metadata(metadata);
+
+ var client = mockClientWithRolloverAndAlias();
+ var rollover = new MlIndexRollover(
+ List.of(new MlIndexRollover.IndexPatternAndAlias("my-index-*", indexAlias)),
+ indexNameExpressionResolver,
+ client
+ );
+
+ rollover.runUpdate(csBuilder.build());
+ verify(client).settings();
+ verify(client, times(5)).threadPool();
+ verify(client).execute(same(TransportIndicesAliasesAction.TYPE), any(), any()); // alias created
+ verify(client).execute(same(RolloverAction.INSTANCE), any(), any()); // index rolled over
+ verifyNoMoreInteractions(client);
+ }
+
+ public void testIsCompatibleIndexVersion() {
+ assertTrue(MlIndexRollover.isCompatibleIndexVersion(IndexVersion.current()));
+ assertTrue(MlIndexRollover.isCompatibleIndexVersion(IndexVersions.MINIMUM_COMPATIBLE));
+ assertFalse(MlIndexRollover.isCompatibleIndexVersion(IndexVersions.MINIMUM_READONLY_COMPATIBLE));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Client mockClientWithRolloverAndAlias() {
+ var client = mock(Client.class);
+
+ doAnswer(invocationOnMock -> {
+ ActionListener actionListener = (ActionListener) invocationOnMock.getArguments()[2];
+ actionListener.onResponse(new RolloverResponse("old", "new", Map.of(), false, true, true, true, true));
+ return null;
+ }).when(client).execute(same(RolloverAction.INSTANCE), any(RolloverRequest.class), any(ActionListener.class));
+
+ doAnswer(invocationOnMock -> {
+ ActionListener actionListener = (ActionListener) invocationOnMock
+ .getArguments()[2];
+ actionListener.onResponse(IndicesAliasesResponse.ACKNOWLEDGED_NO_ERRORS);
+ return null;
+ }).when(client).execute(same(TransportIndicesAliasesAction.TYPE), any(IndicesAliasesRequest.class), any(ActionListener.class));
+
+ var threadPool = mock(ThreadPool.class);
+ when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY));
+ when(client.threadPool()).thenReturn(threadPool);
+
+ return client;
+ }
+}
diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdaterTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdaterTests.java
index bf6e63faeb6c..337de5ae7d7a 100644
--- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdaterTests.java
+++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedConfigAutoUpdaterTests.java
@@ -79,7 +79,7 @@ public class DatafeedConfigAutoUpdaterTests extends ESTestCase {
withDatafeed(datafeedWithRewrite2, true);
DatafeedConfigAutoUpdater updater = new DatafeedConfigAutoUpdater(provider, indexNameExpressionResolver);
- updater.runUpdate();
+ updater.runUpdate(mock(ClusterState.class));
verify(provider, times(1)).updateDatefeedConfig(
eq(datafeedWithRewrite1),
@@ -120,7 +120,7 @@ public class DatafeedConfigAutoUpdaterTests extends ESTestCase {
}).when(provider).updateDatefeedConfig(eq(datafeedWithRewriteFailure), any(), any(), any(), any());
DatafeedConfigAutoUpdater updater = new DatafeedConfigAutoUpdater(provider, indexNameExpressionResolver);
- ElasticsearchException ex = expectThrows(ElasticsearchException.class, updater::runUpdate);
+ ElasticsearchException ex = expectThrows(ElasticsearchException.class, () -> updater.runUpdate(mock(ClusterState.class)));
assertThat(ex.getMessage(), equalTo("some datafeeds failed being upgraded."));
assertThat(ex.getSuppressed().length, equalTo(1));
assertThat(ex.getSuppressed()[0].getMessage(), equalTo("Failed to update datafeed " + datafeedWithRewriteFailure));
@@ -155,7 +155,7 @@ public class DatafeedConfigAutoUpdaterTests extends ESTestCase {
withDatafeed(datafeedWithoutRewrite2, false);
DatafeedConfigAutoUpdater updater = new DatafeedConfigAutoUpdater(provider, indexNameExpressionResolver);
- updater.runUpdate();
+ updater.runUpdate(mock(ClusterState.class));
verify(provider, times(0)).updateDatefeedConfig(any(), any(DatafeedUpdate.class), eq(Collections.emptyMap()), any(), any());
}
From 2002510d4a42658002091d3ce38ad84c0f2b4c52 Mon Sep 17 00:00:00 2001
From: Alexey Ivanov
Date: Thu, 23 Jan 2025 11:39:36 +0000
Subject: [PATCH 04/29] Fix Existing System Index Migration Integration Tests
(ES-10527) (#120663)
Re-enables system index migration tests in main. Previously, these tests were only running on the last minor version, leaving the system indices migration infrastructure largely untested for most of the time.
---
.../migration/AbstractFeatureMigrationIntegTest.java | 10 +++-------
.../elasticsearch/migration/FeatureMigrationIT.java | 8 ++++----
.../migration/MultiFeatureMigrationIT.java | 10 +++++-----
.../TransportGetFeatureUpgradeStatusAction.java | 12 +++++-------
.../org/elasticsearch/indices/SystemIndices.java | 4 +++-
5 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/AbstractFeatureMigrationIntegTest.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/AbstractFeatureMigrationIntegTest.java
index ea1c8ade00ab..2a1401242f81 100644
--- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/AbstractFeatureMigrationIntegTest.java
+++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/AbstractFeatureMigrationIntegTest.java
@@ -68,10 +68,11 @@ public abstract class AbstractFeatureMigrationIntegTest extends ESIntegTestCase
static final String INTERNAL_MANAGED_INDEX_NAME = ".int-man-old";
static final int INDEX_DOC_COUNT = 100; // arbitrarily chosen
static final int INTERNAL_MANAGED_FLAG_VALUE = 1;
- public static final Version NEEDS_UPGRADE_VERSION = TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION.previousMajor();
- public static final IndexVersion NEEDS_UPGRADE_INDEX_VERSION = IndexVersionUtils.getPreviousMajorVersion(
+ static final String FIELD_NAME = "some_field";
+ protected static final IndexVersion NEEDS_UPGRADE_INDEX_VERSION = IndexVersionUtils.getPreviousMajorVersion(
TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_INDEX_VERSION
);
+ protected static final int UPGRADED_TO_VERSION = TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION.major + 1;
static final SystemIndexDescriptor EXTERNAL_UNMANAGED = SystemIndexDescriptor.builder()
.setIndexPattern(".ext-unman-*")
@@ -131,11 +132,6 @@ public abstract class AbstractFeatureMigrationIntegTest extends ESIntegTestCase
@Before
public void setup() {
- assumeTrue(
- "We can only create the test indices we need if they're in the previous major version",
- NEEDS_UPGRADE_VERSION.onOrAfter(Version.CURRENT.previousMajor())
- );
-
internalCluster().setBootstrapMasterNodeIndex(0);
masterName = internalCluster().startMasterOnlyNode();
masterAndDataNode = internalCluster().startNode();
diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/FeatureMigrationIT.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/FeatureMigrationIT.java
index a4aa0514bb47..06233b614782 100644
--- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/FeatureMigrationIT.java
+++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/FeatureMigrationIT.java
@@ -208,7 +208,7 @@ public class FeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
assertIndexHasCorrectProperties(
finalMetadata,
- ".int-man-old-reindexed-for-9",
+ ".int-man-old-reindexed-for-" + UPGRADED_TO_VERSION,
INTERNAL_MANAGED_FLAG_VALUE,
true,
true,
@@ -216,7 +216,7 @@ public class FeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".int-unman-old-reindexed-for-9",
+ ".int-unman-old-reindexed-for-" + UPGRADED_TO_VERSION,
INTERNAL_UNMANAGED_FLAG_VALUE,
false,
true,
@@ -224,7 +224,7 @@ public class FeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".ext-man-old-reindexed-for-9",
+ ".ext-man-old-reindexed-for-" + UPGRADED_TO_VERSION,
EXTERNAL_MANAGED_FLAG_VALUE,
true,
false,
@@ -232,7 +232,7 @@ public class FeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".ext-unman-old-reindexed-for-9",
+ ".ext-unman-old-reindexed-for-" + UPGRADED_TO_VERSION,
EXTERNAL_UNMANAGED_FLAG_VALUE,
false,
false,
diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java
index 3442e9dc4392..01a414243f39 100644
--- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java
+++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java
@@ -218,7 +218,7 @@ public class MultiFeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
// Finally, verify that all the indices exist and have the properties we expect.
assertIndexHasCorrectProperties(
finalMetadata,
- ".int-man-old-reindexed-for-9",
+ ".int-man-old-reindexed-for-" + UPGRADED_TO_VERSION,
INTERNAL_MANAGED_FLAG_VALUE,
true,
true,
@@ -226,7 +226,7 @@ public class MultiFeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".int-unman-old-reindexed-for-9",
+ ".int-unman-old-reindexed-for-" + UPGRADED_TO_VERSION,
INTERNAL_UNMANAGED_FLAG_VALUE,
false,
true,
@@ -234,7 +234,7 @@ public class MultiFeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".ext-man-old-reindexed-for-9",
+ ".ext-man-old-reindexed-for-" + UPGRADED_TO_VERSION,
EXTERNAL_MANAGED_FLAG_VALUE,
true,
false,
@@ -242,7 +242,7 @@ public class MultiFeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
);
assertIndexHasCorrectProperties(
finalMetadata,
- ".ext-unman-old-reindexed-for-9",
+ ".ext-unman-old-reindexed-for-" + UPGRADED_TO_VERSION,
EXTERNAL_UNMANAGED_FLAG_VALUE,
false,
false,
@@ -251,7 +251,7 @@ public class MultiFeatureMigrationIT extends AbstractFeatureMigrationIntegTest {
assertIndexHasCorrectProperties(
finalMetadata,
- ".second-int-man-old-reindexed-for-9",
+ ".second-int-man-old-reindexed-for-" + UPGRADED_TO_VERSION,
SECOND_FEATURE_IDX_FLAG_VALUE,
true,
true,
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java
index 7378cad0ed29..57673e5fe137 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java
@@ -19,12 +19,12 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.util.concurrent.EsExecutors;
+import org.elasticsearch.core.UpdateForV10;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.indices.SystemIndices;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.persistent.PersistentTasksCustomMetadata;
-import org.elasticsearch.persistent.PersistentTasksService;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@@ -53,13 +53,13 @@ public class TransportGetFeatureUpgradeStatusAction extends TransportMasterNodeA
GetFeatureUpgradeStatusResponse> {
/**
- * Once all feature migrations for 9.x -> 10.x have been tested, we can bump this to Version.V_9_0_0
+ * These versions should be set to current major and current major's index version
*/
- public static final Version NO_UPGRADE_REQUIRED_VERSION = Version.V_8_0_0;
- public static final IndexVersion NO_UPGRADE_REQUIRED_INDEX_VERSION = IndexVersions.V_8_0_0;
+ @UpdateForV10(owner = UpdateForV10.Owner.CORE_INFRA)
+ public static final Version NO_UPGRADE_REQUIRED_VERSION = Version.V_9_0_0;
+ public static final IndexVersion NO_UPGRADE_REQUIRED_INDEX_VERSION = IndexVersions.UPGRADE_TO_LUCENE_10_0_0;
private final SystemIndices systemIndices;
- PersistentTasksService persistentTasksService;
@Inject
public TransportGetFeatureUpgradeStatusAction(
@@ -68,7 +68,6 @@ public class TransportGetFeatureUpgradeStatusAction extends TransportMasterNodeA
ActionFilters actionFilters,
ClusterService clusterService,
IndexNameExpressionResolver indexNameExpressionResolver,
- PersistentTasksService persistentTasksService,
SystemIndices systemIndices
) {
super(
@@ -83,7 +82,6 @@ public class TransportGetFeatureUpgradeStatusAction extends TransportMasterNodeA
);
this.systemIndices = systemIndices;
- this.persistentTasksService = persistentTasksService;
}
@Override
diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java
index 42cda4da1a9e..d01763d676f3 100644
--- a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java
+++ b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java
@@ -16,6 +16,7 @@ import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.admin.cluster.migration.TransportGetFeatureUpgradeStatusAction;
import org.elasticsearch.action.admin.cluster.snapshots.features.ResetFeatureStateResponse.ResetFeatureStateStatus;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.TransportDeleteIndexAction;
@@ -110,7 +111,8 @@ import static org.elasticsearch.tasks.TaskResultsService.TASKS_FEATURE_NAME;
public class SystemIndices {
public static final String SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY = "_system_index_access_allowed";
public static final String EXTERNAL_SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY = "_external_system_index_access_origin";
- public static final String UPGRADED_INDEX_SUFFIX = "-reindexed-for-9";
+ private static final int UPGRADED_TO_VERSION = TransportGetFeatureUpgradeStatusAction.NO_UPGRADE_REQUIRED_VERSION.major + 1;
+ public static final String UPGRADED_INDEX_SUFFIX = "-reindexed-for-" + UPGRADED_TO_VERSION;
private static final Automaton EMPTY = Automata.makeEmpty();
From 45ae0718cc6f52c78c9dc96380d623be9917ba07 Mon Sep 17 00:00:00 2001
From: Luke Whiting
Date: Thu, 23 Jan 2025 12:10:26 +0000
Subject: [PATCH 05/29] Report Deprecated Indices That Are Flagged To Ignore
Migration Reindex As A Warning (#120629)
* Add block state matching option to deprecation check predicate
* Add new deprecation checks to warn on old indices with ignore reindex flag
* Test for new deprecation checks
* Update docs/changelog/120629.yaml
* PR Changes - Remove leftover comment that's no longer true
---
docs/changelog/120629.yaml | 6 ++
.../deprecation/DeprecatedIndexPredicate.java | 34 ++++++---
.../DataStreamDeprecationChecks.java | 41 +++++++++--
.../xpack/deprecation/DeprecationChecks.java | 4 +-
.../deprecation/IndexDeprecationChecks.java | 20 +++++-
.../DataStreamDeprecationChecksTests.java | 72 +++++++++++++++++++
.../IndexDeprecationChecksTests.java | 20 ++++++
...ReindexDataStreamIndexTransportAction.java | 2 +-
.../ReindexDataStreamTransportAction.java | 2 +-
...indexDataStreamPersistentTaskExecutor.java | 6 +-
10 files changed, 186 insertions(+), 21 deletions(-)
create mode 100644 docs/changelog/120629.yaml
diff --git a/docs/changelog/120629.yaml b/docs/changelog/120629.yaml
new file mode 100644
index 000000000000..7862888d7fd4
--- /dev/null
+++ b/docs/changelog/120629.yaml
@@ -0,0 +1,6 @@
+pr: 120629
+summary: Report Deprecated Indices That Are Flagged To Ignore Migration Reindex As
+ A Warning
+area: Data streams
+type: enhancement
+issues: []
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecatedIndexPredicate.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecatedIndexPredicate.java
index 4c8a63ed7386..48fb8ebdc577 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecatedIndexPredicate.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecatedIndexPredicate.java
@@ -20,29 +20,38 @@ public class DeprecatedIndexPredicate {
public static final IndexVersion MINIMUM_WRITEABLE_VERSION_AFTER_UPGRADE = IndexVersions.UPGRADE_TO_LUCENE_10_0_0;
- /*
+ /**
* This predicate allows through only indices that were created with a previous lucene version, meaning that they need to be reindexed
- * in order to be writable in the _next_ lucene version.
+ * in order to be writable in the _next_ lucene version. It excludes searchable snapshots as they are not writable.
*
* It ignores searchable snapshots as they are not writable.
+ *
+ * @param metadata the cluster metadata
+ * @param filterToBlockedStatus if true, only indices that are write blocked will be returned,
+ * if false, only those without a block are returned
+ * @return a predicate that returns true for indices that need to be reindexed
*/
- public static Predicate getReindexRequiredPredicate(Metadata metadata) {
+ public static Predicate getReindexRequiredPredicate(Metadata metadata, boolean filterToBlockedStatus) {
return index -> {
IndexMetadata indexMetadata = metadata.index(index);
- return reindexRequired(indexMetadata);
+ return reindexRequired(indexMetadata, filterToBlockedStatus);
};
}
- public static boolean reindexRequired(IndexMetadata indexMetadata) {
+ /**
+ * This method check if the indices that were created with a previous lucene version, meaning that they need to be reindexed
+ * in order to be writable in the _next_ lucene version. It excludes searchable snapshots as they are not writable.
+ *
+ * @param indexMetadata the index metadata
+ * @param filterToBlockedStatus if true, only indices that are write blocked will be returned,
+ * if false, only those without a block are returned
+ * @return a predicate that returns true for indices that need to be reindexed
+ */
+ public static boolean reindexRequired(IndexMetadata indexMetadata, boolean filterToBlockedStatus) {
return creationVersionBeforeMinimumWritableVersion(indexMetadata)
&& isNotSearchableSnapshot(indexMetadata)
&& isNotClosed(indexMetadata)
- && isNotVerifiedReadOnly(indexMetadata);
- }
-
- private static boolean isNotVerifiedReadOnly(IndexMetadata indexMetadata) {
- // no need to check blocks.
- return MetadataIndexStateService.VERIFIED_READ_ONLY_SETTING.get(indexMetadata.getSettings()) == false;
+ && matchBlockedStatus(indexMetadata, filterToBlockedStatus);
}
private static boolean isNotSearchableSnapshot(IndexMetadata indexMetadata) {
@@ -57,4 +66,7 @@ public class DeprecatedIndexPredicate {
return indexMetadata.getState().equals(IndexMetadata.State.CLOSE) == false;
}
+ private static boolean matchBlockedStatus(IndexMetadata indexMetadata, boolean filterToBlockedStatus) {
+ return MetadataIndexStateService.VERIFIED_READ_ONLY_SETTING.get(indexMetadata.getSettings()) == filterToBlockedStatus;
+ }
}
diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecks.java
index 65f2659fda04..8af4868f9451 100644
--- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecks.java
+++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecks.java
@@ -24,10 +24,7 @@ public class DataStreamDeprecationChecks {
static DeprecationIssue oldIndicesCheck(DataStream dataStream, ClusterState clusterState) {
List backingIndices = dataStream.getIndices();
- Set indicesNeedingUpgrade = backingIndices.stream()
- .filter(DeprecatedIndexPredicate.getReindexRequiredPredicate(clusterState.metadata()))
- .map(Index::getName)
- .collect(Collectors.toUnmodifiableSet());
+ Set indicesNeedingUpgrade = getReindexRequiredIndices(backingIndices, clusterState, false);
if (indicesNeedingUpgrade.isEmpty() == false) {
return new DeprecationIssue(
@@ -47,4 +44,40 @@ public class DataStreamDeprecationChecks {
return null;
}
+
+ static DeprecationIssue ignoredOldIndicesCheck(DataStream dataStream, ClusterState clusterState) {
+ List backingIndices = dataStream.getIndices();
+
+ Set ignoredIndices = getReindexRequiredIndices(backingIndices, clusterState, true);
+
+ if (ignoredIndices.isEmpty() == false) {
+ return new DeprecationIssue(
+ DeprecationIssue.Level.WARNING,
+ "Old data stream with a compatibility version < 9.0 Have Been Ignored",
+ "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
+ "This data stream has read only backing indices that were created before Elasticsearch 9.0.0 and have been marked as "
+ + "OK to remain read-only after upgrade",
+ false,
+ ofEntries(
+ entry("reindex_required", true),
+ entry("total_backing_indices", backingIndices.size()),
+ entry("ignored_indices_requiring_upgrade_count", ignoredIndices.size()),
+ entry("ignored_indices_requiring_upgrade", ignoredIndices)
+ )
+ );
+ }
+
+ return null;
+ }
+
+ private static Set getReindexRequiredIndices(
+ List backingIndices,
+ ClusterState clusterState,
+ boolean filterToBlockedStatus
+ ) {
+ return backingIndices.stream()
+ .filter(DeprecatedIndexPredicate.getReindexRequiredPredicate(clusterState.metadata(), filterToBlockedStatus))
+ .map(Index::getName)
+ .collect(Collectors.toUnmodifiableSet());
+ }
}
diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java
index 1bc040418bf0..f7a26597e07f 100644
--- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java
+++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java
@@ -94,6 +94,7 @@ public class DeprecationChecks {
static List> INDEX_SETTINGS_CHECKS = List.of(
IndexDeprecationChecks::oldIndicesCheck,
+ IndexDeprecationChecks::ignoredOldIndicesCheck,
IndexDeprecationChecks::translogRetentionSettingCheck,
IndexDeprecationChecks::checkIndexDataPath,
IndexDeprecationChecks::storeTypeSettingCheck,
@@ -102,7 +103,8 @@ public class DeprecationChecks {
);
static List> DATA_STREAM_CHECKS = List.of(
- DataStreamDeprecationChecks::oldIndicesCheck
+ DataStreamDeprecationChecks::oldIndicesCheck,
+ DataStreamDeprecationChecks::ignoredOldIndicesCheck
);
/**
diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java
index 1bef1464152d..5a9d6771e5f4 100644
--- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java
+++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java
@@ -36,7 +36,7 @@ public class IndexDeprecationChecks {
// TODO: this check needs to be revised. It's trivially true right now.
IndexVersion currentCompatibilityVersion = indexMetadata.getCompatibilityVersion();
// We intentionally exclude indices that are in data streams because they will be picked up by DataStreamDeprecationChecks
- if (DeprecatedIndexPredicate.reindexRequired(indexMetadata) && isNotDataStreamIndex(indexMetadata, clusterState)) {
+ if (DeprecatedIndexPredicate.reindexRequired(indexMetadata, false) && isNotDataStreamIndex(indexMetadata, clusterState)) {
return new DeprecationIssue(
DeprecationIssue.Level.CRITICAL,
"Old index with a compatibility version < 9.0",
@@ -49,6 +49,24 @@ public class IndexDeprecationChecks {
return null;
}
+ static DeprecationIssue ignoredOldIndicesCheck(IndexMetadata indexMetadata, ClusterState clusterState) {
+ IndexVersion currentCompatibilityVersion = indexMetadata.getCompatibilityVersion();
+ // We intentionally exclude indices that are in data streams because they will be picked up by DataStreamDeprecationChecks
+ if (DeprecatedIndexPredicate.reindexRequired(indexMetadata, true) && isNotDataStreamIndex(indexMetadata, clusterState)) {
+ return new DeprecationIssue(
+ DeprecationIssue.Level.WARNING,
+ "Old index with a compatibility version < 9.0 Has Been Ignored",
+ "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
+ "This read-only index has version: "
+ + currentCompatibilityVersion.toReleaseVersion()
+ + " and will be supported as read-only in 9.0",
+ false,
+ Collections.singletonMap("reindex_required", true)
+ );
+ }
+ return null;
+ }
+
private static boolean isNotDataStreamIndex(IndexMetadata indexMetadata, ClusterState clusterState) {
return clusterState.metadata().findDataStreams(indexMetadata.getIndex().getName()).isEmpty();
}
diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecksTests.java
index 712807db46ec..edc7ea03823d 100644
--- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecksTests.java
+++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DataStreamDeprecationChecksTests.java
@@ -13,6 +13,7 @@ import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.cluster.metadata.DataStreamOptions;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.cluster.metadata.MetadataIndexStateService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexMode;
@@ -224,4 +225,75 @@ public class DataStreamDeprecationChecksTests extends ESTestCase {
nameToIndexMetadata.put(indexMetadata.getIndex().getName(), indexMetadata);
return indexMetadata.getIndex();
}
+
+ public void testOldIndicesIgnoredWarningCheck() {
+ int oldIndexCount = randomIntBetween(1, 100);
+ int newIndexCount = randomIntBetween(1, 100);
+
+ List allIndices = new ArrayList<>();
+ Map nameToIndexMetadata = new HashMap<>();
+ Set expectedIndices = new HashSet<>();
+
+ for (int i = 0; i < oldIndexCount; i++) {
+ Settings.Builder settings = settings(IndexVersion.fromId(7170099));
+
+ String indexName = "old-data-stream-index-" + i;
+ settings.put(MetadataIndexStateService.VERIFIED_READ_ONLY_SETTING.getKey(), true);
+ expectedIndices.add(indexName);
+
+ Settings.Builder settingsBuilder = settings;
+ IndexMetadata oldIndexMetadata = IndexMetadata.builder(indexName)
+ .settings(settingsBuilder)
+ .numberOfShards(1)
+ .numberOfReplicas(0)
+ .build();
+ allIndices.add(oldIndexMetadata.getIndex());
+ nameToIndexMetadata.put(oldIndexMetadata.getIndex().getName(), oldIndexMetadata);
+ }
+
+ for (int i = 0; i < newIndexCount; i++) {
+ Index newIndex = createNewIndex(i, false, nameToIndexMetadata);
+ allIndices.add(newIndex);
+ }
+
+ DataStream dataStream = new DataStream(
+ randomAlphaOfLength(10),
+ allIndices,
+ randomNegativeLong(),
+ Map.of(),
+ randomBoolean(),
+ false,
+ false,
+ randomBoolean(),
+ randomFrom(IndexMode.values()),
+ null,
+ randomFrom(DataStreamOptions.EMPTY, DataStreamOptions.FAILURE_STORE_DISABLED, DataStreamOptions.FAILURE_STORE_ENABLED, null),
+ List.of(),
+ randomBoolean(),
+ null
+ );
+
+ Metadata metadata = Metadata.builder().indices(nameToIndexMetadata).build();
+ ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metadata(metadata).build();
+
+ DeprecationIssue expected = new DeprecationIssue(
+ DeprecationIssue.Level.WARNING,
+ "Old data stream with a compatibility version < 9.0 Have Been Ignored",
+ "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
+ "This data stream has read only backing indices that were created before Elasticsearch 9.0.0 and have been marked as "
+ + "OK to remain read-only after upgrade",
+ false,
+ ofEntries(
+ entry("reindex_required", true),
+ entry("total_backing_indices", oldIndexCount + newIndexCount),
+ entry("ignored_indices_requiring_upgrade_count", expectedIndices.size()),
+ entry("ignored_indices_requiring_upgrade", expectedIndices)
+ )
+ );
+
+ List issues = DeprecationChecks.filterChecks(DATA_STREAM_CHECKS, c -> c.apply(dataStream, clusterState));
+
+ assertThat(issues, equalTo(singletonList(expected)));
+ }
+
}
diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java
index de229c555ade..ed119634427e 100644
--- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java
+++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java
@@ -13,6 +13,7 @@ import org.elasticsearch.cluster.metadata.DataStreamMetadata;
import org.elasticsearch.cluster.metadata.DataStreamOptions;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.cluster.metadata.MetadataIndexStateService;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexMode;
@@ -132,6 +133,25 @@ public class IndexDeprecationChecksTests extends ESTestCase {
assertThat(issues, empty());
}
+ public void testOldIndicesIgnoredWarningCheck() {
+ IndexVersion createdWith = IndexVersion.fromId(7170099);
+ Settings.Builder settings = settings(createdWith).put(MetadataIndexStateService.VERIFIED_READ_ONLY_SETTING.getKey(), true);
+ IndexMetadata indexMetadata = IndexMetadata.builder("test").settings(settings).numberOfShards(1).numberOfReplicas(0).build();
+ ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE)
+ .metadata(Metadata.builder().put(indexMetadata, true))
+ .build();
+ DeprecationIssue expected = new DeprecationIssue(
+ DeprecationIssue.Level.WARNING,
+ "Old index with a compatibility version < 9.0 Has Been Ignored",
+ "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
+ "This read-only index has version: " + createdWith.toReleaseVersion() + " and will be supported as read-only in 9.0",
+ false,
+ singletonMap("reindex_required", true)
+ );
+ List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(indexMetadata, clusterState));
+ assertEquals(singletonList(expected), issues);
+ }
+
public void testTranslogRetentionSettings() {
Settings.Builder settings = settings(IndexVersion.current());
settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_AGE_SETTING.getKey(), randomPositiveTimeValue());
diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java
index 45c318a6ec5a..fc2ca0364e8a 100644
--- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java
+++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java
@@ -118,7 +118,7 @@ public class ReindexDataStreamIndexTransportAction extends HandledTransportActio
IndexMetadata sourceIndex = clusterService.state().getMetadata().index(sourceIndexName);
Settings settingsBefore = sourceIndex.getSettings();
- var hasOldVersion = DeprecatedIndexPredicate.getReindexRequiredPredicate(clusterService.state().metadata());
+ var hasOldVersion = DeprecatedIndexPredicate.getReindexRequiredPredicate(clusterService.state().metadata(), false);
if (hasOldVersion.test(sourceIndex.getIndex()) == false) {
logger.warn(
"Migrating index [{}] with version [{}] is unnecessary as its version is not before [{}]",
diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
index 26301f1397a0..a6d9adc6b4e3 100644
--- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
+++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportAction.java
@@ -69,7 +69,7 @@ public class ReindexDataStreamTransportAction extends HandledTransportAction dataStreamInfos = response.getDataStreams();
if (dataStreamInfos.size() == 1) {
DataStream dataStream = dataStreamInfos.getFirst().getDataStream();
- if (getReindexRequiredPredicate(clusterService.state().metadata()).test(dataStream.getWriteIndex())) {
+ if (getReindexRequiredPredicate(clusterService.state().metadata(), false).test(dataStream.getWriteIndex())) {
RolloverRequest rolloverRequest = new RolloverRequest(sourceDataStream, null);
rolloverRequest.setParentTask(taskId);
reindexClient.execute(
@@ -161,7 +161,9 @@ public class ReindexDataStreamPersistentTaskExecutor extends PersistentTasksExec
TaskId parentTaskId
) {
List indices = dataStream.getIndices();
- List indicesToBeReindexed = indices.stream().filter(getReindexRequiredPredicate(clusterService.state().metadata())).toList();
+ List indicesToBeReindexed = indices.stream()
+ .filter(getReindexRequiredPredicate(clusterService.state().metadata(), false))
+ .toList();
final ReindexDataStreamPersistentTaskState updatedState;
if (params.totalIndices() != totalIndicesInDataStream
|| params.totalIndicesToBeUpgraded() != indicesToBeReindexed.size()
From ec546e31dbf73c77039ea9eb13bb94444a05d26c Mon Sep 17 00:00:00 2001
From: Craig Taverner
Date: Thu, 23 Jan 2025 15:04:54 +0200
Subject: [PATCH 06/29] Initial support for TEXT fields in LOOKUP JOIN
condition (#119473)
When the join field on the right hand-side is a TEXT field, we cannot do an exact match. Since ES|QL treats TEXT fields as KEYWORD in all cases, ideally we would like to do the same for JOIN. However, this is achieved on the left-hand index in a way that is not easily achievable on the right-hand side. Comparing filtering and field extraction of left and right:
* `FROM left`
* FieldExtraction is done using `field.keyword` subfield if it exists, or from `_source` otherwise
* Filtering is done by pushing down to Lucene `field.keyword` if it exists, or by not pushing down and filtering the value extracted from `_source` inside the compute engine itself
* `LOOKUP JOIN right`
* FieldExtraction is done simplistically, with no `_source` extraction
* Filtering pushdown can be done with `field.keyword` if it exists, but we have no easy solution to filtering otherwise
The decision taken is to disallow joining on TEXT fields, but allow explicit joining on the underlying keyword field (explicit in the query):
| left type | right type | result |
| --- | --- | --- |
| KEYWORD | KEYWORD | :white_check_mark: Works |
| TEXT | KEYWORD | :white_check_mark: Works |
| KEYWORD | TEXT | :x: Type mismatch error |
| TEXT | TEXT | :x: Type mismatch error |
### Examples
#### KEYWORD-KEYWORD :white_check_mark:
```
FROM test
| LOOKUP JOIN `test-lookup` ON color.keyword
```
#### TEXT-KEYWORD :white_check_mark:
```
FROM test
| RENAME color AS x
| EVAL color.keyword = x
| LOOKUP JOIN `test-lookup` ON color.keyword
```
#### KEYWORD-TEXT :x:
```
FROM test
| EVAL color = color.keyword
| LOOKUP JOIN `test-lookup` ON color
```
#### TEXT-TEXT :x:
```
FROM test
| LOOKUP JOIN `test-lookup` ON color
```
---
.../src/main/resources/lookup-join.csv-spec | 30 +++-
.../xpack/esql/action/EsqlCapabilities.java | 5 +
.../esql/enrich/LookupFromIndexService.java | 15 +-
.../xpack/esql/plan/logical/join/Join.java | 8 +-
.../esql/planner/LocalExecutionPlanner.java | 36 +++--
.../test/esql/191_lookup_join_text.yml | 133 ++++++++++++++++++
6 files changed, 201 insertions(+), 26 deletions(-)
create mode 100644 x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec
index ae7593b7999a..7b2395030a53 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec
@@ -520,7 +520,7 @@ emp_no:integer | language_code:integer | language_name:keyword
;
###########################################################################
-# nested filed join behavior with languages_nested_fields index
+# nested field join behavior with languages_nested_fields index
###########################################################################
joinOnNestedField
@@ -568,6 +568,34 @@ language.id:integer | language.name:text | language.name.keyword:keyword
1 | English | English
;
+joinOnNestedNestedFieldRowExplicitKeyword
+required_capability: join_lookup_v11
+required_capability: lookup_join_text
+
+ROW language.name.keyword = "English"
+| LOOKUP JOIN languages_nested_fields ON language.name.keyword
+| KEEP language.id, language.name, language.name.keyword
+;
+
+language.id:integer | language.name:text | language.name.keyword:keyword
+1 | English | English
+;
+
+joinOnNestedNestedFieldRowExplicitKeywords
+required_capability: join_lookup_v11
+required_capability: lookup_join_text
+
+ROW language.name.keyword = ["English", "French"]
+| MV_EXPAND language.name.keyword
+| LOOKUP JOIN languages_nested_fields ON language.name.keyword
+| KEEP language.id, language.name, language.name.keyword, language.code
+;
+
+language.id:integer | language.name:text | language.name.keyword:keyword | language.code:keyword
+1 | English | English | EN
+2 | French | French | FR
+;
+
###############################################
# Tests with clientips_lookup index
###############################################
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
index fd132af21001..08e0f0cf473e 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
@@ -690,6 +690,11 @@ public class EsqlCapabilities {
*/
JOIN_LOOKUP_V11(Build.current().isSnapshot()),
+ /**
+ * LOOKUP JOIN with TEXT fields on the right (right side of the join) (#119473)
+ */
+ LOOKUP_JOIN_TEXT(Build.current().isSnapshot()),
+
/**
* LOOKUP JOIN without MV matching (https://github.com/elastic/elasticsearch/issues/118780)
*/
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
index 37d2e14d7d89..9bea212a56aa 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
@@ -19,12 +19,10 @@ import org.elasticsearch.compute.data.BlockStreamInput;
import org.elasticsearch.compute.data.Page;
import org.elasticsearch.compute.operator.lookup.QueryList;
import org.elasticsearch.core.Releasables;
-import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
import org.elasticsearch.xpack.esql.action.EsqlQueryAction;
import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
import org.elasticsearch.xpack.esql.core.tree.Source;
@@ -79,9 +77,7 @@ public class LookupFromIndexService extends AbstractLookupService matchFields = new ArrayList<>(join.leftFields().size());
- for (Attribute m : join.leftFields()) {
- Layout.ChannelAndType t = source.layout.get(m.id());
- if (t == null) {
- throw new IllegalArgumentException("can't plan [" + join + "][" + m + "]");
+ if (join.leftFields().size() != join.rightFields().size()) {
+ throw new IllegalArgumentException("can't plan [" + join + "]: mismatching left and right field count");
+ }
+ List matchFields = new ArrayList<>(join.leftFields().size());
+ for (int i = 0; i < join.leftFields().size(); i++) {
+ TypedAttribute left = (TypedAttribute) join.leftFields().get(i);
+ FieldAttribute right = (FieldAttribute) join.rightFields().get(i);
+ Layout.ChannelAndType input = source.layout.get(left.id());
+ if (input == null) {
+ throw new IllegalArgumentException("can't plan [" + join + "][" + left + "]");
}
- matchFields.add(t);
+ matchFields.add(new MatchConfig(right, input));
}
if (matchFields.size() != 1) {
- throw new IllegalArgumentException("can't plan [" + join + "]");
+ throw new IllegalArgumentException("can't plan [" + join + "]: multiple join predicates are not supported");
}
+ // TODO support multiple match fields, and support more than equality predicates
+ MatchConfig matchConfig = matchFields.getFirst();
return source.with(
new LookupFromIndexOperator.Factory(
sessionId,
parentTask,
context.queryPragmas().enrichMaxWorkers(),
- matchFields.getFirst().channel(),
+ matchConfig.channel(),
ctx -> lookupFromIndexService,
- matchFields.getFirst().type(),
+ matchConfig.type(),
indexName,
- join.leftFields().getFirst().name(),
+ matchConfig.fieldName(),
join.addedFields().stream().map(f -> (NamedExpression) f).toList(),
join.source()
),
@@ -600,6 +609,13 @@ public class LocalExecutionPlanner {
);
}
+ private record MatchConfig(String fieldName, int channel, DataType type) {
+ private MatchConfig(FieldAttribute match, Layout.ChannelAndType input) {
+ // Note, this handles TEXT fields with KEYWORD subfields
+ this(match.exactAttribute().name(), input.channel(), input.type());
+ }
+ }
+
private PhysicalOperation planLocal(LocalSourceExec localSourceExec, LocalExecutionPlannerContext context) {
Layout.Builder layout = new Layout.Builder();
layout.append(localSourceExec.output());
diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml
new file mode 100644
index 000000000000..1b532ab80eeb
--- /dev/null
+++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml
@@ -0,0 +1,133 @@
+---
+setup:
+ - requires:
+ test_runner_features: [capabilities, contains]
+ capabilities:
+ - method: POST
+ path: /_query
+ parameters: []
+ capabilities: [lookup_join_text]
+ reason: "uses LOOKUP JOIN"
+ - do:
+ indices.create:
+ index: test
+ body:
+ mappings:
+ properties:
+ color:
+ type: text
+ fields:
+ keyword:
+ type: keyword
+ description:
+ type: text
+ fields:
+ keyword:
+ type: keyword
+ - do:
+ indices.create:
+ index: test-lookup
+ body:
+ settings:
+ index:
+ mode: lookup
+ number_of_shards: 1
+ mappings:
+ properties:
+ color:
+ type: text
+ fields:
+ keyword:
+ type: keyword
+ description:
+ type: text
+ fields:
+ keyword:
+ type: keyword
+ - do:
+ bulk:
+ index: "test"
+ refresh: true
+ body:
+ - { "index": { } }
+ - { "color": "red", "description": "The color Red" }
+ - { "index": { } }
+ - { "color": "blue", "description": "The color Blue" }
+ - { "index": { } }
+ - { "color": "green", "description": "The color Green" }
+ - do:
+ bulk:
+ index: "test-lookup"
+ refresh: true
+ body:
+ - { "index": { } }
+ - { "color": "red", "description": "As red as a tomato" }
+ - { "index": { } }
+ - { "color": "blue", "description": "As blue as the sky" }
+
+---
+keyword-keyword:
+ - do:
+ esql.query:
+ body:
+ query: 'FROM test | SORT color | LOOKUP JOIN `test-lookup` ON color.keyword | LIMIT 3'
+
+ - length: { columns: 4 }
+ - length: { values: 3 }
+ - match: {columns.0.name: "color.keyword"}
+ - match: {columns.0.type: "keyword"}
+ - match: {columns.1.name: "color"}
+ - match: {columns.1.type: "text"}
+ - match: {columns.2.name: "description"}
+ - match: {columns.2.type: "text"}
+ - match: {columns.3.name: "description.keyword"}
+ - match: {columns.3.type: "keyword"}
+ - match: {values.0: ["blue", "blue", "As blue as the sky", "As blue as the sky"]}
+ - match: {values.1: ["green", null, null, null]}
+ - match: {values.2: ["red", "red", "As red as a tomato", "As red as a tomato"]}
+
+---
+text-keyword:
+ - do:
+ esql.query:
+ body:
+ query: 'FROM test | SORT color | RENAME color AS x | EVAL color.keyword = x | LOOKUP JOIN `test-lookup` ON color.keyword | LIMIT 3'
+
+ - length: { columns: 5 }
+ - length: { values: 3 }
+ - match: {columns.0.name: "x"}
+ - match: {columns.0.type: "text"}
+ - match: {columns.1.name: "color.keyword"}
+ - match: {columns.1.type: "text"}
+ - match: {columns.2.name: "color"}
+ - match: {columns.2.type: "text"}
+ - match: {columns.3.name: "description"}
+ - match: {columns.3.type: "text"}
+ - match: {columns.4.name: "description.keyword"}
+ - match: {columns.4.type: "keyword"}
+ - match: {values.0: ["blue", "blue", "blue", "As blue as the sky", "As blue as the sky"]}
+ - match: {values.1: ["green", "green", null, null, null]}
+ - match: {values.2: ["red", "red", "red", "As red as a tomato", "As red as a tomato"]}
+
+---
+text-text:
+ - do:
+ esql.query:
+ body:
+ query: 'FROM test | SORT color | LOOKUP JOIN `test-lookup` ON color | LIMIT 3'
+ catch: "bad_request"
+
+ - match: { error.type: "verification_exception" }
+ - contains: { error.reason: "Found 1 problem\nline 1:55: JOIN with right field [color] of type [TEXT] is not supported" }
+
+---
+keyword-text:
+ - do:
+ esql.query:
+ body:
+ query: 'FROM test | SORT color | EVAL color = color.keyword | LOOKUP JOIN `test-lookup` ON color | LIMIT 3'
+ catch: "bad_request"
+
+ - match: { error.type: "verification_exception" }
+ - contains: { error.reason: "Found 1 problem\nline 1:84: JOIN with right field [color] of type [TEXT] is not supported" }
+
From bb0d0ed6dd9dc07f5cdbc381cdb38d11ee1fe159 Mon Sep 17 00:00:00 2001
From: Liam Thompson <32779855+leemthompo@users.noreply.github.com>
Date: Thu, 23 Jan 2025 14:08:27 +0100
Subject: [PATCH 07/29] Removes outdated admonition (#120556) (#120703)
Resolves /security-docs/https://github.com/elastic/security-docs/issues/6430. Removes an outdated admonition.
(cherry picked from commit 63074d8e7042059ee1a10243b005534114f9fe82)
Co-authored-by: Benjamin Ironside Goldstein <91905639+benironside@users.noreply.github.com>
---
docs/reference/esql/esql-security-solution.asciidoc | 5 -----
1 file changed, 5 deletions(-)
diff --git a/docs/reference/esql/esql-security-solution.asciidoc b/docs/reference/esql/esql-security-solution.asciidoc
index 24766a5ef93f..835f2e59c46e 100644
--- a/docs/reference/esql/esql-security-solution.asciidoc
+++ b/docs/reference/esql/esql-security-solution.asciidoc
@@ -34,8 +34,3 @@ more, refer to {security-guide}/rules-ui-create.html#create-esql-rule[Create an
Use the Elastic AI Assistant to build {esql} queries, or answer questions about
the {esql} query language. To learn more, refer to
{security-guide}/security-assistant.html[AI Assistant].
-
-NOTE: For AI Assistant to answer questions about {esql} and write {esql}
-queries, you need to
-{security-guide}/security-assistant.html#set-up-ai-assistant[enable knowledge
-base].
From 77ef1d41a0e58fe6eb036a93952fc38031fe69cf Mon Sep 17 00:00:00 2001
From: Mary Gouseti
Date: Thu, 23 Jan 2025 15:24:45 +0200
Subject: [PATCH 08/29] Failure store - No selectors in snapshots (#120114)
In this PR we explore how we can include the failure store indices while
disallowing the user to use selectors in the `indices` field of the
create and restore snapshot requests.
**Security concerns**
The create and restore snapshot require cluster privileges only, so we
do not have to worry about how to handle index level security (see
[Create Snapshot
API](https://www.elastic.co/guide/en/elasticsearch/reference/current/create-snapshot-api.html#create-snapshot-api-prereqs)
& [Restore Snapshot
API](https://www.elastic.co/guide/en/elasticsearch/reference/current/restore-snapshot-api.html#restore-snapshot-api-prereqs)).
**Challenges**
While there are other APIs that do not support selectors but they do
include the failure indices in their response such as `GET
_data_stream/`, `GET _data_stream//stats`, etc; the
snapshot APIs are a little bit different. The reason for that is that
the data stream APIs first collect only the relevant data streams and
then they select the backing indices and/or the failure indices. On the
other hand, the snapshot API that works with both indices and data
streams cannot take such a shortcut and needs to use the
`concreteIndices` method.
We propose, to add a flag that when the selectors are not "allowed" can
determine if we need to include the failure store or not. In the past we
had something similar called the default selector, but it was more
flexible and it was used a fallback always.
Our goal now is to only specify the behaviour we want when the selectors
are not supported. This new flag allowed also to simplify the concrete
index resolution in `GET _data_stream//stats`
Relates to https://github.com/elastic/elasticsearch/issues/119545
---
.../datastreams/DataStreamsSnapshotsIT.java | 49 ++++++-
.../DataStreamsStatsTransportAction.java | 12 --
.../org/elasticsearch/TransportVersions.java | 1 +
.../create/CreateSnapshotRequest.java | 5 +-
.../restore/RestoreSnapshotRequest.java | 13 +-
.../datastreams/DataStreamsActionUtil.java | 59 ++-------
.../datastreams/DataStreamsStatsAction.java | 1 +
.../action/support/IndicesOptions.java | 121 +++++++++---------
.../metadata/IndexNameExpressionResolver.java | 27 ++--
.../snapshots/SnapshotsService.java | 3 -
.../create/CreateSnapshotRequestTests.java | 1 +
.../restore/RestoreSnapshotRequestTests.java | 1 +
.../DataStreamsActionUtilTests.java | 13 ++
.../action/support/IndicesOptionsTests.java | 9 +-
.../datafeed/DatafeedNodeSelectorTests.java | 7 +-
15 files changed, 177 insertions(+), 145 deletions(-)
diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
index 0ae7504bb9d7..e78d9b4f2b8c 100644
--- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
+++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
@@ -37,7 +37,6 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.cluster.metadata.DataStreamAlias;
-import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.index.Index;
@@ -341,13 +340,13 @@ public class DataStreamsSnapshotsIT extends AbstractSnapshotIntegTestCase {
assertThat(rolloverResponse.getNewIndex(), equalTo(DataStream.getDefaultBackingIndexName("ds", 3)));
}
- public void testFailureStoreSnapshotAndRestore() throws Exception {
+ public void testFailureStoreSnapshotAndRestore() {
String dataStreamName = "with-fs";
CreateSnapshotResponse createSnapshotResponse = client.admin()
.cluster()
.prepareCreateSnapshot(TEST_REQUEST_TIMEOUT, REPO, SNAPSHOT)
.setWaitForCompletion(true)
- .setIndices(IndexNameExpressionResolver.combineSelector(dataStreamName, IndexComponentSelector.ALL_APPLICABLE))
+ .setIndices(dataStreamName)
.setIncludeGlobalState(false)
.get();
@@ -398,6 +397,49 @@ public class DataStreamsSnapshotsIT extends AbstractSnapshotIntegTestCase {
}
}
+ public void testSelectorsNotAllowedInSnapshotAndRestore() {
+ String dataStreamName = "with-fs";
+ try {
+ client.admin()
+ .cluster()
+ .prepareCreateSnapshot(TEST_REQUEST_TIMEOUT, REPO, SNAPSHOT)
+ .setWaitForCompletion(true)
+ .setIndices(dataStreamName + "::" + randomFrom(IndexComponentSelector.values()).getKey())
+ .setIncludeGlobalState(false)
+ .get();
+ fail("Should have failed because selectors are not allowed in snapshot creation");
+ } catch (IllegalArgumentException e) {
+ assertThat(
+ e.getMessage(),
+ containsString("Index component selectors are not supported in this context but found selector in expression")
+ );
+ }
+ CreateSnapshotResponse createSnapshotResponse = client.admin()
+ .cluster()
+ .prepareCreateSnapshot(TEST_REQUEST_TIMEOUT, REPO, SNAPSHOT)
+ .setWaitForCompletion(true)
+ .setIndices("ds")
+ .setIncludeGlobalState(false)
+ .get();
+
+ RestStatus status = createSnapshotResponse.getSnapshotInfo().status();
+ assertEquals(RestStatus.OK, status);
+ try {
+ client.admin()
+ .cluster()
+ .prepareRestoreSnapshot(TEST_REQUEST_TIMEOUT, REPO, SNAPSHOT)
+ .setWaitForCompletion(true)
+ .setIndices(dataStreamName + "::" + randomFrom(IndexComponentSelector.values()).getKey())
+ .get();
+ fail("Should have failed because selectors are not allowed in snapshot restore");
+ } catch (IllegalArgumentException e) {
+ assertThat(
+ e.getMessage(),
+ containsString("Index component selectors are not supported in this context but found selector in expression")
+ );
+ }
+ }
+
public void testSnapshotAndRestoreAllIncludeSpecificDataStream() throws Exception {
DocWriteResponse indexResponse = client.prepareIndex("other-ds")
.setOpType(DocWriteRequest.OpType.CREATE)
@@ -1241,6 +1283,7 @@ public class DataStreamsSnapshotsIT extends AbstractSnapshotIntegTestCase {
SnapshotInfo retrievedSnapshot = getSnapshot(REPO, SNAPSHOT);
assertThat(retrievedSnapshot.dataStreams(), contains(dataStreamName));
assertThat(retrievedSnapshot.indices(), containsInAnyOrder(fsBackingIndexName));
+ assertThat(retrievedSnapshot.indices(), not(containsInAnyOrder(fsFailureIndexName)));
assertAcked(
safeGet(client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(TEST_REQUEST_TIMEOUT, "*")))
diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java
index c6bee86e20fc..4d6eead07b94 100644
--- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java
+++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/action/DataStreamsStatsTransportAction.java
@@ -16,7 +16,6 @@ import org.elasticsearch.action.datastreams.DataStreamsActionUtil;
import org.elasticsearch.action.datastreams.DataStreamsStatsAction;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
-import org.elasticsearch.action.support.IndexComponentSelector;
import org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlockException;
@@ -101,17 +100,6 @@ public class DataStreamsStatsTransportAction extends TransportBroadcastByNodeAct
return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices);
}
- @Override
- protected String[] resolveConcreteIndexNames(ClusterState clusterState, DataStreamsStatsAction.Request request) {
- return DataStreamsActionUtil.resolveConcreteIndexNamesWithSelector(
- indexNameExpressionResolver,
- clusterState,
- request.indices(),
- IndexComponentSelector.ALL_APPLICABLE,
- request.indicesOptions()
- ).toArray(String[]::new);
- }
-
@Override
protected ShardsIterator shards(ClusterState clusterState, DataStreamsStatsAction.Request request, String[] concreteIndices) {
return clusterState.getRoutingTable().allShards(concreteIndices);
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 65a745f0fe36..a50f888927d4 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -162,6 +162,7 @@ public class TransportVersions {
public static final TransportVersion ADD_INDEX_BLOCK_TWO_PHASE = def(8_828_00_0);
public static final TransportVersion RESOLVE_CLUSTER_NO_INDEX_EXPRESSION = def(8_829_00_0);
public static final TransportVersion ML_ROLLOVER_LEGACY_INDICES = def(8_830_00_0);
+ public static final TransportVersion ADD_INCLUDE_FAILURE_INDICES_OPTION = def(8_831_00_0);
/*
* STOP! READ THIS FIRST! No, really,
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java
index bfdf41e58f6d..5637971665a0 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java
@@ -16,7 +16,6 @@ import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.MasterNodeRequest;
-import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.bytes.BytesReference;
@@ -69,9 +68,7 @@ public class CreateSnapshotRequest extends MasterNodeRequest abstractionNames = indexNameExpressionResolver.dataStreams(
+ List resolvedDataStreamExpressions = indexNameExpressionResolver.dataStreams(
clusterState,
updateIndicesOptions(indicesOptions),
names
);
SortedMap indicesLookup = clusterState.getMetadata().getIndicesLookup();
- List results = new ArrayList<>(abstractionNames.size());
- for (ResolvedExpression abstractionName : abstractionNames) {
- IndexAbstraction indexAbstraction = indicesLookup.get(abstractionName.resource());
+ List results = new ArrayList<>(resolvedDataStreamExpressions.size());
+ for (ResolvedExpression resolvedExpression : resolvedDataStreamExpressions) {
+ IndexAbstraction indexAbstraction = indicesLookup.get(resolvedExpression.resource());
assert indexAbstraction != null;
if (indexAbstraction.getType() == IndexAbstraction.Type.DATA_STREAM) {
- selectDataStreamIndicesNames(
- (DataStream) indexAbstraction,
- IndexComponentSelector.FAILURES.equals(abstractionName.selector()),
- results
- );
- }
- }
- return results;
- }
-
- /**
- * Resolves a list of expressions into data stream names and then collects the concrete indices
- * that are applicable for those data streams based on the selector provided in the arguments.
- * @param indexNameExpressionResolver resolver object
- * @param clusterState state to query
- * @param names data stream expressions
- * @param selector which component indices of the data stream should be returned
- * @param indicesOptions options for expression resolution
- * @return A stream of concrete index names that belong to the components specified
- * on the data streams returned from the expressions given
- */
- public static List resolveConcreteIndexNamesWithSelector(
- IndexNameExpressionResolver indexNameExpressionResolver,
- ClusterState clusterState,
- String[] names,
- IndexComponentSelector selector,
- IndicesOptions indicesOptions
- ) {
- assert indicesOptions.allowSelectors() == false : "If selectors are enabled, use resolveConcreteIndexNames instead";
- List abstractionNames = indexNameExpressionResolver.dataStreamNames(
- clusterState,
- updateIndicesOptions(indicesOptions),
- names
- );
- SortedMap indicesLookup = clusterState.getMetadata().getIndicesLookup();
-
- List results = new ArrayList<>(abstractionNames.size());
- for (String abstractionName : abstractionNames) {
- IndexAbstraction indexAbstraction = indicesLookup.get(abstractionName);
- assert indexAbstraction != null;
- if (indexAbstraction.getType() == IndexAbstraction.Type.DATA_STREAM) {
- if (selector.shouldIncludeData()) {
- selectDataStreamIndicesNames((DataStream) indexAbstraction, false, results);
+ DataStream dataStream = (DataStream) indexAbstraction;
+ if (IndexNameExpressionResolver.shouldIncludeRegularIndices(indicesOptions, resolvedExpression.selector())) {
+ selectDataStreamIndicesNames(dataStream, false, results);
}
- if (selector.shouldIncludeFailures()) {
- selectDataStreamIndicesNames((DataStream) indexAbstraction, true, results);
+ if (IndexNameExpressionResolver.shouldIncludeFailureIndices(indicesOptions, resolvedExpression.selector())) {
+ selectDataStreamIndicesNames(dataStream, true, results);
}
}
}
diff --git a/server/src/main/java/org/elasticsearch/action/datastreams/DataStreamsStatsAction.java b/server/src/main/java/org/elasticsearch/action/datastreams/DataStreamsStatsAction.java
index 82afeec75237..aa89a2b04173 100644
--- a/server/src/main/java/org/elasticsearch/action/datastreams/DataStreamsStatsAction.java
+++ b/server/src/main/java/org/elasticsearch/action/datastreams/DataStreamsStatsAction.java
@@ -57,6 +57,7 @@ public class DataStreamsStatsAction extends ActionType states = EnumSet.noneOf(WildcardStates.class);
@@ -937,10 +936,15 @@ public record IndicesOptions(
} else if (in.getTransportVersion().onOrAfter(TransportVersions.REPLACE_FAILURE_STORE_OPTIONS_WITH_SELECTOR_SYNTAX)) {
allowSelectors = options.contains(Option.ALLOW_SELECTORS);
}
+ boolean includeFailureIndices = false;
+ if (in.getTransportVersion().onOrAfter(TransportVersions.ADD_INCLUDE_FAILURE_INDICES_OPTION)) {
+ includeFailureIndices = options.contains(Option.INCLUDE_FAILURE_INDICES);
+ }
GatekeeperOptions gatekeeperOptions = GatekeeperOptions.builder()
.allowClosedIndices(options.contains(Option.ERROR_WHEN_CLOSED_INDICES) == false)
.allowAliasToMultipleIndices(options.contains(Option.ERROR_WHEN_ALIASES_TO_MULTIPLE_INDICES) == false)
.allowSelectors(allowSelectors)
+ .includeFailureIndices(includeFailureIndices)
.ignoreThrottled(options.contains(Option.IGNORE_THROTTLED))
.build();
if (in.getTransportVersion().between(TransportVersions.V_8_14_0, TransportVersions.V_8_16_0)) {
@@ -1319,6 +1323,15 @@ public record IndicesOptions(
return STRICT_EXPAND_OPEN;
}
+ /**
+ * @return indices options that requires every specified index to exist, expands wildcards only to open indices and
+ * allows that no indices are resolved from wildcard expressions (not returning an error). It disallows selectors
+ * in the expression (no :: separators).
+ */
+ public static IndicesOptions strictExpandOpenFailureNoSelectors() {
+ return STRICT_EXPAND_OPEN_FAILURE_NO_SELECTOR;
+ }
+
/**
* @return indices options that requires every specified index to exist, expands wildcards only to open indices,
* allows that no indices are resolved from wildcard expressions (not returning an error) and forbids the
@@ -1362,22 +1375,13 @@ public record IndicesOptions(
return STRICT_EXPAND_OPEN_CLOSED_HIDDEN_NO_SELECTORS;
}
- /**
- * @return indices option that expands wildcards to both open and closed indices, includes failure store
- * (with data stream) and allows that indices can be missing and no indices are resolved from wildcard expressions
- * (not returning an error).
- */
- public static IndicesOptions lenientExpandIncludeFailureStore() {
- return LENIENT_EXPAND_OPEN_CLOSED_FAILURE_STORE;
- }
-
/**
* @return indices option that requires every specified index to exist, expands wildcards to both open and closed indices, includes
- * hidden indices, includes failure store (with data stream) and allows that no indices are resolved from wildcard expressions
- * (not returning an error).
+ * hidden indices, allows that no indices are resolved from wildcard expressions (not returning an error), and disallows selectors
+ * in the expression (no :: separators) but includes the failure indices.
*/
- public static IndicesOptions strictExpandHiddenIncludeFailureStore() {
- return STRICT_EXPAND_OPEN_CLOSED_HIDDEN_FAILURE_STORE;
+ public static IndicesOptions strictExpandHiddenFailureNoSelectors() {
+ return STRICT_EXPAND_OPEN_CLOSED_HIDDEN_FAILURE_NO_SELECTORS;
}
/**
@@ -1467,6 +1471,9 @@ public record IndicesOptions(
+ ignoreThrottled()
// Until the feature flag is removed we access the field directly from the gatekeeper options.
+ (DataStream.isFailureStoreFeatureFlagEnabled() ? ", allow_selectors=" + gatekeeperOptions().allowSelectors() : "")
+ + (DataStream.isFailureStoreFeatureFlagEnabled()
+ ? ", include_failure_indices=" + gatekeeperOptions().includeFailureIndices()
+ : "")
+ ']';
}
}
diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
index 9ad00b517d51..cb074b143704 100644
--- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
+++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
@@ -93,7 +93,17 @@ public class IndexNameExpressionResolver {
*/
public record ResolvedExpression(String resource, @Nullable IndexComponentSelector selector) {
public ResolvedExpression(String indexAbstraction) {
- this(indexAbstraction, null);
+ this(indexAbstraction, (IndexComponentSelector) null);
+ }
+
+ /**
+ * Constructs a ResolvedExpression with the DATA selector if the selectors are allowed
+ * or null otherwise.
+ * @param indexAbstraction
+ * @param options
+ */
+ public ResolvedExpression(String indexAbstraction, IndicesOptions options) {
+ this(indexAbstraction, options.allowSelectors() ? IndexComponentSelector.DATA : null);
}
public String combined() {
@@ -599,19 +609,19 @@ public class IndexNameExpressionResolver {
}
}
- private static boolean shouldIncludeRegularIndices(IndicesOptions indicesOptions, IndexComponentSelector expressionSelector) {
+ public static boolean shouldIncludeRegularIndices(IndicesOptions indicesOptions, IndexComponentSelector expressionSelector) {
if (indicesOptions.allowSelectors()) {
return expressionSelector == null || expressionSelector.shouldIncludeData();
}
return true;
}
- private static boolean shouldIncludeFailureIndices(IndicesOptions indicesOptions, IndexComponentSelector expressionSelector) {
+ public static boolean shouldIncludeFailureIndices(IndicesOptions indicesOptions, IndexComponentSelector expressionSelector) {
// We return failure indices regardless of whether the data stream actually has the `failureStoreEnabled` flag set to true.
if (indicesOptions.allowSelectors()) {
return expressionSelector != null && expressionSelector.shouldIncludeFailures();
}
- return false;
+ return indicesOptions.includeFailureIndices();
}
private static boolean resolvesToMoreThanOneIndex(IndexAbstraction indexAbstraction, Context context, ResolvedExpression expression) {
@@ -1699,12 +1709,7 @@ public class IndexNameExpressionResolver {
Index index = indexAbstraction.getIndices().get(i);
IndexMetadata indexMetadata = context.state.metadata().index(index);
if (indexMetadata.getState() != excludeState) {
- resources.add(
- new ResolvedExpression(
- index.getName(),
- context.options.allowSelectors() ? IndexComponentSelector.DATA : null
- )
- );
+ resources.add(new ResolvedExpression(index.getName(), context.getOptions()));
}
}
}
@@ -1715,7 +1720,7 @@ public class IndexNameExpressionResolver {
Index index = failureIndices.get(i);
IndexMetadata indexMetadata = context.state.metadata().index(index);
if (indexMetadata.getState() != excludeState) {
- resources.add(new ResolvedExpression(index.getName(), IndexComponentSelector.DATA));
+ resources.add(new ResolvedExpression(index.getName(), context.getOptions()));
}
}
}
diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
index d8763c47ecc4..008c75ed1347 100644
--- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
+++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
@@ -4136,9 +4136,6 @@ public final class SnapshotsService extends AbstractLifecycleComponent implement
request.partial(),
indexIds,
CollectionUtils.concatLists(
- // It's ok to just get the data stream names here because we have already resolved every concrete index that will be
- // in the snapshot, and thus already resolved any selectors that might be present. We now only care about which data
- // streams we're packing up in the resulting snapshot, not what their contents are.
indexNameExpressionResolver.dataStreamNames(currentState, request.indicesOptions(), request.indices()),
systemDataStreamNames
),
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestTests.java
index d0f734474f7c..6d620acca63b 100644
--- a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestTests.java
@@ -89,6 +89,7 @@ public class CreateSnapshotRequestTests extends ESTestCase {
randomBoolean()
)
)
+ .gatekeeperOptions(IndicesOptions.GatekeeperOptions.builder().allowSelectors(false).includeFailureIndices(true).build())
.build()
);
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
index a1c4466280a4..2be8989216d7 100644
--- a/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
@@ -89,6 +89,7 @@ public class RestoreSnapshotRequestTests extends AbstractWireSerializingTestCase
randomBoolean()
)
)
+ .gatekeeperOptions(IndicesOptions.GatekeeperOptions.builder().allowSelectors(false).includeFailureIndices(true).build())
.build()
);
}
diff --git a/server/src/test/java/org/elasticsearch/action/datastreams/DataStreamsActionUtilTests.java b/server/src/test/java/org/elasticsearch/action/datastreams/DataStreamsActionUtilTests.java
index 445a5ad067b7..2f6d8ffc9e15 100644
--- a/server/src/test/java/org/elasticsearch/action/datastreams/DataStreamsActionUtilTests.java
+++ b/server/src/test/java/org/elasticsearch/action/datastreams/DataStreamsActionUtilTests.java
@@ -104,6 +104,19 @@ public class DataStreamsActionUtilTests extends ESTestCase {
assertThat(resolved, containsInAnyOrder(".ds-foo1", ".ds-foo2", ".ds-baz1"));
+ // Including the failure indices
+ resolved = DataStreamsActionUtil.resolveConcreteIndexNames(
+ indexNameExpressionResolver,
+ clusterState,
+ query,
+ IndicesOptions.builder()
+ .wildcardOptions(IndicesOptions.WildcardOptions.builder().includeHidden(true))
+ .gatekeeperOptions(IndicesOptions.GatekeeperOptions.builder().allowSelectors(false).includeFailureIndices(true))
+ .build()
+ );
+
+ assertThat(resolved, containsInAnyOrder(".ds-foo1", ".ds-foo2", ".ds-baz1", ".fs-foo1"));
+
when(indexNameExpressionResolver.dataStreams(any(), any(), eq(query))).thenReturn(
List.of(
new ResolvedExpression("fooDs", IndexComponentSelector.DATA),
diff --git a/server/src/test/java/org/elasticsearch/action/support/IndicesOptionsTests.java b/server/src/test/java/org/elasticsearch/action/support/IndicesOptionsTests.java
index e33185d64e68..ebcd7698356e 100644
--- a/server/src/test/java/org/elasticsearch/action/support/IndicesOptionsTests.java
+++ b/server/src/test/java/org/elasticsearch/action/support/IndicesOptionsTests.java
@@ -55,6 +55,7 @@ public class IndicesOptionsTests extends ESTestCase {
.ignoreThrottled(randomBoolean())
.allowAliasToMultipleIndices(randomBoolean())
.allowClosedIndices(randomBoolean())
+ .allowSelectors(randomBoolean())
)
.build();
@@ -340,7 +341,13 @@ public class IndicesOptionsTests extends ESTestCase {
randomBoolean(),
randomBoolean()
);
- GatekeeperOptions gatekeeperOptions = new GatekeeperOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean());
+ GatekeeperOptions gatekeeperOptions = new GatekeeperOptions(
+ randomBoolean(),
+ randomBoolean(),
+ randomBoolean(),
+ randomBoolean(),
+ randomBoolean()
+ );
IndicesOptions indicesOptions = new IndicesOptions(concreteTargetOptions, wildcardOptions, gatekeeperOptions);
diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelectorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelectorTests.java
index f6cc282dde4d..50c260eff3d8 100644
--- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelectorTests.java
+++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelectorTests.java
@@ -349,7 +349,7 @@ public class DatafeedNodeSelectorTests extends ESTestCase {
+ "indices_options [IndicesOptions[ignore_unavailable=false, allow_no_indices=true, expand_wildcards_open=true, "
+ "expand_wildcards_closed=false, expand_wildcards_hidden=false, allow_aliases_to_multiple_indices=true, "
+ "forbid_closed_indices=true, ignore_aliases=false, ignore_throttled=true, "
- + "allow_selectors=true]] with exception [no such index [not_foo]]"
+ + "allow_selectors=true, include_failure_indices=false]] with exception [no such index [not_foo]]"
)
)
);
@@ -383,7 +383,8 @@ public class DatafeedNodeSelectorTests extends ESTestCase {
+ "indices given [not_foo] and indices_options [IndicesOptions[ignore_unavailable=false, allow_no_indices=true, "
+ "expand_wildcards_open=true, expand_wildcards_closed=false, expand_wildcards_hidden=false, "
+ "allow_aliases_to_multiple_indices=true, forbid_closed_indices=true, ignore_aliases=false, "
- + "ignore_throttled=true, allow_selectors=true]] with exception [no such index [not_foo]]]"
+ + "ignore_throttled=true, allow_selectors=true, include_failure_indices=false]] with exception "
+ + "[no such index [not_foo]]]"
)
)
);
@@ -560,7 +561,7 @@ public class DatafeedNodeSelectorTests extends ESTestCase {
+ "indices_options [IndicesOptions[ignore_unavailable=false, allow_no_indices=true, expand_wildcards_open=true, "
+ "expand_wildcards_closed=false, expand_wildcards_hidden=false, allow_aliases_to_multiple_indices=true, "
+ "forbid_closed_indices=true, ignore_aliases=false, ignore_throttled=true, "
- + "allow_selectors=true]] with exception [no such index [not_foo]]]"
+ + "allow_selectors=true, include_failure_indices=false]] with exception [no such index [not_foo]]]"
)
)
);
From 0e5fe752508e95357400d4312691907ba76e8bde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?=
Date: Thu, 23 Jan 2025 14:37:00 +0100
Subject: [PATCH 09/29] Rename test-plugin to entitlement-test-plugin (#120696)
---
libs/entitlement/qa/build.gradle | 4 ++--
.../{test-plugin => entitlement-test-plugin}/build.gradle | 2 +-
.../src/main/java/module-info.java | 0
.../entitlement/qa/test/DummyImplementations.java | 0
.../entitlement/qa/test/EntitlementTestPlugin.java | 0
.../qa/test/LoadNativeLibrariesCheckActions.java | 0
.../entitlement/qa/test/NetworkAccessCheckActions.java | 0
.../entitlement/qa/test/RestEntitlementsCheckAction.java | 0
.../entitlement/qa/test/VersionSpecificNetworkChecks.java | 0
.../entitlement/qa/test/WritePropertiesCheckActions.java | 0
.../entitlement/qa/test/VersionSpecificNetworkChecks.java | 0
.../entitlement/qa/test/VersionSpecificNetworkChecks.java | 0
.../elasticsearch/entitlement/qa/EntitlementsAllowedIT.java | 2 +-
.../entitlement/qa/EntitlementsAllowedNonModularIT.java | 2 +-
.../elasticsearch/entitlement/qa/EntitlementsDeniedIT.java | 2 +-
.../entitlement/qa/EntitlementsDeniedNonModularIT.java | 2 +-
muted-tests.yml | 6 ------
17 files changed, 7 insertions(+), 13 deletions(-)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/build.gradle (96%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/module-info.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/EntitlementTestPlugin.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/LoadNativeLibrariesCheckActions.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main/java/org/elasticsearch/entitlement/qa/test/WritePropertiesCheckActions.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java (100%)
rename libs/entitlement/qa/{test-plugin => entitlement-test-plugin}/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java (100%)
diff --git a/libs/entitlement/qa/build.gradle b/libs/entitlement/qa/build.gradle
index fea746eb3bfa..b3b1c830a1b8 100644
--- a/libs/entitlement/qa/build.gradle
+++ b/libs/entitlement/qa/build.gradle
@@ -12,6 +12,6 @@ apply plugin: 'elasticsearch.internal-java-rest-test'
apply plugin: 'elasticsearch.internal-test-artifact'
dependencies {
- javaRestTestImplementation project(':libs:entitlement:qa:test-plugin')
- clusterModules project(':libs:entitlement:qa:test-plugin')
+ javaRestTestImplementation project(':libs:entitlement:qa:entitlement-test-plugin')
+ clusterModules project(':libs:entitlement:qa:entitlement-test-plugin')
}
diff --git a/libs/entitlement/qa/test-plugin/build.gradle b/libs/entitlement/qa/entitlement-test-plugin/build.gradle
similarity index 96%
rename from libs/entitlement/qa/test-plugin/build.gradle
rename to libs/entitlement/qa/entitlement-test-plugin/build.gradle
index 74409d1f4e07..f23a8e979e36 100644
--- a/libs/entitlement/qa/test-plugin/build.gradle
+++ b/libs/entitlement/qa/entitlement-test-plugin/build.gradle
@@ -14,7 +14,7 @@ apply plugin: 'elasticsearch.build'
apply plugin: 'elasticsearch.mrjar'
esplugin {
- name = 'test-plugin'
+ name = 'entitlement-test-plugin'
description = 'A test plugin that invokes methods checked by entitlements'
classname = 'org.elasticsearch.entitlement.qa.test.EntitlementTestPlugin'
}
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/module-info.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/module-info.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/EntitlementTestPlugin.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/EntitlementTestPlugin.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/EntitlementTestPlugin.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/EntitlementTestPlugin.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/LoadNativeLibrariesCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/LoadNativeLibrariesCheckActions.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/LoadNativeLibrariesCheckActions.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/LoadNativeLibrariesCheckActions.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
diff --git a/libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/WritePropertiesCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/WritePropertiesCheckActions.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/WritePropertiesCheckActions.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/WritePropertiesCheckActions.java
diff --git a/libs/entitlement/qa/test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
diff --git a/libs/entitlement/qa/test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
similarity index 100%
rename from libs/entitlement/qa/test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
rename to libs/entitlement/qa/entitlement-test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java
diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java
index c9eb4ea665aa..54628fc674d7 100644
--- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java
+++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java
@@ -28,7 +28,7 @@ public class EntitlementsAllowedIT extends ESRestTestCase {
@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
- .module("test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, ALLOWED_ENTITLEMENTS))
+ .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, ALLOWED_ENTITLEMENTS))
.systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false")
.build();
diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java
index 5c56774ca906..8390f0e5fd11 100644
--- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java
+++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java
@@ -28,7 +28,7 @@ public class EntitlementsAllowedNonModularIT extends ESRestTestCase {
@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
- .module("test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, ALLOWED_ENTITLEMENTS))
+ .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, ALLOWED_ENTITLEMENTS))
.systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false")
.build();
diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java
index fac70e3167f4..3405e41897cc 100644
--- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java
+++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java
@@ -26,7 +26,7 @@ public class EntitlementsDeniedIT extends ESRestTestCase {
@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
- .module("test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, null))
+ .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, null))
.systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false")
// Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml
diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java
index d48e23e6332e..a2a4773bf752 100644
--- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java
+++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java
@@ -26,7 +26,7 @@ public class EntitlementsDeniedNonModularIT extends ESRestTestCase {
@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
- .module("test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, null))
+ .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, null))
.systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false")
// Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml
diff --git a/muted-tests.yml b/muted-tests.yml
index a603e7fc2e39..c591e19c373d 100644
--- a/muted-tests.yml
+++ b/muted-tests.yml
@@ -233,12 +233,6 @@ tests:
- class: org.elasticsearch.xpack.inference.DefaultEndPointsIT
method: testMultipleInferencesTriggeringDownloadAndDeploy
issue: https://github.com/elastic/elasticsearch/issues/120668
-- class: org.elasticsearch.entitlement.qa.EntitlementsAllowedIT
- issue: https://github.com/elastic/elasticsearch/issues/120674
-- class: org.elasticsearch.entitlement.qa.EntitlementsDeniedNonModularIT
- issue: https://github.com/elastic/elasticsearch/issues/120675
-- class: org.elasticsearch.entitlement.qa.EntitlementsDeniedIT
- issue: https://github.com/elastic/elasticsearch/issues/120676
# Examples:
#
From 443f0f3ded3ef76b85fc3f91f0c3f75357f47c63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?=
Date: Thu, 23 Jan 2025 14:41:12 +0100
Subject: [PATCH 10/29] [DOCS] Adds note about differences between chat
completion and stream API (#120636)
---
docs/reference/inference/chat-completion-inference.asciidoc | 6 +++++-
docs/reference/inference/stream-inference.asciidoc | 4 ++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/reference/inference/chat-completion-inference.asciidoc b/docs/reference/inference/chat-completion-inference.asciidoc
index 83a8f94634f2..1d7d05b0f7d8 100644
--- a/docs/reference/inference/chat-completion-inference.asciidoc
+++ b/docs/reference/inference/chat-completion-inference.asciidoc
@@ -34,9 +34,13 @@ However, if you do not plan to use the {infer} APIs to use these models or if yo
The chat completion {infer} API enables real-time responses for chat completion tasks by delivering answers incrementally, reducing response times during computation.
It only works with the `chat_completion` task type for `openai` and `elastic` {infer} services.
+
[NOTE]
====
-The `chat_completion` task type is only available within the _unified API and only supports streaming.
+* The `chat_completion` task type is only available within the _unified API and only supports streaming.
+* The Chat completion {infer} API and the Stream {infer} API differ in their response structure and capabilities.
+The Chat completion {infer} API provides more comprehensive customization options through more fields and function calling support.
+If you use the `openai` service or the `elastic` service, use the Chat completion {infer} API.
====
[discrete]
diff --git a/docs/reference/inference/stream-inference.asciidoc b/docs/reference/inference/stream-inference.asciidoc
index 4a3ce3190971..bfcead654258 100644
--- a/docs/reference/inference/stream-inference.asciidoc
+++ b/docs/reference/inference/stream-inference.asciidoc
@@ -40,6 +40,10 @@ However, if you do not plan to use the {infer} APIs to use these models or if yo
The stream {infer} API enables real-time responses for completion tasks by delivering answers incrementally, reducing response times during computation.
It only works with the `completion` and `chat_completion` task types.
+The Chat completion {infer} API and the Stream {infer} API differ in their response structure and capabilities.
+The Chat completion {infer} API provides more comprehensive customization options through more fields and function calling support.
+If you use the `openai` service or the `elastic` service, use the Chat completion {infer} API.
+
[NOTE]
====
include::inference-shared.asciidoc[tag=chat-completion-docs]
From dd5e467f8558cc70c619f7eaf1501adba1d5528e Mon Sep 17 00:00:00 2001
From: Nik Everett
Date: Thu, 23 Jan 2025 08:41:45 -0500
Subject: [PATCH 11/29] ESQL: More tests for LOOKUP (#120656)
Expand the integration tests for LOOKUP a little.
---
.../xpack/esql/action/LookupFromIndexIT.java | 65 ++++++++++---------
1 file changed, 36 insertions(+), 29 deletions(-)
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
index 13abadd66769..15bbc06836de 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
@@ -20,10 +20,8 @@ import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.MockBigArrays;
import org.elasticsearch.common.util.MockPageCacheRecycler;
+import org.elasticsearch.compute.data.Block;
import org.elasticsearch.compute.data.BlockFactory;
-import org.elasticsearch.compute.data.BytesRefBlock;
-import org.elasticsearch.compute.data.BytesRefVector;
-import org.elasticsearch.compute.data.ElementType;
import org.elasticsearch.compute.data.LongBlock;
import org.elasticsearch.compute.data.LongVector;
import org.elasticsearch.compute.lucene.DataPartitioning;
@@ -34,6 +32,7 @@ import org.elasticsearch.compute.operator.Driver;
import org.elasticsearch.compute.operator.DriverContext;
import org.elasticsearch.compute.operator.DriverRunner;
import org.elasticsearch.compute.operator.PageConsumerOperator;
+import org.elasticsearch.compute.test.BlockTestUtils;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.IndexSettings;
@@ -53,6 +52,7 @@ import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.enrich.LookupFromIndexOperator;
import org.elasticsearch.xpack.esql.planner.EsPhysicalOperationProviders;
+import org.elasticsearch.xpack.esql.planner.PlannerUtils;
import org.elasticsearch.xpack.esql.plugin.EsqlPlugin;
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
import org.elasticsearch.xpack.esql.plugin.TransportEsqlQueryAction;
@@ -68,21 +68,22 @@ import java.util.concurrent.CopyOnWriteArrayList;
import static org.elasticsearch.test.ListMatcher.matchesList;
import static org.elasticsearch.test.MapMatcher.assertMap;
import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasSize;
public class LookupFromIndexIT extends AbstractEsqlIntegTestCase {
- // TODO should we remove this now that this is integrated into ESQL proper?
- /**
- * Quick and dirty test for looking up data from a lookup index.
- */
- public void testLookupIndex() throws IOException {
- runLookup(new UsingSingleLookupTable(new String[] { "aa", "bb", "cc", "dd" }));
+ public void testKeywordKey() throws IOException {
+ runLookup(DataType.KEYWORD, new UsingSingleLookupTable(new String[] { "aa", "bb", "cc", "dd" }));
+ }
+
+ public void testLongKey() throws IOException {
+ runLookup(DataType.LONG, new UsingSingleLookupTable(new Long[] { 12L, 33L, 1L }));
}
/**
- * Tests when multiple results match.
+ * LOOKUP multiple results match.
*/
public void testLookupIndexMultiResults() throws IOException {
- runLookup(new UsingSingleLookupTable(new String[] { "aa", "bb", "bb", "dd" }));
+ runLookup(DataType.KEYWORD, new UsingSingleLookupTable(new String[] { "aa", "bb", "bb", "dd" }));
}
interface PopulateIndices {
@@ -90,10 +91,10 @@ public class LookupFromIndexIT extends AbstractEsqlIntegTestCase {
}
class UsingSingleLookupTable implements PopulateIndices {
- private final Map> matches = new HashMap<>();
- private final String[] lookupData;
+ private final Map
*/
- private static void writeToTempDir(String subdir, String str, String extension) throws IOException {
+ private static void writeToTempDir(String subdir, String name, String extension, String str) throws IOException {
// We have to write to a tempdir because it's all test are allowed to write to. Gradle can move them.
Path dir = PathUtils.get(System.getProperty("java.io.tmpdir")).resolve("esql").resolve("functions").resolve(subdir);
Files.createDirectories(dir);
- Path file = dir.resolve(functionName() + "." + extension);
+ Path file = dir.resolve(name + "." + extension);
Files.writeString(file, str);
LogManager.getLogger(getTestClass()).info("Wrote to file: {}", file);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
index 589477a8bebd..26340be22408 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
@@ -20,7 +20,9 @@ import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.junit.AfterClass;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@@ -150,4 +152,9 @@ public class RLikeTests extends AbstractScalarFunctionTestCase {
? new RLike(source, expression, new RLikePattern(patternString), true)
: new RLike(source, expression, new RLikePattern(patternString));
}
+
+ @AfterClass
+ public static void renderNotRLike() throws IOException {
+ WildcardLikeTests.renderNot(constructorWithFunctionInfo(RLike.class), "RLIKE", d -> d);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
index e60c5f77ab42..7f04f076ed15 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
@@ -9,6 +9,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.string;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+import com.unboundid.util.NotNull;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.xpack.esql.core.expression.Expression;
@@ -18,11 +19,19 @@ import org.elasticsearch.xpack.esql.core.expression.predicate.regex.WildcardPatt
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
+import org.elasticsearch.xpack.esql.expression.function.Example;
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
import org.elasticsearch.xpack.esql.expression.function.FunctionName;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.junit.AfterClass;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
import java.util.function.Supplier;
import static org.hamcrest.Matchers.equalTo;
@@ -87,4 +96,67 @@ public class WildcardLikeTests extends AbstractScalarFunctionTestCase {
}
return new WildcardLike(source, expression, new WildcardPattern(((BytesRef) pattern.fold(FoldContext.small())).utf8ToString()));
}
+
+ @AfterClass
+ public static void renderNotLike() throws IOException {
+ renderNot(constructorWithFunctionInfo(WildcardLike.class), "LIKE", d -> d);
+ }
+
+ public static void renderNot(@NotNull Constructor> ctor, String name, Function description) throws IOException {
+ FunctionInfo orig = ctor.getAnnotation(FunctionInfo.class);
+ assert orig != null;
+ FunctionInfo functionInfo = new FunctionInfo() {
+ @Override
+ public Class extends Annotation> annotationType() {
+ return orig.annotationType();
+ }
+
+ @Override
+ public String operator() {
+ return "NOT " + name;
+ }
+
+ @Override
+ public String[] returnType() {
+ return orig.returnType();
+ }
+
+ @Override
+ public boolean preview() {
+ return orig.preview();
+ }
+
+ @Override
+ public String description() {
+ return description.apply(orig.description().replace(name, "NOT " + name));
+ }
+
+ @Override
+ public String detailedDescription() {
+ return "";
+ }
+
+ @Override
+ public String note() {
+ return orig.note().replace(name, "NOT " + name);
+ }
+
+ @Override
+ public String appendix() {
+ return orig.appendix().replace(name, "NOT " + name);
+ }
+
+ @Override
+ public boolean isAggregation() {
+ return orig.isAggregation();
+ }
+
+ @Override
+ public Example[] examples() {
+ // throw away examples
+ return new Example[] {};
+ }
+ };
+ renderDocsForOperators("not_" + name.toLowerCase(Locale.ENGLISH), ctor, functionInfo);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
index 80f67ec8e5e3..03a4b063d629 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
@@ -19,7 +19,10 @@ import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase;
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.WildcardLikeTests;
+import org.junit.AfterClass;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -333,4 +336,14 @@ public class InTests extends AbstractFunctionTestCase {
protected Expression build(Source source, List args) {
return new In(source, args.get(args.size() - 1), args.subList(0, args.size() - 1));
}
+
+ @AfterClass
+ public static void renderNotIn() throws IOException {
+ WildcardLikeTests.renderNot(
+ constructorWithFunctionInfo(In.class),
+ "IN",
+ d -> "The `NOT IN` operator allows testing whether a field or expression does *not* equal any element "
+ + "in a list of literals, fields or expressions."
+ );
+ }
}
From 787a16d0d5f01502b1d73f649d7d9f15de9d8c69 Mon Sep 17 00:00:00 2001
From: Simon Cooper
Date: Thu, 23 Jan 2025 15:59:19 +0000
Subject: [PATCH 20/29] Update the index version compatible test to only check
the minimum (#120406)
---
.../test/index/IndexVersionUtilsTests.java | 22 ++++++-------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java b/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java
index 53758c165a3c..05e8a93ad99e 100644
--- a/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java
+++ b/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java
@@ -9,7 +9,6 @@
package org.elasticsearch.test.index;
-import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.test.ESTestCase;
@@ -19,26 +18,19 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import static org.hamcrest.Matchers.equalTo;
+
public class IndexVersionUtilsTests extends ESTestCase {
/**
* Tests that {@link IndexVersions#MINIMUM_COMPATIBLE} and {@link IndexVersionUtils#allReleasedVersions()}
- * agree with the list of index compatible versions we build in gradle.
+ * agree on the minimum version that should be tested.
*/
- @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98054")
- public void testGradleVersionsMatchVersionUtils() {
+ public void testIndexCompatibleVersionMatches() {
VersionsFromProperty indexCompatible = new VersionsFromProperty("tests.gradle_index_compat_versions");
- List released = IndexVersionUtils.allReleasedVersions()
- .stream()
- /* Java lists all versions from the 5.x series onwards, but we only want to consider
- * ones that we're supposed to be compatible with. */
- .filter(v -> v.onOrAfter(IndexVersions.MINIMUM_COMPATIBLE))
- .toList();
- List releasedIndexCompatible = released.stream()
- .filter(v -> IndexVersion.current().equals(v) == false)
- .map(Object::toString)
- .toList();
- assertEquals(releasedIndexCompatible, indexCompatible.released);
+ String minIndexVersion = IndexVersions.MINIMUM_COMPATIBLE.toReleaseVersion();
+ String lowestCompatibleVersion = indexCompatible.released.get(0);
+ assertThat(lowestCompatibleVersion, equalTo(minIndexVersion));
}
/**
From 9a9bc698838a6e1fee8b079fa240a18f95859107 Mon Sep 17 00:00:00 2001
From: Ignacio Vera
Date: Thu, 23 Jan 2025 17:28:52 +0100
Subject: [PATCH 21/29] Stop caching source map on SearchHit#getSourceMap
(#119888)
This call has the side effect that if you are iterating a number of hits calling this method, you will be increasing the
memory usage by a non trivial number which in most of cases is unwanted. Therefore this commit removes this caching
all together and add an assertion so the method is call once during the lifetime of the object.
---
.../join/query/ChildQuerySearchIT.java | 15 +-
.../aggregations/metrics/TopHitsIT.java | 10 +-
.../search/fetch/subphase/InnerHitsIT.java | 42 ++--
.../search/searchafter/SearchAfterIT.java | 21 +-
.../search/sort/FieldSortIT.java | 6 +-
.../search/source/SourceFetchingIT.java | 12 +-
.../suggest/CompletionSuggestSearchIT.java | 5 +-
.../org/elasticsearch/search/SearchHit.java | 22 +-
.../aggregations/metrics/InternalTopHits.java | 2 +
.../elasticsearch/search/SearchHitTests.java | 2 -
.../search/SearchResponseUtils.java | 1 -
.../xpack/enrich/EnrichCache.java | 5 +-
.../EnrichShardMultiSearchActionTests.java | 9 +-
.../rules/QueryRulesIndexService.java | 6 +-
.../registry/ModelRegistryTests.java | 1 -
.../extractor/scroll/ScrollDataExtractor.java | 8 +-
.../scroll/SearchHitToJsonProcessor.java | 5 +-
.../scroll/TimeBasedExtractedFields.java | 5 +-
.../extractor/DataFrameDataExtractor.java | 33 +--
.../dataframe/inference/InferenceRunner.java | 17 +-
.../process/DataFrameRowsJoiner.java | 10 +-
.../xpack/ml/extractor/DocValueField.java | 2 +-
.../xpack/ml/extractor/ExtractedField.java | 6 +-
.../xpack/ml/extractor/ExtractedFields.java | 4 +-
.../xpack/ml/extractor/GeoPointField.java | 4 +-
.../xpack/ml/extractor/GeoShapeField.java | 4 +-
.../xpack/ml/extractor/MultiField.java | 4 +-
.../xpack/ml/extractor/ProcessedField.java | 4 +-
.../xpack/ml/extractor/ScriptField.java | 2 +-
.../xpack/ml/extractor/SourceField.java | 13 +-
.../xpack/ml/extractor/SourceSupplier.java | 34 +++
.../xpack/ml/extractor/TimeField.java | 2 +-
.../scroll/SearchHitToJsonProcessorTests.java | 3 +-
.../scroll/TimeBasedExtractedFieldsTests.java | 11 +-
.../DataFrameDataExtractorTests.java | 2 +-
.../ExtractedFieldsDetectorTests.java | 8 +-
.../process/DataFrameRowsJoinerTests.java | 1 +
.../ml/extractor/DocValueFieldTests.java | 8 +-
.../ml/extractor/ExtractedFieldsTests.java | 12 +-
.../ml/extractor/GeoPointFieldTests.java | 6 +-
.../ml/extractor/GeoShapeFieldTests.java | 8 +-
.../xpack/ml/extractor/MultiFieldTests.java | 4 +-
.../ml/extractor/ProcessedFieldTests.java | 8 +-
.../xpack/ml/extractor/ScriptFieldTests.java | 8 +-
.../xpack/ml/extractor/SourceFieldTests.java | 10 +-
.../xpack/ml/extractor/TimeFieldTests.java | 8 +-
.../monitoring/integration/MonitoringIT.java | 25 ++-
.../LocalExporterResourceIntegTests.java | 8 +-
.../DocumentAndFieldLevelSecurityTests.java | 52 +++--
.../DocumentLevelSecurityTests.java | 66 +++---
.../integration/FieldLevelSecurityTests.java | 203 ++++++++++--------
.../xpack/security/authc/TokenService.java | 6 +-
.../transform/transforms/latest/Latest.java | 8 +-
.../integration/RejectedExecutionTests.java | 14 +-
.../transform/TransformIntegrationTests.java | 20 +-
55 files changed, 461 insertions(+), 354 deletions(-)
create mode 100644 x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/extractor/SourceSupplier.java
diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
index cce0ef06cbf6..96802ac76295 100644
--- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
+++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
@@ -184,8 +184,9 @@ public class ChildQuerySearchIT extends ParentChildTestCase {
assertNoFailuresAndResponse(prepareSearch("test").setQuery(idsQuery().addIds("c1")), response -> {
assertThat(response.getHits().getTotalHits().value(), equalTo(1L));
assertThat(response.getHits().getAt(0).getId(), equalTo("c1"));
- assertThat(extractValue("join_field.name", response.getHits().getAt(0).getSourceAsMap()), equalTo("child"));
- assertThat(extractValue("join_field.parent", response.getHits().getAt(0).getSourceAsMap()), equalTo("p1"));
+ Map source = response.getHits().getAt(0).getSourceAsMap();
+ assertThat(extractValue("join_field.name", source), equalTo("child"));
+ assertThat(extractValue("join_field.parent", source), equalTo("p1"));
});
@@ -197,11 +198,13 @@ public class ChildQuerySearchIT extends ParentChildTestCase {
response -> {
assertThat(response.getHits().getTotalHits().value(), equalTo(2L));
assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2")));
- assertThat(extractValue("join_field.name", response.getHits().getAt(0).getSourceAsMap()), equalTo("child"));
- assertThat(extractValue("join_field.parent", response.getHits().getAt(0).getSourceAsMap()), equalTo("p1"));
+ Map source0 = response.getHits().getAt(0).getSourceAsMap();
+ assertThat(extractValue("join_field.name", source0), equalTo("child"));
+ assertThat(extractValue("join_field.parent", source0), equalTo("p1"));
assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2")));
- assertThat(extractValue("join_field.name", response.getHits().getAt(1).getSourceAsMap()), equalTo("child"));
- assertThat(extractValue("join_field.parent", response.getHits().getAt(1).getSourceAsMap()), equalTo("p1"));
+ Map source1 = response.getHits().getAt(1).getSourceAsMap();
+ assertThat(extractValue("join_field.name", source1), equalTo("child"));
+ assertThat(extractValue("join_field.parent", source1), equalTo("p1"));
}
);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
index affa371d92aa..c246b7cc2f5c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
@@ -655,8 +655,9 @@ public class TopHitsIT extends ESIntegTestCase {
assertThat(hit.field("field2").getValue(), equalTo(2.71f));
assertThat(hit.field("script").getValue().toString(), equalTo("5"));
- assertThat(hit.getSourceAsMap().size(), equalTo(1));
- assertThat(hit.getSourceAsMap().get("text").toString(), equalTo("some text to entertain"));
+ Map source = hit.getSourceAsMap();
+ assertThat(source.size(), equalTo(1));
+ assertThat(source.get("text").toString(), equalTo("some text to entertain"));
assertEquals("some text to entertain", hit.getFields().get("text").getValue());
assertEquals("some text to entertain", hit.getFields().get("text_stored_lookup").getValue());
}
@@ -927,8 +928,9 @@ public class TopHitsIT extends ESIntegTestCase {
field = searchHit.field("script");
assertThat(field.getValue().toString(), equalTo("5"));
- assertThat(searchHit.getSourceAsMap().size(), equalTo(1));
- assertThat(extractValue("message", searchHit.getSourceAsMap()), equalTo("some comment"));
+ Map source = searchHit.getSourceAsMap();
+ assertThat(source.size(), equalTo(1));
+ assertThat(extractValue("message", source), equalTo("some comment"));
}
);
}
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
index e39f8df9bad3..edc0c65bc773 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
@@ -490,8 +490,9 @@ public class InnerHitsIT extends ESIntegTestCase {
response -> {
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comments");
innerHits = innerHits.getAt(0).getInnerHits().get("remark");
- assertNotNull(innerHits.getAt(0).getSourceAsMap());
- assertFalse(innerHits.getAt(0).getSourceAsMap().isEmpty());
+ Map source = innerHits.getAt(0).getSourceAsMap();
+ assertNotNull(source);
+ assertFalse(source.isEmpty());
}
);
assertNoFailuresAndResponse(
@@ -507,8 +508,9 @@ public class InnerHitsIT extends ESIntegTestCase {
response -> {
SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comments");
innerHits = innerHits.getAt(0).getInnerHits().get("remark");
- assertNotNull(innerHits.getAt(0).getSourceAsMap());
- assertFalse(innerHits.getAt(0).getSourceAsMap().isEmpty());
+ Map source = innerHits.getAt(0).getSourceAsMap();
+ assertNotNull(source);
+ assertFalse(source.isEmpty());
}
);
}
@@ -845,16 +847,12 @@ public class InnerHitsIT extends ESIntegTestCase {
assertHitCount(response, 1);
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits().value(), equalTo(2L));
- assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(1));
- assertThat(
- response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().get("message"),
- equalTo("fox eat quick")
- );
- assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap().size(), equalTo(1));
- assertThat(
- response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap().get("message"),
- equalTo("fox ate rabbit x y z")
- );
+ Map source0 = response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap();
+ assertThat(source0.size(), equalTo(1));
+ assertThat(source0.get("message"), equalTo("fox eat quick"));
+ Map source1 = response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap();
+ assertThat(source1.size(), equalTo(1));
+ assertThat(source1.get("message"), equalTo("fox ate rabbit x y z"));
}
);
@@ -866,16 +864,12 @@ public class InnerHitsIT extends ESIntegTestCase {
assertHitCount(response, 1);
assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits().value(), equalTo(2L));
- assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(2));
- assertThat(
- response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().get("message"),
- equalTo("fox eat quick")
- );
- assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(2));
- assertThat(
- response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap().get("message"),
- equalTo("fox ate rabbit x y z")
- );
+ Map source0 = response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap();
+ assertThat(source0.size(), equalTo(2));
+ assertThat(source0.get("message"), equalTo("fox eat quick"));
+ Map source1 = response.getHits().getAt(0).getInnerHits().get("comments").getAt(1).getSourceAsMap();
+ assertThat(source1.size(), equalTo(2));
+ assertThat(source1.get("message"), equalTo("fox ate rabbit x y z"));
}
);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java
index 353858e9d697..29f56eeb5ecb 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java
@@ -47,6 +47,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@@ -152,8 +153,9 @@ public class SearchAfterIT extends ESIntegTestCase {
searchResponse -> {
assertThat(searchResponse.getHits().getTotalHits().value(), Matchers.equalTo(2L));
assertThat(searchResponse.getHits().getHits().length, Matchers.equalTo(1));
- assertThat(searchResponse.getHits().getHits()[0].getSourceAsMap().get("field1"), Matchers.equalTo(100));
- assertThat(searchResponse.getHits().getHits()[0].getSourceAsMap().get("field2"), Matchers.equalTo("toto"));
+ Map source = searchResponse.getHits().getHits()[0].getSourceAsMap();
+ assertThat(source.get("field1"), Matchers.equalTo(100));
+ assertThat(source.get("field2"), Matchers.equalTo("toto"));
}
);
}
@@ -438,8 +440,9 @@ public class SearchAfterIT extends ESIntegTestCase {
int foundHits = 0;
do {
for (SearchHit hit : resp.getHits().getHits()) {
- assertNotNull(hit.getSourceAsMap());
- final Object timestamp = hit.getSourceAsMap().get("timestamp");
+ Map source = hit.getSourceAsMap();
+ assertNotNull(source);
+ final Object timestamp = source.get("timestamp");
assertNotNull(timestamp);
assertThat(((Number) timestamp).longValue(), equalTo(timestamps.get(foundHits)));
foundHits++;
@@ -469,8 +472,9 @@ public class SearchAfterIT extends ESIntegTestCase {
do {
Object[] after = null;
for (SearchHit hit : resp.getHits().getHits()) {
- assertNotNull(hit.getSourceAsMap());
- final Object timestamp = hit.getSourceAsMap().get("timestamp");
+ Map source = hit.getSourceAsMap();
+ assertNotNull(source);
+ final Object timestamp = source.get("timestamp");
assertNotNull(timestamp);
assertThat(((Number) timestamp).longValue(), equalTo(timestamps.get(foundHits)));
after = hit.getSortValues();
@@ -505,8 +509,9 @@ public class SearchAfterIT extends ESIntegTestCase {
do {
Object[] after = null;
for (SearchHit hit : resp.getHits().getHits()) {
- assertNotNull(hit.getSourceAsMap());
- final Object timestamp = hit.getSourceAsMap().get("timestamp");
+ Map source = hit.getSourceAsMap();
+ assertNotNull(source);
+ final Object timestamp = source.get("timestamp");
assertNotNull(timestamp);
foundSeqNos.add(((Number) timestamp).longValue());
after = hit.getSortValues();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
index f407c14c48c5..7fd31b056779 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
@@ -130,10 +130,11 @@ public class FieldSortIT extends ESIntegTestCase {
.setSize(10),
response -> {
logClusterState();
+ Number previous = (Number) response.getHits().getHits()[0].getSourceAsMap().get("entry");
for (int j = 1; j < response.getHits().getHits().length; j++) {
Number current = (Number) response.getHits().getHits()[j].getSourceAsMap().get("entry");
- Number previous = (Number) response.getHits().getHits()[j - 1].getSourceAsMap().get("entry");
assertThat(response.toString(), current.intValue(), lessThan(previous.intValue()));
+ previous = current;
}
}
);
@@ -144,10 +145,11 @@ public class FieldSortIT extends ESIntegTestCase {
.setSize(10),
response -> {
logClusterState();
+ Number previous = (Number) response.getHits().getHits()[0].getSourceAsMap().get("entry");
for (int j = 1; j < response.getHits().getHits().length; j++) {
Number current = (Number) response.getHits().getHits()[j].getSourceAsMap().get("entry");
- Number previous = (Number) response.getHits().getHits()[j - 1].getSourceAsMap().get("entry");
assertThat(response.toString(), current.intValue(), greaterThan(previous.intValue()));
+ previous = current;
}
}
);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java
index 0e7f8b604a8d..0b1d665f4f3e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java
@@ -11,6 +11,8 @@ package org.elasticsearch.search.source;
import org.elasticsearch.test.ESIntegTestCase;
+import java.util.Map;
+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponses;
import static org.hamcrest.Matchers.notNullValue;
@@ -57,8 +59,9 @@ public class SourceFetchingIT extends ESIntegTestCase {
assertResponses(response -> {
assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue());
- assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1));
- assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field1"), equalTo("value"));
+ Map source = response.getHits().getAt(0).getSourceAsMap();
+ assertThat(source.size(), equalTo(1));
+ assertThat(source.get("field1"), equalTo("value"));
},
prepareSearch("test").setFetchSource("field1", null),
prepareSearch("test").setFetchSource(new String[] { "*" }, new String[] { "field2" })
@@ -84,8 +87,9 @@ public class SourceFetchingIT extends ESIntegTestCase {
assertResponses(response -> {
assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue());
- assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1));
- assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field"), equalTo("value"));
+ Map source = response.getHits().getAt(0).getSourceAsMap();
+ assertThat(source.size(), equalTo(1));
+ assertThat((String) source.get("field"), equalTo("value"));
},
prepareSearch("test").setFetchSource(new String[] { "*.notexisting", "field" }, null),
prepareSearch("test").setFetchSource(new String[] { "field.notexisting.*", "field" }, null)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
index 8b21bb54361b..cb0e91ce57c7 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
@@ -356,8 +356,9 @@ public class CompletionSuggestSearchIT extends ESIntegTestCase {
assertThat(option.getText().toString(), equalTo("suggestion" + id));
assertThat(option.getHit(), hasId("" + id));
assertThat(option.getHit(), hasScore((id)));
- assertNotNull(option.getHit().getSourceAsMap());
- Set sourceFields = option.getHit().getSourceAsMap().keySet();
+ Map source = option.getHit().getSourceAsMap();
+ assertNotNull(source);
+ Set sourceFields = source.keySet();
assertThat(sourceFields, contains("a"));
assertThat(sourceFields, not(contains("b")));
id--;
diff --git a/server/src/main/java/org/elasticsearch/search/SearchHit.java b/server/src/main/java/org/elasticsearch/search/SearchHit.java
index 701d451ac2c1..8a70f8a7f41a 100644
--- a/server/src/main/java/org/elasticsearch/search/SearchHit.java
+++ b/server/src/main/java/org/elasticsearch/search/SearchHit.java
@@ -104,7 +104,8 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
private transient String index;
private transient String clusterAlias;
- private Map sourceAsMap;
+ // For asserting that the method #getSourceAsMap is called just once on the lifetime of this object
+ private boolean sourceAsMapCalled = false;
private Map innerHits;
@@ -142,7 +143,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
null,
null,
null,
- null,
new HashMap<>(),
new HashMap<>(),
refCounted
@@ -166,7 +166,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
SearchShardTarget shard,
String index,
String clusterAlias,
- Map sourceAsMap,
Map innerHits,
Map documentFields,
Map metaFields,
@@ -188,7 +187,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
this.shard = shard;
this.index = index;
this.clusterAlias = clusterAlias;
- this.sourceAsMap = sourceAsMap;
this.innerHits = innerHits;
this.documentFields = documentFields;
this.metaFields = metaFields;
@@ -279,7 +277,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
shardTarget,
index,
clusterAlias,
- null,
innerHits,
documentFields,
metaFields,
@@ -447,7 +444,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
*/
public SearchHit sourceRef(BytesReference source) {
this.source = source;
- this.sourceAsMap = null;
return this;
}
@@ -476,19 +472,18 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
}
/**
- * The source of the document as a map (can be {@code null}).
+ * The source of the document as a map (can be {@code null}). This method is expected
+ * to be called at most once during the lifetime of the object as the generated map
+ * is expensive to generate and it does not get cache.
*/
public Map getSourceAsMap() {
assert hasReferences();
+ assert sourceAsMapCalled == false : "getSourceAsMap() called twice";
+ sourceAsMapCalled = true;
if (source == null) {
return null;
}
- if (sourceAsMap != null) {
- return sourceAsMap;
- }
-
- sourceAsMap = Source.fromBytes(source).source();
- return sourceAsMap;
+ return Source.fromBytes(source).source();
}
/**
@@ -758,7 +753,6 @@ public final class SearchHit implements Writeable, ToXContentObject, RefCounted
shard,
index,
clusterAlias,
- sourceAsMap,
innerHits == null
? null
: innerHits.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().asUnpooled())),
diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java
index 8ff381cbbc84..7e7e10c48ea1 100644
--- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java
+++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java
@@ -235,6 +235,8 @@ public class InternalTopHits extends InternalAggregation implements TopHits {
} else if (tokens[0].equals(SCORE)) {
return topHit.getScore();
} else if (tokens[0].equals(SOURCE)) {
+ // Caching the map might help here but memory usage is a concern for this class
+ // This is dead code, pipeline aggregations do not support _source.field.
Map sourceAsMap = topHit.getSourceAsMap();
if (sourceAsMap != null) {
Object property = sourceAsMap.get(tokens[1]);
diff --git a/server/src/test/java/org/elasticsearch/search/SearchHitTests.java b/server/src/test/java/org/elasticsearch/search/SearchHitTests.java
index 2b082f2f8b02..25a71d04b321 100644
--- a/server/src/test/java/org/elasticsearch/search/SearchHitTests.java
+++ b/server/src/test/java/org/elasticsearch/search/SearchHitTests.java
@@ -317,9 +317,7 @@ public class SearchHitTests extends AbstractWireSerializingTestCase {
assertThat(searchHit.getSourceAsMap(), nullValue());
assertThat(searchHit.getSourceRef(), nullValue());
- assertThat(searchHit.getSourceAsMap(), nullValue());
assertThat(searchHit.getSourceAsString(), nullValue());
- assertThat(searchHit.getSourceAsMap(), nullValue());
assertThat(searchHit.getSourceRef(), nullValue());
assertThat(searchHit.getSourceAsString(), nullValue());
}
diff --git a/test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java b/test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java
index b0edbb829df2..330058b16a81 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/SearchResponseUtils.java
@@ -889,7 +889,6 @@ public enum SearchResponseUtils {
shardTarget,
index,
clusterAlias,
- null,
get(SearchHit.Fields.INNER_HITS, values, null),
get(SearchHit.DOCUMENT_FIELDS, values, Collections.emptyMap()),
get(SearchHit.METADATA_FIELDS, values, Collections.emptyMap()),
diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java
index 400d9f0cc84b..a2899813aa42 100644
--- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java
+++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java
@@ -173,8 +173,11 @@ public final class EnrichCache {
List