This is an automated email from the ASF dual-hosted git repository.

rpuch pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 843cb32a3e7 IGNITE-27044 Extract tests restarting nodes from 
ItBuildIndexTest (#6966)
843cb32a3e7 is described below

commit 843cb32a3e742d02bcf6b1ff682ddf6fb93a74b5
Author: Roman Puchkovskiy <[email protected]>
AuthorDate: Thu Nov 13 18:43:41 2025 +0400

    IGNITE-27044 Extract tests restarting nodes from ItBuildIndexTest (#6966)
---
 .../internal/index/ItBuildIndexOneNodeTest.java    |  10 +-
 .../ignite/internal/index/ItBuildIndexTest.java    | 141 +------------
 .../ItBuildIndexWriteIntentsHandlingTest.java      | 230 +++++++++++++++++++++
 .../internal/index/WriteIntentSwitchControl.java   |  31 +++
 .../internal/ClusterPerClassIntegrationTest.java   |   2 +-
 5 files changed, 269 insertions(+), 145 deletions(-)

diff --git 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexOneNodeTest.java
 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexOneNodeTest.java
index 6b38736040e..6b6d68d5218 100644
--- 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexOneNodeTest.java
+++ 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexOneNodeTest.java
@@ -21,6 +21,7 @@ import static 
java.util.concurrent.CompletableFuture.failedFuture;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
 import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
+import static 
org.apache.ignite.internal.index.WriteIntentSwitchControl.disableWriteIntentSwitchExecution;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static 
org.apache.ignite.internal.sql.engine.util.QueryChecker.containsIndexScan;
 import static org.apache.ignite.internal.table.TableTestUtils.getIndexStrict;
@@ -60,7 +61,6 @@ import org.apache.ignite.internal.storage.index.IndexRow;
 import org.apache.ignite.internal.storage.index.IndexStorage;
 import org.apache.ignite.internal.storage.index.SortedIndexStorage;
 import org.apache.ignite.internal.table.TableViewInternal;
-import org.apache.ignite.internal.tx.message.WriteIntentSwitchReplicaRequest;
 import org.apache.ignite.internal.util.Cursor;
 import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.table.Table;
@@ -348,7 +348,7 @@ public class ItBuildIndexOneNodeTest extends 
BaseSqlIntegrationTest {
     void writeIntentFromCommittedTxShouldBeIndexed() throws Exception {
         createZoneAndTable(ZONE_NAME, TABLE_NAME, 1, 1);
 
-        disableWriteIntentSwitchExecution();
+        disableWriteIntentSwitchExecution(CLUSTER);
 
         Transaction tx = node().transactions().begin();
         insertDataInTransaction(tx, TABLE_NAME, List.of("ID", "NAME", 
"SALARY"), new Object[]{0, "0", 10.0});
@@ -371,7 +371,7 @@ public class ItBuildIndexOneNodeTest extends 
BaseSqlIntegrationTest {
     void writeIntentFromAbortedTxShouldNotBeIndexed() throws Exception {
         createZoneAndTable(ZONE_NAME, TABLE_NAME, 1, 1);
 
-        disableWriteIntentSwitchExecution();
+        disableWriteIntentSwitchExecution(CLUSTER);
 
         Transaction tx = node().transactions().begin();
         insertDataInTransaction(tx, TABLE_NAME, List.of("ID", "NAME", 
"SALARY"), new Object[]{0, "0", 10.0});
@@ -390,10 +390,6 @@ public class ItBuildIndexOneNodeTest extends 
BaseSqlIntegrationTest {
         assertFalse(hasSomethingInIndex, "Nothing should have been put to the 
index");
     }
 
-    private static void disableWriteIntentSwitchExecution() {
-        node().dropMessages((recipientId, message) -> message instanceof 
WriteIntentSwitchReplicaRequest);
-    }
-
     private static IgniteImpl node() {
         return unwrapIgniteImpl(CLUSTER.node(0));
     }
diff --git 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexTest.java
 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexTest.java
index 9bc5cbe05db..bb2eaa49e4e 100644
--- 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexTest.java
+++ 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexTest.java
@@ -28,7 +28,6 @@ import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static 
org.apache.ignite.internal.lang.IgniteSystemProperties.colocationEnabled;
 import static 
org.apache.ignite.internal.sql.engine.util.QueryChecker.containsIndexScan;
-import static org.apache.ignite.internal.table.TableTestUtils.getIndexStrict;
 import static 
org.apache.ignite.internal.table.distributed.storage.InternalTableImpl.AWAIT_PRIMARY_REPLICA_TIMEOUT;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
@@ -37,7 +36,6 @@ import static org.awaitility.Awaitility.await;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import java.util.HashMap;
@@ -57,7 +55,6 @@ import 
org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.hlc.HybridClock;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
-import 
org.apache.ignite.internal.index.message.IsNodeFinishedRwTransactionsStartedBeforeRequest;
 import org.apache.ignite.internal.network.NetworkMessage;
 import 
org.apache.ignite.internal.partition.replicator.network.command.BuildIndexCommand;
 import org.apache.ignite.internal.partitiondistribution.Assignment;
@@ -70,22 +67,15 @@ import 
org.apache.ignite.internal.replicator.TablePartitionId;
 import org.apache.ignite.internal.replicator.ZonePartitionId;
 import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
 import org.apache.ignite.internal.sql.SqlCommon;
-import org.apache.ignite.internal.storage.StorageException;
-import org.apache.ignite.internal.storage.index.IndexRow;
 import org.apache.ignite.internal.storage.index.IndexStorage;
-import org.apache.ignite.internal.storage.index.SortedIndexStorage;
 import org.apache.ignite.internal.table.InternalTable;
 import org.apache.ignite.internal.table.NodeUtils;
 import org.apache.ignite.internal.table.TableViewInternal;
-import org.apache.ignite.internal.tx.message.WriteIntentSwitchReplicaRequest;
-import org.apache.ignite.internal.util.Cursor;
 import org.apache.ignite.raft.jraft.rpc.WriteActionRequest;
 import org.apache.ignite.table.Table;
-import org.apache.ignite.tx.Transaction;
 import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
@@ -223,129 +213,6 @@ public class ItBuildIndexTest extends 
BaseSqlIntegrationTest {
         assertThat(sendBuildIndexCommandFuture, willSucceedFast());
     }
 
-    @Test
-    void writeIntentFromTxAbandonedBeforeShouldNotBeIndexed() throws Exception 
{
-        createTable(1, 1);
-
-        disableWriteIntentSwitchExecution();
-
-        // Create and abandon a transaction.
-        int txCoordinatorOrdinal = 2;
-        Transaction tx = 
CLUSTER.node(txCoordinatorOrdinal).transactions().begin();
-        insertDataInTransaction(tx, TABLE_NAME, List.of("I0", "I1"), new 
Object[]{1, 1});
-
-        CLUSTER.restartNode(txCoordinatorOrdinal);
-
-        createIndex(INDEX_NAME);
-        await("Index did not become available in time")
-                .atMost(10, SECONDS)
-                .until(() -> 
isIndexAvailable(unwrapIgniteImpl(CLUSTER.aliveNode()), INDEX_NAME));
-
-        verifyNoNodesHaveAnythingInIndex();
-    }
-
-    @Test
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26811";)
-    void 
writeIntentFromTxAbandonedWhileWaitingForTransactionsToFinishShouldNotBeIndexed()
 throws Exception {
-        createTable(1, 1);
-
-        // Both disable write intent switch execution and track when we start 
waiting for transactions to finish before index build.
-        CompletableFuture<Void> startedWaitForPreIndexTxsToFinish = new 
CompletableFuture<>();
-        CLUSTER.nodes().forEach(node -> {
-            unwrapIgniteImpl(node).dropMessages((recipientId, message) -> {
-                if (message instanceof WriteIntentSwitchReplicaRequest) {
-                    return true;
-                }
-
-                if (message instanceof 
IsNodeFinishedRwTransactionsStartedBeforeRequest) {
-                    startedWaitForPreIndexTxsToFinish.complete(null);
-                }
-
-                return false;
-            });
-        });
-
-        // Create and abandon a transaction.
-        int txCoordinatorOrdinal = 2;
-        Transaction tx = 
CLUSTER.node(txCoordinatorOrdinal).transactions().begin();
-        insertDataInTransaction(tx, TABLE_NAME, List.of("I0", "I1"), new 
Object[]{1, 1});
-
-        createIndex(INDEX_NAME);
-        assertThat(startedWaitForPreIndexTxsToFinish, 
willCompleteSuccessfully());
-
-        // The index pre-build wait has started, let's restart the coordinator 
to abandon the transaction and abruptly terminate
-        // the pre-build wait.
-        CLUSTER.restartNode(txCoordinatorOrdinal);
-
-        await("Index did not become available in time")
-                .atMost(10, SECONDS)
-                .until(() -> 
isIndexAvailable(unwrapIgniteImpl(CLUSTER.aliveNode()), INDEX_NAME));
-
-        verifyNoNodesHaveAnythingInIndex();
-    }
-
-    private void verifyNoNodesHaveAnythingInIndex() {
-        for (int nodeIndex = 0; nodeIndex < initialNodes(); nodeIndex++) {
-            IgniteImpl ignite = unwrapIgniteImpl(node(nodeIndex));
-
-            CatalogIndexDescriptor indexDescriptor = 
indexDescriptor(INDEX_NAME, ignite);
-            SortedIndexStorage indexStorage = (SortedIndexStorage) 
indexStorage(indexDescriptor, 0, ignite);
-
-            if (indexStorage != null) {
-                try (Cursor<IndexRow> indexRows = 
indexStorage.readOnlyScan(null, null, 0)) {
-                    assertFalse(indexRows.hasNext(), "Nothing should have been 
put to the index, but it was found on node " + nodeIndex);
-                }
-            }
-        }
-    }
-
-    private static void disableWriteIntentSwitchExecution() {
-        CLUSTER.runningNodes().forEach(ignite -> {
-            unwrapIgniteImpl(ignite).dropMessages((recipientId, message) -> 
message instanceof WriteIntentSwitchReplicaRequest);
-        });
-    }
-
-    private static CatalogIndexDescriptor indexDescriptor(String indexName, 
IgniteImpl ignite) {
-        return getIndexStrict(ignite.catalogManager(), indexName, 
ignite.clock().nowLong());
-    }
-
-    private static @Nullable IndexStorage indexStorage(CatalogIndexDescriptor 
indexDescriptor, int partitionId, IgniteImpl ignite) {
-        TableViewInternal tableViewInternal = 
tableViewInternal(indexDescriptor.tableId(), ignite);
-
-        int indexId = indexDescriptor.id();
-
-        IndexStorage indexStorage;
-        try {
-            indexStorage = 
tableViewInternal.internalTable().storage().getIndex(partitionId, indexId);
-        } catch (StorageException e) {
-            if (e.getMessage().contains("Partition ID " + partitionId + " does 
not exist")) {
-                return null;
-            }
-
-            throw e;
-        }
-
-        assertNotNull(indexStorage, String.format("No index storage exists for 
indexId=%s, partitionId=%s", indexId, partitionId));
-
-        return indexStorage;
-    }
-
-    private static TableViewInternal tableViewInternal(int tableId, Ignite 
ignite) {
-        CompletableFuture<List<Table>> tablesFuture = 
ignite.tables().tablesAsync();
-
-        assertThat(tablesFuture, willCompleteSuccessfully());
-
-        TableViewInternal tableViewInternal = tablesFuture.join().stream()
-                .map(TestWrappers::unwrapTableViewInternal)
-                .filter(table -> table.tableId() == tableId)
-                .findFirst()
-                .orElse(null);
-
-        assertNotNull(tableViewInternal, "No table object found for tableId=" 
+ tableId);
-
-        return tableViewInternal;
-    }
-
     @SafeVarargs
     private static String toValuesString(List<Object>... values) {
         return Stream.of(values)
@@ -374,7 +241,7 @@ public class ItBuildIndexTest extends 
BaseSqlIntegrationTest {
         ));
     }
 
-    private void createIndex(String indexName) throws Exception {
+    private void createIndex(String indexName) {
         // We execute this operation asynchronously, because some tests block 
network messages, which makes the underlying code
         // stuck with timeouts. We don't need to wait for the operation to 
complete, as we wait for the necessary invariants further
         // below.
@@ -394,7 +261,7 @@ public class ItBuildIndexTest extends 
BaseSqlIntegrationTest {
      *
      * @param indexName Name of an index to wait for.
      */
-    private static void waitForIndex(String indexName) throws 
InterruptedException {
+    private static void waitForIndex(String indexName) {
         await().atMost(10, SECONDS).until(
                 () -> CLUSTER.runningNodes()
                         .map(TestWrappers::unwrapIgniteImpl)
@@ -432,7 +299,7 @@ public class ItBuildIndexTest extends 
BaseSqlIntegrationTest {
         };
     }
 
-    private static void checkIndexBuild(int partitions, int replicas, String 
indexName) throws Exception {
+    private static void checkIndexBuild(int partitions, int replicas, String 
indexName) {
         Map<Integer, Set<String>> nodesWithBuiltIndexesByPartitionId = 
waitForIndexBuild(TABLE_NAME, indexName);
 
         // Check that the number of nodes with built indexes is equal to the 
number of replicas.
@@ -550,7 +417,7 @@ public class ItBuildIndexTest extends 
BaseSqlIntegrationTest {
      * @param node Node.
      * @param indexName Index name.
      */
-    private static @Nullable CatalogIndexDescriptor 
getIndexDescriptor(IgniteImpl node, String indexName) {
+    static @Nullable CatalogIndexDescriptor getIndexDescriptor(IgniteImpl 
node, String indexName) {
         HybridClock clock = node.clock();
         CatalogManager catalogManager = node.catalogManager();
         return 
catalogManager.activeCatalog(clock.nowLong()).aliveIndex(SCHEMA_NAME, 
indexName);
diff --git 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexWriteIntentsHandlingTest.java
 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexWriteIntentsHandlingTest.java
new file mode 100644
index 00000000000..6eb989d1d62
--- /dev/null
+++ 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/ItBuildIndexWriteIntentsHandlingTest.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.index;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static 
org.apache.ignite.internal.ClusterPerClassIntegrationTest.isIndexAvailable;
+import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
+import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
+import static 
org.apache.ignite.internal.index.ItBuildIndexTest.getIndexDescriptor;
+import static 
org.apache.ignite.internal.index.WriteIntentSwitchControl.disableWriteIntentSwitchExecution;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static org.apache.ignite.internal.table.TableTestUtils.getIndexStrict;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
+import org.apache.ignite.internal.TestWrappers;
+import org.apache.ignite.internal.app.IgniteImpl;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+import 
org.apache.ignite.internal.index.message.IsNodeFinishedRwTransactionsStartedBeforeRequest;
+import org.apache.ignite.internal.storage.StorageException;
+import org.apache.ignite.internal.storage.index.IndexRow;
+import org.apache.ignite.internal.storage.index.IndexStorage;
+import org.apache.ignite.internal.storage.index.SortedIndexStorage;
+import org.apache.ignite.internal.table.TableViewInternal;
+import org.apache.ignite.internal.tx.message.WriteIntentSwitchReplicaRequest;
+import org.apache.ignite.internal.util.Cursor;
+import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.table.Table;
+import org.apache.ignite.tx.Transaction;
+import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+class ItBuildIndexWriteIntentsHandlingTest extends 
ClusterPerTestIntegrationTest {
+    private static final String ZONE_NAME = "ZONE_TABLE";
+
+    private static final String TABLE_NAME = "TEST_TABLE";
+
+    private static final String INDEX_NAME = "TEST_INDEX";
+
+    @Test
+    void writeIntentFromTxAbandonedBeforeShouldNotBeIndexed() {
+        createTable(1, 1);
+
+        disableWriteIntentSwitchExecution(cluster);
+
+        // Create and abandon a transaction.
+        int txCoordinatorOrdinal = 2;
+        Transaction tx = 
cluster.node(txCoordinatorOrdinal).transactions().begin();
+        insertDataInTransaction(tx, TABLE_NAME, List.of("I0", "I1"), new 
Object[]{1, 1});
+
+        cluster.restartNode(txCoordinatorOrdinal);
+
+        createIndex(INDEX_NAME);
+        await("Index did not become available in time")
+                .atMost(10, SECONDS)
+                .until(() -> 
isIndexAvailable(unwrapIgniteImpl(cluster.aliveNode()), INDEX_NAME));
+
+        verifyNoNodesHaveAnythingInIndex();
+    }
+
+    @Test
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26811";)
+    void 
writeIntentFromTxAbandonedWhileWaitingForTransactionsToFinishShouldNotBeIndexed()
 {
+        createTable(1, 1);
+
+        // Both disable write intent switch execution and track when we start 
waiting for transactions to finish before index build.
+        CompletableFuture<Void> startedWaitForPreIndexTxsToFinish = new 
CompletableFuture<>();
+        cluster.nodes().forEach(node -> {
+            unwrapIgniteImpl(node).dropMessages((recipientId, message) -> {
+                if (message instanceof WriteIntentSwitchReplicaRequest) {
+                    return true;
+                }
+
+                if (message instanceof 
IsNodeFinishedRwTransactionsStartedBeforeRequest) {
+                    startedWaitForPreIndexTxsToFinish.complete(null);
+                }
+
+                return false;
+            });
+        });
+
+        // Create and abandon a transaction.
+        int txCoordinatorOrdinal = 2;
+        Transaction tx = 
cluster.node(txCoordinatorOrdinal).transactions().begin();
+        insertDataInTransaction(tx, TABLE_NAME, List.of("I0", "I1"), new 
Object[]{1, 1});
+
+        createIndex(INDEX_NAME);
+        assertThat(startedWaitForPreIndexTxsToFinish, 
willCompleteSuccessfully());
+
+        // The index pre-build wait has started, let's restart the coordinator 
to abandon the transaction and abruptly terminate
+        // the pre-build wait.
+        cluster.restartNode(txCoordinatorOrdinal);
+
+        await("Index did not become available in time")
+                .atMost(10, SECONDS)
+                .until(() -> 
isIndexAvailable(unwrapIgniteImpl(cluster.aliveNode()), INDEX_NAME));
+
+        verifyNoNodesHaveAnythingInIndex();
+    }
+
+    private void verifyNoNodesHaveAnythingInIndex() {
+        for (int nodeIndex = 0; nodeIndex < initialNodes(); nodeIndex++) {
+            IgniteImpl ignite = unwrapIgniteImpl(node(nodeIndex));
+
+            CatalogIndexDescriptor indexDescriptor = 
indexDescriptor(INDEX_NAME, ignite);
+            SortedIndexStorage indexStorage = (SortedIndexStorage) 
indexStorage(indexDescriptor, 0, ignite);
+
+            if (indexStorage != null) {
+                try (Cursor<IndexRow> indexRows = 
indexStorage.readOnlyScan(null, null, 0)) {
+                    assertFalse(indexRows.hasNext(), "Nothing should have been 
put to the index, but it was found on node " + nodeIndex);
+                }
+            }
+        }
+    }
+
+    private static CatalogIndexDescriptor indexDescriptor(String indexName, 
IgniteImpl ignite) {
+        return getIndexStrict(ignite.catalogManager(), indexName, 
ignite.clock().nowLong());
+    }
+
+    private static @Nullable IndexStorage indexStorage(CatalogIndexDescriptor 
indexDescriptor, int partitionId, IgniteImpl ignite) {
+        TableViewInternal tableViewInternal = 
tableViewInternal(indexDescriptor.tableId(), ignite);
+
+        int indexId = indexDescriptor.id();
+
+        IndexStorage indexStorage;
+        try {
+            indexStorage = 
tableViewInternal.internalTable().storage().getIndex(partitionId, indexId);
+        } catch (StorageException e) {
+            if (e.getMessage().contains("Partition ID " + partitionId + " does 
not exist")) {
+                return null;
+            }
+
+            throw e;
+        }
+
+        assertNotNull(indexStorage, String.format("No index storage exists for 
indexId=%s, partitionId=%s", indexId, partitionId));
+
+        return indexStorage;
+    }
+
+    private static TableViewInternal tableViewInternal(int tableId, Ignite 
ignite) {
+        CompletableFuture<List<Table>> tablesFuture = 
ignite.tables().tablesAsync();
+
+        assertThat(tablesFuture, willCompleteSuccessfully());
+
+        TableViewInternal tableViewInternal = tablesFuture.join().stream()
+                .map(TestWrappers::unwrapTableViewInternal)
+                .filter(table -> table.tableId() == tableId)
+                .findFirst()
+                .orElse(null);
+
+        assertNotNull(tableViewInternal, "No table object found for tableId=" 
+ tableId);
+
+        return tableViewInternal;
+    }
+
+    private void createTable(int replicas, int partitions) {
+        IgniteSql sql = cluster.node(0).sql();
+
+        sql.executeScript(format("CREATE ZONE IF NOT EXISTS {} (REPLICAS {}, 
PARTITIONS {}) STORAGE PROFILES ['{}']",
+                ZONE_NAME, replicas, partitions, DEFAULT_STORAGE_PROFILE
+        ));
+        sql.executeScript(format(
+                "CREATE TABLE {} (i0 INTEGER PRIMARY KEY, i1 INTEGER) ZONE {}",
+                TABLE_NAME, ZONE_NAME
+        ));
+    }
+
+    private void createIndex(String indexName) {
+        // We execute this operation asynchronously, because some tests block 
network messages, which makes the underlying code
+        // stuck with timeouts. We don't need to wait for the operation to 
complete, as we wait for the necessary invariants further
+        // below.
+        cluster.aliveNode().sql()
+                .executeAsync(null, format("CREATE INDEX {} ON {} (i1)", 
indexName, TABLE_NAME))
+                .whenComplete((res, ex) -> {
+                    if (ex != null) {
+                        log.error("Failed to create index", ex);
+                    }
+                });
+
+        waitForIndex(indexName);
+    }
+
+    /**
+     * Waits for all nodes in the cluster to have the given index in the 
Catalog.
+     *
+     * @param indexName Name of an index to wait for.
+     */
+    private void waitForIndex(String indexName) {
+        await().atMost(10, SECONDS).until(
+                () -> cluster.runningNodes()
+                        .map(TestWrappers::unwrapIgniteImpl)
+                        .map(node -> getIndexDescriptor(node, indexName))
+                        .allMatch(Objects::nonNull)
+        );
+    }
+
+    private void insertDataInTransaction(Transaction tx, String tblName, 
List<String> columnNames, Object[]... tuples) {
+        String insertStmt = "INSERT INTO " + tblName + "(" + String.join(", ", 
columnNames) + ")"
+                + " VALUES (" + ", ?".repeat(columnNames.size()).substring(2) 
+ ")";
+
+        for (Object[] args : tuples) {
+            cluster.node(0).sql().execute(tx, insertStmt, args).close();
+        }
+    }
+}
diff --git 
a/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/WriteIntentSwitchControl.java
 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/WriteIntentSwitchControl.java
new file mode 100644
index 00000000000..b5ffe7b3274
--- /dev/null
+++ 
b/modules/index/src/integrationTest/java/org/apache/ignite/internal/index/WriteIntentSwitchControl.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.index;
+
+import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
+
+import org.apache.ignite.internal.Cluster;
+import org.apache.ignite.internal.tx.message.WriteIntentSwitchReplicaRequest;
+
+class WriteIntentSwitchControl {
+    static void disableWriteIntentSwitchExecution(Cluster cluster) {
+        cluster.runningNodes().forEach(ignite -> {
+            unwrapIgniteImpl(ignite).dropMessages((recipientId, message) -> 
message instanceof WriteIntentSwitchReplicaRequest);
+        });
+    }
+}
diff --git 
a/modules/runner/src/testFixtures/java/org/apache/ignite/internal/ClusterPerClassIntegrationTest.java
 
b/modules/runner/src/testFixtures/java/org/apache/ignite/internal/ClusterPerClassIntegrationTest.java
index fdbc4366705..bfdb1dddf85 100644
--- 
a/modules/runner/src/testFixtures/java/org/apache/ignite/internal/ClusterPerClassIntegrationTest.java
+++ 
b/modules/runner/src/testFixtures/java/org/apache/ignite/internal/ClusterPerClassIntegrationTest.java
@@ -657,7 +657,7 @@ public abstract class ClusterPerClassIntegrationTest 
extends BaseIgniteAbstractT
      * @param ignite Node.
      * @param indexName Index name that is being checked.
      */
-    protected static boolean isIndexAvailable(IgniteImpl ignite, String 
indexName) {
+    public static boolean isIndexAvailable(IgniteImpl ignite, String 
indexName) {
         CatalogManager catalogManager = ignite.catalogManager();
         HybridClock clock = ignite.clock();
 

Reply via email to