|
@@ -9,21 +9,33 @@ package org.elasticsearch.xpack.transform.integration;
|
|
|
|
|
|
import org.apache.http.HttpHost;
|
|
|
import org.elasticsearch.client.Request;
|
|
|
+import org.elasticsearch.client.Response;
|
|
|
import org.elasticsearch.client.ResponseException;
|
|
|
import org.elasticsearch.client.RestClient;
|
|
|
import org.elasticsearch.client.RestClientBuilder;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
|
|
import org.elasticsearch.core.Strings;
|
|
|
+import org.elasticsearch.threadpool.TestThreadPool;
|
|
|
+import org.junit.After;
|
|
|
import org.junit.Before;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.concurrent.Callable;
|
|
|
+import java.util.concurrent.ExecutionException;
|
|
|
+import java.util.concurrent.Future;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
+import static org.hamcrest.Matchers.both;
|
|
|
+import static org.hamcrest.Matchers.containsString;
|
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
|
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
|
|
+import static org.hamcrest.Matchers.instanceOf;
|
|
|
import static org.hamcrest.Matchers.is;
|
|
|
+import static org.hamcrest.Matchers.lessThan;
|
|
|
|
|
|
public class TransformUpdateIT extends TransformRestTestCase {
|
|
|
|
|
@@ -47,6 +59,8 @@ public class TransformUpdateIT extends TransformRestTestCase {
|
|
|
private static final String DATA_ACCESS_ROLE = "test_data_access";
|
|
|
private static final String DATA_ACCESS_ROLE_2 = "test_data_access_2";
|
|
|
|
|
|
+ private TestThreadPool threadPool;
|
|
|
+
|
|
|
// preserve indices in order to reuse source indices in several test cases
|
|
|
@Override
|
|
|
protected boolean preserveIndicesUponCompletion() {
|
|
@@ -76,6 +90,15 @@ public class TransformUpdateIT extends TransformRestTestCase {
|
|
|
setupUser(TEST_ADMIN_USER_NAME_2, List.of("transform_admin", DATA_ACCESS_ROLE_2));
|
|
|
setupUser(TEST_ADMIN_USER_NAME_NO_DATA, List.of("transform_admin"));
|
|
|
createReviewsIndex();
|
|
|
+
|
|
|
+ threadPool = new TestThreadPool(getTestName());
|
|
|
+ }
|
|
|
+
|
|
|
+ @After
|
|
|
+ public void shutdownThreadPool() {
|
|
|
+ if (threadPool != null) {
|
|
|
+ threadPool.shutdown();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@@ -167,6 +190,50 @@ public class TransformUpdateIT extends TransformRestTestCase {
|
|
|
assertThat(updatedConfig.get("settings"), is(equalTo(Map.of("max_page_search_size", 123))));
|
|
|
}
|
|
|
|
|
|
+ public void testConcurrentUpdates() throws Exception {
|
|
|
+ String transformId = "test_concurrent_updates";
|
|
|
+ String destIndex = transformId + "-dest";
|
|
|
+
|
|
|
+ // Create the transform
|
|
|
+ createPivotReviewsTransform(transformId, destIndex, null, null, null);
|
|
|
+
|
|
|
+ // Create a number of concurrent threads competing to update the transform with different settings.
|
|
|
+ int minMaxPageSearchSize = 10;
|
|
|
+ int maxMaxPageSearchSize = 20;
|
|
|
+ List<Callable<Response>> concurrentUpdates = new ArrayList<>(10);
|
|
|
+ for (int maxPageSearchSize = minMaxPageSearchSize; maxPageSearchSize < maxMaxPageSearchSize; ++maxPageSearchSize) {
|
|
|
+ Request updateTransformRequest = createRequestWithAuth("POST", getTransformEndpoint() + transformId + "/_update", null);
|
|
|
+ updateTransformRequest.setJsonEntity(Strings.format("""
|
|
|
+ { "settings": { "max_page_search_size": %s } }""", maxPageSearchSize));
|
|
|
+
|
|
|
+ // Schedule a thread to update the transform's settings
|
|
|
+ concurrentUpdates.add(() -> client().performRequest(updateTransformRequest));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Gather the results.
|
|
|
+ List<Future<Response>> futures = threadPool.generic().invokeAll(concurrentUpdates);
|
|
|
+ for (Future<Response> future : futures) {
|
|
|
+ try { // The update may succeed...
|
|
|
+ future.get();
|
|
|
+ } catch (ExecutionException e) { // ... but if it fails, it's due to conflict
|
|
|
+ assertThat(e.getCause(), instanceOf(ResponseException.class));
|
|
|
+ ResponseException re = (ResponseException) e.getCause();
|
|
|
+ assertThat(re.getResponse().getStatusLine().getStatusCode(), is(equalTo(409)));
|
|
|
+ assertThat(
|
|
|
+ re.getMessage(),
|
|
|
+ containsString("Transform with id [" + transformId + "] got updated in the meantime. Please try again")
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify that the settings got updated. Any of the concurrent threads could have won the competition.
|
|
|
+ Map<String, Object> finalConfig = getTransformConfig(transformId, null);
|
|
|
+ assertThat(
|
|
|
+ (int) XContentMapValues.extractValue(finalConfig, "settings", "max_page_search_size"),
|
|
|
+ is(both(greaterThanOrEqualTo(minMaxPageSearchSize)).and(lessThan(maxMaxPageSearchSize)))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
private void updateTransferRightsTester(boolean useSecondaryAuthHeaders) throws Exception {
|
|
|
String transformId = "transform1";
|
|
|
// Note: Due to a bug the transform does not fail to start after deleting the user and role, therefore invalidating
|