This is an automated email from the ASF dual-hosted git repository.
timoninmaxim pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 2017e43f96a IGNITE-25534 Ignite accepts joining node with rolling
upgrade version (#12301)
2017e43f96a is described below
commit 2017e43f96a972a310b518de9f844abca36c284b
Author: Aleksandr Chesnokov <[email protected]>
AuthorDate: Wed Nov 5 10:20:27 2025 +0300
IGNITE-25534 Ignite accepts joining node with rolling upgrade version
(#12301)
Co-authored-by: Maksim Timonin <[email protected]>
Co-authored-by: Maksim Davydov <[email protected]>
---
.../apache/ignite/internal/GridKernalContext.java | 8 +
.../ignite/internal/GridKernalContextImpl.java | 12 +
.../org/apache/ignite/internal/IgniteKernal.java | 4 +-
.../wal/reader/StandaloneGridKernalContext.java | 6 +
.../OsDiscoveryNodeValidationProcessor.java | 73 ----
.../rollingupgrade/RollingUpgradeProcessor.java | 309 ++++++++++++++
.../ignite/spi/discovery/tcp/TcpDiscoverySpi.java | 11 +
.../tcp/internal/TcpDiscoveryNodesRing.java | 30 +-
.../ignite/internal/GridReleaseTypeSelfTest.java | 447 +++++++++++++++++++--
9 files changed, 779 insertions(+), 121 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
index 75531b9c523..742105bf4da 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
@@ -67,6 +67,7 @@ import
org.apache.ignite.internal.processors.port.GridPortProcessor;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.segmentation.GridSegmentationProcessor;
@@ -640,6 +641,13 @@ public interface GridKernalContext extends
Iterable<GridComponent> {
*/
public PerformanceStatisticsProcessor performanceStatistics();
+ /**
+ * Gets Rolling upgrade processor.
+ *
+ * @return Rolling upgrade processor.
+ */
+ public RollingUpgradeProcessor rollingUpgrade();
+
/**
* Executor that is in charge of processing user async continuations.
*
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index 5d8ae5d4af9..2a7c7f66afa 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -91,6 +91,7 @@ import
org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.segmentation.GridSegmentationProcessor;
@@ -362,6 +363,10 @@ public class GridKernalContextImpl implements
GridKernalContext, Externalizable
@GridToStringExclude
private PerformanceStatisticsProcessor perfStatProc;
+ /** Rolling upgrade processor. */
+ @GridToStringExclude
+ private RollingUpgradeProcessor rollUpProc;
+
/** */
private Thread.UncaughtExceptionHandler hnd;
@@ -596,6 +601,8 @@ public class GridKernalContextImpl implements
GridKernalContext, Externalizable
transProc = (CacheObjectTransformerProcessor)comp;
else if (comp instanceof PerformanceStatisticsProcessor)
perfStatProc = (PerformanceStatisticsProcessor)comp;
+ else if (comp instanceof RollingUpgradeProcessor)
+ rollUpProc = (RollingUpgradeProcessor)comp;
else if (comp instanceof IndexProcessor)
indexProc = (IndexProcessor)comp;
else if (!(comp instanceof DiscoveryNodeValidationProcessor
@@ -1105,6 +1112,11 @@ public class GridKernalContextImpl implements
GridKernalContext, Externalizable
return perfStatProc;
}
+ /** {@inheritDoc} */
+ @Override public RollingUpgradeProcessor rollingUpgrade() {
+ return rollUpProc;
+ }
+
/** {@inheritDoc} */
@Override public Executor getAsyncContinuationExecutor() {
return config().getAsyncContinuationExecutor() == null
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index 1878ef042e8..56191e68d9e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -146,7 +146,6 @@ import
org.apache.ignite.internal.processors.marshaller.GridMarshallerMappingPro
import
org.apache.ignite.internal.processors.metastorage.persistence.DistributedMetaStorageImpl;
import org.apache.ignite.internal.processors.metric.GridMetricManager;
import
org.apache.ignite.internal.processors.nodevalidation.DiscoveryNodeValidationProcessor;
-import
org.apache.ignite.internal.processors.nodevalidation.OsDiscoveryNodeValidationProcessor;
import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
import
org.apache.ignite.internal.processors.performancestatistics.PerformanceStatisticsProcessor;
import org.apache.ignite.internal.processors.platform.PlatformNoopProcessor;
@@ -160,6 +159,7 @@ import
org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import
org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
import org.apache.ignite.internal.processors.rest.GridRestProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
import org.apache.ignite.internal.processors.security.GridSecurityProcessor;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import org.apache.ignite.internal.processors.security.IgniteSecurityProcessor;
@@ -3225,7 +3225,7 @@ public class IgniteKernal implements IgniteEx,
Externalizable {
return (T)new CacheObjectBinaryProcessorImpl(ctx);
if (cls.equals(DiscoveryNodeValidationProcessor.class))
- return (T)new OsDiscoveryNodeValidationProcessor(ctx);
+ return (T)new RollingUpgradeProcessor(ctx);
if (cls.equals(IGridClusterStateProcessor.class))
return (T)new GridClusterStateProcessor(ctx);
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
index 2e729960808..84af46b6e31 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
@@ -90,6 +90,7 @@ import
org.apache.ignite.internal.processors.port.GridPortProcessor;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.resource.GridResourceProcessor;
import org.apache.ignite.internal.processors.rest.IgniteRestProcessor;
+import
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
import
org.apache.ignite.internal.processors.schedule.IgniteScheduleProcessorAdapter;
import org.apache.ignite.internal.processors.security.IgniteSecurity;
import
org.apache.ignite.internal.processors.security.NoOpIgniteSecurityProcessor;
@@ -741,6 +742,11 @@ public class StandaloneGridKernalContext implements
GridKernalContext {
return null;
}
+ /** {@inheritDoc} */
+ @Override public RollingUpgradeProcessor rollingUpgrade() {
+ return null;
+ }
+
/** {@inheritDoc} */
@Override public Executor getAsyncContinuationExecutor() {
return null;
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/nodevalidation/OsDiscoveryNodeValidationProcessor.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/nodevalidation/OsDiscoveryNodeValidationProcessor.java
deleted file mode 100644
index 21a9fde19ee..00000000000
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/nodevalidation/OsDiscoveryNodeValidationProcessor.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.processors.nodevalidation;
-
-import java.util.Objects;
-import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.processors.GridProcessorAdapter;
-import org.apache.ignite.internal.util.typedef.internal.LT;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.spi.IgniteNodeValidationResult;
-import org.jetbrains.annotations.Nullable;
-
-import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
-
-/**
- * Node validation.
- */
-public class OsDiscoveryNodeValidationProcessor extends GridProcessorAdapter
implements DiscoveryNodeValidationProcessor {
- /**
- * @param ctx Kernal context.
- */
- public OsDiscoveryNodeValidationProcessor(GridKernalContext ctx) {
- super(ctx);
- }
-
- /** {@inheritDoc} */
- @Nullable @Override public IgniteNodeValidationResult
validateNode(ClusterNode node) {
- ClusterNode locNode = ctx.discovery().localNode();
-
- // Check version.
- String locBuildVer = locNode.attribute(ATTR_BUILD_VER);
- String rmtBuildVer = node.attribute(ATTR_BUILD_VER);
-
- if (!Objects.equals(rmtBuildVer, locBuildVer)) {
- // OS nodes don't support rolling updates.
- if (!locBuildVer.equals(rmtBuildVer)) {
- String errMsg = "Local node and remote node have different
version numbers " +
- "(node will not join, Ignite does not support rolling
updates, " +
- "so versions must be exactly the same) " +
- "[locBuildVer=" + locBuildVer + ", rmtBuildVer=" +
rmtBuildVer +
- ", locNodeAddrs=" + U.addressesAsString(locNode) +
- ", rmtNodeAddrs=" + U.addressesAsString(node) +
- ", locNodeId=" + locNode.id() + ", rmtNodeId=" + node.id()
+ ']';
-
- LT.warn(log, errMsg);
-
- // Always output in debug.
- if (log.isDebugEnabled())
- log.debug(errMsg);
-
- return new IgniteNodeValidationResult(node.id(), errMsg);
- }
- }
-
- return null;
- }
-}
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
new file mode 100644
index 00000000000..14af99193ab
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
@@ -0,0 +1,309 @@
+/*
+ * 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.processors.rollingupgrade;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
+import
org.apache.ignite.internal.processors.metastorage.DistributedMetastorageLifecycleListener;
+import
org.apache.ignite.internal.processors.metastorage.ReadableDistributedMetaStorage;
+import
org.apache.ignite.internal.processors.nodevalidation.DiscoveryNodeValidationProcessor;
+import org.apache.ignite.internal.util.lang.IgnitePair;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteProductVersion;
+import org.apache.ignite.spi.IgniteNodeValidationResult;
+import org.apache.ignite.spi.discovery.DiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
+import static
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage.IGNITE_INTERNAL_KEY_PREFIX;
+
+/** Rolling upgrade processor. Manages current and target versions of cluster.
*/
+public class RollingUpgradeProcessor extends GridProcessorAdapter implements
DiscoveryNodeValidationProcessor {
+ /** Key for the distributed property that holds current and target
versions. */
+ private static final String ROLLING_UPGRADE_VERSIONS_KEY =
IGNITE_INTERNAL_KEY_PREFIX + "rolling.upgrade.versions";
+
+ /** Metastorage with the write access. */
+ @Nullable private volatile DistributedMetaStorage metastorage;
+
+ /** TCP discovery nodes ring. */
+ private TcpDiscoveryNodesRing ring;
+
+ /** Last joining node. */
+ private ClusterNode lastJoiningNode;
+
+ /** Last joining node timestamp. */
+ private long lastJoiningNodeTimestamp;
+
+ /** Lock for synchronization between tcp-disco-msg-worker thread and
management operations. */
+ private final Object lock = new Object();
+
+ /** */
+ private final CountDownLatch startLatch = new CountDownLatch(1);
+
+ /** Pair with current and target versions. {@code null} when rolling
upgrade is disabled. */
+ @Nullable private volatile IgnitePair<IgniteProductVersion> rollUpVers;
+
+ /**
+ * @param ctx Context.
+ */
+ public RollingUpgradeProcessor(GridKernalContext ctx) {
+ super(ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onKernalStart(boolean active) throws
IgniteCheckedException {
+ DiscoverySpi spi = ctx.config().getDiscoverySpi();
+
+ if (spi instanceof TcpDiscoverySpi)
+ ring = ((TcpDiscoverySpi)spi).discoveryRing();
+
+ startLatch.countDown();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void start() throws IgniteCheckedException {
+
ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new
DistributedMetastorageLifecycleListener() {
+ @Override public void onReadyForWrite(DistributedMetaStorage
metastorage) {
+ RollingUpgradeProcessor.this.metastorage = metastorage;
+ }
+
+ @Override public void
onReadyForRead(ReadableDistributedMetaStorage metastorage) {
+ try {
+ rollUpVers =
metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+
+ // Keep the current and target version pair in sync with
metastorage updates, e.g., to handle coordinator changes.
+ metastorage.listen(ROLLING_UPGRADE_VERSIONS_KEY::equals, (key,
oldVal, newVal) -> {
+ rollUpVers = (IgnitePair<IgniteProductVersion>)newVal;
+ });
+ }
+ });
+ }
+
+ /** {@inheritDoc} The joining node is stored to verify later whether it
successfully connected to the ring or failed to join. */
+ @Override public @Nullable IgniteNodeValidationResult
validateNode(ClusterNode node) {
+ synchronized (lock) {
+ lastJoiningNode = node;
+
+ lastJoiningNodeTimestamp = U.currentTimeMillis();
+ }
+
+ ClusterNode locNode = ctx.discovery().localNode();
+
+ String locBuildVer = locNode.attribute(ATTR_BUILD_VER);
+ String rmtBuildVer = node.attribute(ATTR_BUILD_VER);
+
+ IgniteProductVersion rmtVer =
IgniteProductVersion.fromString(rmtBuildVer);
+
+ IgnitePair<IgniteProductVersion> pair = rollUpVers;
+
+ IgniteProductVersion curVer = pair == null ?
IgniteProductVersion.fromString(locBuildVer) : pair.get1();
+ IgniteProductVersion targetVer = pair == null ? null : pair.get2();
+
+ if (Objects.equals(rmtVer, curVer) || Objects.equals(rmtVer,
targetVer))
+ return null;
+
+ String errMsg = "Remote node rejected due to incompatible version for
cluster join.\n"
+ + "Remote node info:\n"
+ + " - Version : " + rmtBuildVer + "\n"
+ + " - Addresses : " + U.addressesAsString(node) + "\n"
+ + " - Node ID : " + node.id() + "\n"
+ + "Local node info:\n"
+ + " - Version : " + locBuildVer + "\n"
+ + " - Addresses : " + U.addressesAsString(locNode) + "\n"
+ + " - Node ID : " + locNode.id() + "\n"
+ + "Allowed versions for joining: " + curVer + (targetVer == null ?
"" : ", " + targetVer);
+
+ LT.warn(log, errMsg);
+
+ if (log.isDebugEnabled())
+ log.debug(errMsg);
+
+ return new IgniteNodeValidationResult(node.id(), errMsg);
+ }
+
+ /**
+ * Enables rolling upgrade with specified target version.
+ * This method can only be called on coordinator node with {@link
TcpDiscoverySpi}.
+ *
+ * @param target Target version.
+ * @throws IgniteCheckedException If:
+ * <ul>
+ * <li>The current and target versions are incompatible;</li>
+ * <li>The local node is not a coordinator;</li>
+ * <li>The discovery SPI is not {@link TcpDiscoverySpi};</li>
+ * <li>The distributed metastorage is not ready;</li>
+ * </ul>
+ */
+ public void enable(IgniteProductVersion target) throws
IgniteCheckedException {
+ if (startLatch.getCount() > 0)
+ throw new IgniteCheckedException("Cannot enable rolling upgrade:
processor has not been started yet");
+
+ if (!U.isLocalNodeCoordinator(ctx.discovery()))
+ throw new IgniteCheckedException("Rolling upgrade can be enabled
only on coordinator node");
+
+ if (metastorage == null)
+ throw new IgniteCheckedException("Metastorage is not ready yet.
Try again later");
+
+ if (!(ctx.config().getDiscoverySpi() instanceof TcpDiscoverySpi))
+ throw new IgniteCheckedException("Rolling upgrade is supported
only with TCP discovery SPI");
+
+ String curBuildVer =
ctx.discovery().localNode().attribute(ATTR_BUILD_VER);
+ IgniteProductVersion curVer =
IgniteProductVersion.fromString(curBuildVer);
+
+ if (!checkVersionsForEnabling(curVer, target))
+ return;
+
+ IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target);
+
+ if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null,
newPair)) {
+ IgnitePair<IgniteProductVersion> oldVerPair =
metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY);
+
+ if (newPair.equals(oldVerPair))
+ return;
+
+ if (oldVerPair == null)
+ throw new IgniteCheckedException("Could not enable rolling
upgrade. Try again");
+
+ throw new IgniteCheckedException("Rolling upgrade is already
enabled with a different current and target version: " +
+ oldVerPair.get1() + " , " + oldVerPair.get2());
+ }
+
+ rollUpVers = newPair;
+
+ if (log.isInfoEnabled())
+ log.info("Rolling upgrade enabled [current=" + curVer + ",
target=" + target + ']');
+ }
+
+ /**
+ * Disables rolling upgrade.
+ * This method can only be called on coordinator node.
+ *
+ * @throws IgniteCheckedException If cluster has two or more nodes with
different versions or if node is not coordinator
+ * or metastorage is not ready.
+ */
+ public void disable() throws IgniteCheckedException {
+ if (!U.isLocalNodeCoordinator(ctx.discovery()))
+ throw new IgniteCheckedException("Rolling upgrade can be disabled
only on coordinator node");
+
+ if (metastorage == null)
+ throw new IgniteCheckedException("Meta storage is not ready. Try
again");
+
+ if (rollUpVers == null)
+ return;
+
+ IgnitePair<IgniteProductVersion> minMaxVerPair =
ring.minMaxNodeVersions();
+
+ if (!minMaxVerPair.get1().equals(minMaxVerPair.get2()))
+ throw new IgniteCheckedException("Can't disable rolling upgrade
with different versions in cluster: "
+ + minMaxVerPair.get1() + ", " + minMaxVerPair.get2());
+
+ synchronized (lock) {
+ if (lastJoiningNode != null) {
+ long timeout =
((TcpDiscoverySpi)ctx.config().getDiscoverySpi()).getJoinTimeout() * 3;
+
+ if (ring.node(lastJoiningNode.id()) != null || (timeout > 0 &&
U.currentTimeMillis() - lastJoiningNodeTimestamp > timeout))
+ lastJoiningNode = null;
+ }
+
+ if (lastJoiningNode != null) {
+ IgniteProductVersion lastJoiningNodeVer =
IgniteProductVersion.fromString(lastJoiningNode.attribute(ATTR_BUILD_VER));
+
+ if (!minMaxVerPair.get1().equals(lastJoiningNodeVer))
+ throw new IgniteCheckedException("Can't disable rolling
upgrade with different versions in cluster: "
+ + minMaxVerPair.get1() + ", " + lastJoiningNodeVer);
+ }
+
+ rollUpVers = null;
+ }
+
+ metastorage.remove(ROLLING_UPGRADE_VERSIONS_KEY);
+
+ if (log.isInfoEnabled())
+ log.info("Rolling upgrade disabled. Current version of nodes in
cluster: " + minMaxVerPair.get1());
+ }
+
+ /**
+ * Returns a pair containing the current and target versions of the
cluster.
+ * <p>
+ * This method returns {@code null} if rolling upgrade has not been
enabled yet
+ * or if version information has not been read from the distributed
metastorage.
+ *
+ * @return A pair where:
+ * <ul>
+ * <li><b>First element</b> — current version of the cluster.</li>
+ * <li><b>Second element</b> — target version to which the cluster
is being upgraded.</li>
+ * </ul>
+ * or {@code null} if rolling upgrade is not active.
+ */
+ @Nullable public IgnitePair<IgniteProductVersion> versions() {
+ return rollUpVers;
+ }
+
+ /** Checks whether the cluster is in the rolling upgrade mode. */
+ public boolean enabled() {
+ return versions() != null;
+ }
+
+ /**
+ * Checks cur and target versions.
+ *
+ * @param cur Current cluster version.
+ * @param target Target cluster version.
+ * @return {@code false} if there is no need to update versions {@code
true} otherwise.
+ * @throws IgniteCheckedException If versions are incorrect.
+ */
+ private boolean checkVersionsForEnabling(IgniteProductVersion cur,
IgniteProductVersion target) throws IgniteCheckedException {
+ IgnitePair<IgniteProductVersion> oldVerPair = rollUpVers;
+ if (oldVerPair != null) {
+ if (oldVerPair.get1().equals(cur) &&
oldVerPair.get2().equals(target))
+ return false;
+
+ throw new IgniteCheckedException("Rolling upgrade is already
enabled with a different current and target version: " +
+ oldVerPair.get1() + " , " + oldVerPair.get2());
+ }
+
+ if (cur.major() != target.major())
+ throw new IgniteCheckedException("Major versions are different");
+
+ if (cur.minor() != target.minor()) {
+ if (target.minor() == cur.minor() + 1 && target.maintenance() == 0)
+ return true;
+
+ throw new IgniteCheckedException("Minor version can only be
incremented by 1");
+ }
+
+ if (cur.maintenance() + 1 != target.maintenance())
+ throw new IgniteCheckedException("Patch version can only be
incremented by 1");
+
+ return true;
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
index 6df10d88a0f..b82f3fca33a 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
@@ -97,6 +97,7 @@ import
org.apache.ignite.spi.discovery.DiscoverySpiNodeAuthenticator;
import org.apache.ignite.spi.discovery.DiscoverySpiOrderSupport;
import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
+import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryStatistics;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import
org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder;
@@ -504,6 +505,16 @@ public class TcpDiscoverySpi extends IgniteSpiAdapter
implements IgniteDiscovery
return getNode(id);
}
+ /**
+ * @return TCP discovery nodes ring.
+ */
+ @Nullable public TcpDiscoveryNodesRing discoveryRing() {
+ if (impl instanceof ServerImpl)
+ return ((ServerImpl)impl).ring();
+
+ return null;
+ }
+
/** {@inheritDoc} */
@Override public boolean pingNode(UUID nodeId) {
return impl.pingNode(nodeId);
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
index 981745d8e82..6a316a9a599 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
@@ -30,6 +30,7 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.util.lang.ClusterNodeFunc;
+import org.apache.ignite.internal.util.lang.IgnitePair;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
@@ -91,17 +92,18 @@ public class TcpDiscoveryNodesRing {
@GridToStringExclude
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
- /** */
+ /** Minimum node version in the cluster. */
private IgniteProductVersion minNodeVer;
- /**
- * @return Minimum node version.
- */
- public IgniteProductVersion minimumNodeVersion() {
+ /** Maximum node version in the cluster. */
+ private IgniteProductVersion maxNodeVer;
+
+ /** Returns min and max node versions. */
+ public IgnitePair<IgniteProductVersion> minMaxNodeVersions() {
rwLock.readLock().lock();
try {
- return minNodeVer;
+ return F.pair(minNodeVer, maxNodeVer);
}
finally {
rwLock.readLock().unlock();
@@ -257,7 +259,7 @@ public class TcpDiscoveryNodesRing {
maxInternalOrder = node.internalOrder();
- initializeMinimumVersion();
+ initializeMinMaxVersions();
}
finally {
rwLock.writeLock().unlock();
@@ -329,7 +331,7 @@ public class TcpDiscoveryNodesRing {
nodeOrder = topVer;
- initializeMinimumVersion();
+ initializeMinMaxVersions();
}
finally {
rwLock.writeLock().unlock();
@@ -376,7 +378,7 @@ public class TcpDiscoveryNodesRing {
nodes.remove(rmv);
}
- initializeMinimumVersion();
+ initializeMinMaxVersions();
return rmv;
}
@@ -410,8 +412,10 @@ public class TcpDiscoveryNodesRing {
topVer = 0;
- if (locNode != null)
+ if (locNode != null) {
minNodeVer = locNode.version();
+ maxNodeVer = locNode.version();
+ }
}
finally {
rwLock.writeLock().unlock();
@@ -695,12 +699,16 @@ public class TcpDiscoveryNodesRing {
/**
*
*/
- private void initializeMinimumVersion() {
+ private void initializeMinMaxVersions() {
minNodeVer = null;
+ maxNodeVer = null;
for (TcpDiscoveryNode node : nodes) {
if (minNodeVer == null || node.version().compareTo(minNodeVer) < 0)
minNodeVer = node.version();
+
+ if (maxNodeVer == null || node.version().compareTo(maxNodeVer) > 0)
+ maxNodeVer = node.version();
}
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
index d4485b20e98..36035e32b81 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
@@ -17,23 +17,59 @@
package org.apache.ignite.internal;
-import java.io.PrintWriter;
-import java.io.StringWriter;
+import java.util.Collection;
+import java.util.List;
import java.util.Map;
+import java.util.function.UnaryOperator;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.function.ThrowableSupplier;
+import org.apache.ignite.internal.util.lang.IgnitePair;
+import org.apache.ignite.internal.util.lang.RunnableX;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.lang.IgniteProductVersion;
+import org.apache.ignite.spi.IgniteSpiException;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.apache.ignite.testframework.GridTestUtils.assertThrows;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
/**
- * Test grids starting with non compatible release types.
+ * Test Rolling Upgrade release types.
*/
+@RunWith(Parameterized.class)
public class GridReleaseTypeSelfTest extends GridCommonAbstractTest {
/** */
private String nodeVer;
+ /**
+ * Indicates whether the tested node is started as a client.
+ * This flag is used to run all test cases for both client and server node
configurations.
+ */
+ @Parameterized.Parameter
+ public boolean client;
+
+ /** Persistence. */
+ @Parameterized.Parameter(1)
+ public boolean persistence;
+
+ /** @return Test parameters. */
+ @Parameterized.Parameters(name = "client={0}, persistence={1}")
+ public static Collection<?> parameters() {
+ return GridTestUtils.cartesianProduct(List.of(false, true),
List.of(false, true));
+ }
+
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String
igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
@@ -51,67 +87,408 @@ public class GridReleaseTypeSelfTest extends
GridCommonAbstractTest {
cfg.setDiscoverySpi(discoSpi);
+ DataStorageConfiguration storageCfg = new DataStorageConfiguration();
+
+
storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(persistence);
+
+ cfg.setDataStorageConfiguration(storageCfg);
+
return cfg;
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
+ cleanPersistenceDir();
}
- /**
- * @throws Exception If failed.
- */
+ /** */
+ @Test
+ public void testTwoConflictVersions() {
+ testConflictVersions("2.18.0", "2.16.0", client);
+ testConflictVersions("2.21.0", "2.23.1", client);
+ testConflictVersions("2.20.1", "2.20.2", client);
+ }
+
+ /** */
@Test
- public void testOsEditionDoesNotSupportRollingUpdates() throws Exception {
- nodeVer = "1.0.0";
+ public void testThreeConflictVersions() throws Exception {
+ testConflictVersionsWithRollingUpgrade("2.18.0", "2.18.1", "2.18.2",
client, "2.18.1");
+
+ testConflictVersionsWithRollingUpgrade("2.18.0", "2.18.1", "2.17.2",
client, "2.18.1");
- startGrid(0);
+ testConflictVersionsWithRollingUpgrade("2.18.1", "2.19.0", "2.19.1",
client, "2.19.0");
- try {
- nodeVer = "1.0.1";
+ testConflictVersionsWithRollingUpgrade("2.18.1", "2.18.2", "2.18.0",
client, "2.18.2");
+ }
- startGrid(1);
+ /** */
+ @Test
+ public void testTwoCompatibleVersions() throws Exception {
+ testCompatibleVersions("2.18.0", "2.18.0", client, null);
+ testCompatibleVersions("2.19.2", "2.19.2", client, null);
- fail("Exception has not been thrown.");
+ testCompatibleVersions("2.18.0", "2.18.1", client, "2.18.1");
+ testCompatibleVersions("2.18.2", "2.19.0", client, "2.19.0");
+ }
+
+ /** */
+ @Test
+ public void testThreeCompatibleVersions() throws Exception {
+ testCompatibleVersions("2.18.0", "2.18.0", "2.18.0", client, null);
+ testCompatibleVersions("2.18.2", "2.18.2", "2.18.2", client, null);
+
+ testCompatibleVersions("2.18.0", "2.18.1", "2.18.1", client, "2.18.1");
+ testCompatibleVersions("2.18.1", "2.19.0", "2.18.1", client, "2.19.0");
+ }
+
+ /** */
+ @Test
+ public void testForwardRollingUpgrade() throws Exception {
+ cleanPersistenceDir();
+ IgniteEx ign0 = startGrid(0, "2.18.0", false);
+ IgniteEx ign1 = startGrid(1, "2.18.0", client);
+ IgniteEx ign2 = startGrid(2, "2.18.0", client);
+
+ assertClusterSize(3);
+
+ assertRemoteRejected(() -> startGrid(3, "2.18.1", client));
+
+ configureRollingUpgradeVersion(ign0, "2.18.1");
+
+ for (int i = 0; i < 3; i++) {
+ int finalI = i;
+ assertTrue(waitForCondition(() ->
grid(finalI).context().rollingUpgrade().enabled(), getTestTimeout()));
}
- catch (IgniteCheckedException e) {
- StringWriter errors = new StringWriter();
- e.printStackTrace(new PrintWriter(errors));
+ ign2.close();
+
+ assertClusterSize(2);
+
+ startGrid(2, "2.18.1", client);
+
+ assertClusterSize(3);
+
+ ign1.close();
+
+ assertClusterSize(2);
+
+ startGrid(1, "2.18.1", client);
+
+ assertClusterSize(3);
- String stackTrace = errors.toString();
+ ign0.close();
- if (!stackTrace.contains("Local node and remote node have
different version numbers"))
- throw e;
+ assertClusterSize(2);
+
+ startGrid(0, "2.18.1", false);
+
+ assertClusterSize(3);
+
+ if (client)
+ grid(0).context().rollingUpgrade().disable();
+ else
+ grid(2).context().rollingUpgrade().disable();
+
+ for (int i = 0; i < 3; i++) {
+ if (!grid(i).localNode().isClient())
+ assertFalse(grid(i).context().rollingUpgrade().enabled());
}
+
+ assertRemoteRejected(() -> startGrid(3, "2.18.0", client));
}
- /**
- * @throws Exception If failed.
- */
+ /** */
+ @Test
+ public void testJoiningNodeFailed() throws Exception {
+ int joinTimeout = 5_000;
+
+ IgniteEx ign0 = startGrid(0, "2.18.0", false,
+ cfg -> {
+
((TcpDiscoverySpi)cfg.getDiscoverySpi()).setJoinTimeout(joinTimeout);
+ return cfg;
+ });
+
+ configureRollingUpgradeVersion(ign0, "2.18.1");
+
+ RunnableX runnableX = () -> startGrid(1, "2.18.1", false,
+ cfg -> {
+ TcpDiscoverySpi oldSpi =
(TcpDiscoverySpi)cfg.getDiscoverySpi();
+
+ TcpDiscoverySpi newSpi = new TcpDiscoverySpi() {
+ @Override public void setNodeAttributes(Map<String,
Object> attrs, IgniteProductVersion ver) {
+ super.setNodeAttributes(attrs, ver);
+
+ attrs.put(IgniteNodeAttributes.ATTR_BUILD_VER,
nodeVer);
+ attrs.put(IgniteNodeAttributes.ATTR_MARSHALLER,
"null");
+ }
+ };
+
+ newSpi.setIpFinder(oldSpi.getIpFinder());
+
+ return cfg.setDiscoverySpi(newSpi);
+ });
+
+ Throwable e = assertThrows(log, runnableX, IgniteException.class,
null);
+
+ assertTrue(X.hasCause(e, "Local node's marshaller differs from remote
node's marshaller", IgniteSpiException.class));
+
+ assertDisablingFails(ign0, "Can't disable rolling upgrade with
different versions in cluster");
+
+ doSleep(joinTimeout * 3);
+
+ ign0.context().rollingUpgrade().disable();
+
+ assertFalse(ign0.context().rollingUpgrade().enabled());
+ }
+
+ /** */
+ @Test
+ public void testCoordinatorChange() throws Exception {
+ IgniteEx ign0 = startGrid(0, "2.18.0", false);
+ IgniteEx ign1 = startGrid(1, "2.18.0", false);
+
+ configureRollingUpgradeVersion(ign0, "2.19.0");
+
+ startGrid(2, "2.19.0", false);
+
+ assertClusterSize(3);
+
+ ign0.close();
+ ign1.close();
+
+ assertClusterSize(1);
+
+ startGrid(0, "2.18.0", client);
+ startGrid(1, "2.19.0", client);
+
+ assertClusterSize(3);
+
+ assertRemoteRejected(() -> startGrid(4, "2.20.0", client));
+
+ assertClusterSize(3);
+ }
+
+ /** */
@Test
- public void testOsEditionDoesNotSupportRollingUpdatesClientMode() throws
Exception {
- nodeVer = "1.0.0";
+ public void testNodeRestart() throws Exception {
+ assumeTrue("Distributed metastorage is only preserved across restarts
when persistence is enabled", persistence);
+
+ for (int i = 0; i < 3; i++)
+ startGrid(i, "2.18.0", false);
+
+ assertClusterSize(3);
- startGrid(0);
+ configureRollingUpgradeVersion(grid(0), "2.18.1");
- try {
- nodeVer = "1.0.1";
+ for (int i = 0; i < 3; i++)
+ grid(i).close();
- startClientGrid(1);
+ assertClusterSize(0);
- fail("Exception has not been thrown.");
+ for (int i = 0; i < 3; i++)
+ startGrid(i, "2.18.0", false);
+
+ assertClusterSize(3);
+
+ for (int i = 0; i < 3; i++) {
+ assertTrue(grid(i).context().rollingUpgrade().enabled());
+
+ IgnitePair<IgniteProductVersion> stored =
grid(i).context().rollingUpgrade().versions();
+
+ assertEquals(F.pair(IgniteProductVersion.fromString("2.18.0"),
IgniteProductVersion.fromString("2.18.1")), stored);
+ }
+ }
+
+ /** */
+ @Test
+ public void testRollingUpgradeProcessorVersionCheck() throws Exception {
+ IgniteEx grid0 = startGrid(0, "2.18.0", false);
+ startGrid(1, "2.18.0", client);
+
+ assertClusterSize(2);
+
+ assertEnablingFails(grid0, "3.0.0", "Major versions are different");
+ assertEnablingFails(grid0, "2.19.2", "Minor version can only be
incremented by 1");
+ assertEnablingFails(grid0, "2.18.2", "Patch version can only be
incremented by 1");
+
+ IgnitePair<IgniteProductVersion> newPair =
F.pair(IgniteProductVersion.fromString("2.18.0"),
+ IgniteProductVersion.fromString("2.19.0"));
+
+ grid0.context().rollingUpgrade().enable(newPair.get2());
+
+ assertEnablingFails(grid0, "2.18.1", "Rolling upgrade is already
enabled with a different current and target version");
+
+ for (int i = 0; i < 2; i++) {
+
assertTrue(waitForCondition(grid(i).context().rollingUpgrade()::enabled,
getTestTimeout()));
+
+ assertEquals(newPair,
grid(i).context().rollingUpgrade().versions());
}
- catch (IgniteCheckedException e) {
- StringWriter errors = new StringWriter();
+ }
+
+ /**
+ * Checks that enabling rolling upgrade fails with expected error message.
+ *
+ * @param ex Ex.
+ * @param ver New version.
+ * @param errMsg Expected error message.
+ */
+ private void assertEnablingFails(IgniteEx ex, String ver, String errMsg) {
+ Throwable e = assertThrows(log,
+ () ->
ex.context().rollingUpgrade().enable(IgniteProductVersion.fromString(ver)),
+ IgniteException.class,
+ null);
+
+ assertTrue(e.getMessage().contains(errMsg));
+ }
+
+ /**
+ * Checks that disabling rolling upgrade fails with expected error message.
+ *
+ * @param ex Ex.
+ * @param errMsg Expected error message.
+ */
+ private void assertDisablingFails(IgniteEx ex, String errMsg) {
+ Throwable e = assertThrows(log,
+ () -> ex.context().rollingUpgrade().disable(),
+ IgniteException.class,
+ null);
+
+ assertTrue(e.getMessage().contains(errMsg));
+ }
+
+ /** Tests that starting a node with rejected version fails with remote
rejection. */
+ private void testConflictVersions(String acceptedVer, String rejVer,
boolean client) {
+ ThrowableSupplier<IgniteEx, Exception> sup = () -> {
+ IgniteEx ign = startGrid(0, acceptedVer, false);
+
+ startGrid(1, rejVer, client, cfg -> {
+ TcpDiscoverySpi spi = (TcpDiscoverySpi)cfg.getDiscoverySpi();
+
+ // Decrease network timeout to reduce waiting time for node
failure
+ // after it has been rejected by the coordinator due to
version conflict.
+ spi.setNetworkTimeout(1_000);
+
+ return cfg;
+ });
+
+ return ign;
+ };
+
+ assertRemoteRejected(sup);
+
+ stopAllGrids();
+ }
+
+ /** Checks that the third grid is not compatible when rolling upgrade
version is set. */
+ private void testConflictVersionsWithRollingUpgrade(String acceptedVer1,
String acceptedVer2, String rejVer,
+ boolean client, String rollUpVer) throws Exception {
+ ThrowableSupplier<IgniteEx, Exception> sup = () -> {
+ IgniteEx ign = startGrid(0, acceptedVer1, false);
+
+ configureRollingUpgradeVersion(ign, rollUpVer);
+
+ startGrid(1, acceptedVer2, client);
- e.printStackTrace(new PrintWriter(errors));
+ startGrid(2, rejVer, client);
- String stackTrace = errors.toString();
+ return ign;
+ };
+
+ assertRemoteRejected(sup);
+
+ stopAllGrids();
+
+ cleanPersistenceDir();
+ }
+
+ /** Checks that remote node rejected due to incompatible version. */
+ private void assertRemoteRejected(ThrowableSupplier<IgniteEx, Exception>
gridStart) {
+ Throwable e = assertThrows(log, gridStart::get,
IgniteCheckedException.class, null);
+
+ assertTrue(X.hasCause(e, "Remote node rejected due to incompatible
version for cluster join", IgniteSpiException.class));
+ }
+
+ /** Tests two compatible grids. */
+ private void testCompatibleVersions(String acceptedVer1,
+ String acceptedVer2,
+ boolean client,
+ String rollUpVerCheck) throws Exception {
+ IgniteEx grid = startGrid(0, acceptedVer1, false);
+
+ if (rollUpVerCheck != null)
+ configureRollingUpgradeVersion(grid, rollUpVerCheck);
+
+ startGrid(1, acceptedVer2, client);
+
+ assertClusterSize(2);
+
+ stopAllGrids();
+
+ if (persistence)
+ cleanPersistenceDir();
+ }
+
+ /** Tests three compatible grids. */
+ private void testCompatibleVersions(
+ String acceptedVer1,
+ String acceptedVer2,
+ String acceptedVer3,
+ boolean client,
+ String rollUpVerCheck
+ ) throws Exception {
+ IgniteEx grid = startGrid(0, acceptedVer1, false);
+
+ if (rollUpVerCheck != null)
+ configureRollingUpgradeVersion(grid, rollUpVerCheck);
+
+ startGrid(1, acceptedVer2, client);
+ startGrid(2, acceptedVer3, client);
- if (!stackTrace.contains("Local node and remote node have
different version numbers"))
- throw e;
+ assertClusterSize(3);
+
+ stopAllGrids();
+
+ if (persistence)
+ cleanPersistenceDir();
+ }
+
+ /** Starts grid with required version. */
+ private IgniteEx startGrid(int idx, String ver, boolean isClient) throws
Exception {
+ return startGrid(idx, ver, isClient, null);
+ }
+
+ /** Starts grid with required version and custom configuration. */
+ private IgniteEx startGrid(int idx, String ver, boolean isClient,
UnaryOperator<IgniteConfiguration> cfgOp) throws Exception {
+ nodeVer = ver;
+
+ IgniteEx ign = isClient ? startClientGrid(idx, cfgOp) : startGrid(idx,
cfgOp);
+
+ if (persistence)
+ ign.cluster().state(ClusterState.ACTIVE);
+
+ return ign;
+ }
+
+ /**
+ * @param ver Version for rolling upgrade support.
+ */
+ private void configureRollingUpgradeVersion(IgniteEx grid, String ver)
throws IgniteCheckedException {
+ if (ver == null) {
+ grid.context().rollingUpgrade().disable();
+ return;
}
+
+ IgniteProductVersion target = IgniteProductVersion.fromString(ver);
+
+ grid.context().rollingUpgrade().enable(target);
+ }
+
+ /**
+ * @param size Expected cluster size.
+ */
+ private void assertClusterSize(int size) throws
IgniteInterruptedCheckedException {
+ assertTrue("Expected cluster size: " + size + ", but was: " +
Ignition.allGrids().size(),
+ waitForCondition(() -> Ignition.allGrids().size() == size,
getTestTimeout()));
}
}