diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRebalanceTest.java index 61144f4159813..b8bc509c7fea1 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRebalanceTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRebalanceTest.java @@ -336,7 +336,33 @@ public void testWithLocalWalChange() throws Exception { // This node should rebalance data from other nodes and shouldn't have WAL history. Ignite ignite = startGrid(2); - awaitPartitionMapExchange(); + awaitPartitionMapExchange(true, true, null); + + // Wait for WAL rebalance to complete. With ASYNC rebalance, rebalance completion triggers + // a minor exchange to update partition states (REBALANCING -> OWNED). We must wait for + // both rebalance AND the resulting minor exchange to finish, otherwise cache writes can + // race with the minor exchange and hit "Invalid version for inner update" errors. + for (Ignite ig : G.allGrids()) { + GridCachePreloader pld = ((IgniteEx)ig).cachex(CACHE_NAME).context().group().preloader(); + + if (pld.rebalanceFuture() != null) + pld.rebalanceFuture().get(); + } + + // Wait for minor exchanges triggered by rebalance completion to finish on all nodes. + // Since minor exchanges are created asynchronously, we poll until no pending exchanges exist. + assertTrue(GridTestUtils.waitForCondition(() -> { + for (Ignite ig : G.allGrids()) { + List futs = ((IgniteEx)ig).context().cache().context().exchange().exchangeFutures(); + + for (GridDhtPartitionsExchangeFuture fut : futs) { + if (!fut.isDone()) + return false; + } + } + + return true; + }, getTestTimeout())); Set topVers = ((WalRebalanceCheckingCommunicationSpi)ignite.configuration().getCommunicationSpi()) .walRebalanceVersions(grpId); @@ -358,6 +384,12 @@ else if (k % 3 == 1) // Spread removes across all partitions. stopGrid(1); + // Await exchange so that grid(2) finishes processing NODE_LEFT for the stopped + // nodes before grid(3) starts. Otherwise grid(2) may send near-atomic-update + // requests to already stopped nodes, causing CorruptedTreeException + // ("Invalid version for inner update"). + awaitPartitionMapExchange(); + // Start new node which should rebalance all data from node(2) without using WAL, // because node(2) doesn't have full history for rebalance. ignite = startGrid(3);