timoninmaxim commented on code in PR #12301: URL: https://github.com/apache/ignite/pull/12301#discussion_r2460031617
########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + if (metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY) == null) { + if (log.isInfoEnabled()) + log.info("Rolling upgrade is already disabled"); + return; Review Comment: add NL before ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ Review Comment: Let's add a doc which threads we synchronize and for which reason ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + if (metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY) == null) { + if (log.isInfoEnabled()) + log.info("Rolling upgrade is already disabled"); Review Comment: I suppose we should throw here an exception instead ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; Review Comment: no need initialize it with null ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { Review Comment: Let's forbid for non-TcpDiscoverySpi ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); Review Comment: Run on coordinator only ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; Review Comment: Version -> Ver ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); Review Comment: Let's log only cluster-specific events - enable / disable. Other messages for admin only ########## modules/core/src/main/java/org/apache/ignite/internal/processors/nodevalidation/OsDiscoveryNodeValidationProcessor.java: ########## @@ -43,31 +45,50 @@ public OsDiscoveryNodeValidationProcessor(GridKernalContext ctx) { @Nullable @Override public IgniteNodeValidationResult validateNode(ClusterNode node) { ClusterNode locNode = ctx.discovery().localNode(); - // Check version. Review Comment: You don't need this processor anymore ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; Review Comment: see TcpDiscoverySpi#getJoinTimeout ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + if (metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY) == null) { + if (log.isInfoEnabled()) + log.info("Rolling upgrade is already disabled"); + return; + } + + synchronized (lock) { + if (lastJoiningNode != null && U.currentTimeMillis() - lastJoiningNodeTimestamp > JOINING_TIMEOUT) + lastJoiningNode = null; + + if (minMaxVersionSupplier == null) { + if (log.isDebugEnabled()) + log.debug("Using local node version as min/max version"); + + IgniteProductVersion curVer = IgniteProductVersion.fromString(ctx.discovery().localNode().attribute(ATTR_BUILD_VER)); + minMaxVersionSupplier = () -> F.pair(curVer, curVer); + } + + IgnitePair<IgniteProductVersion> minMaxVerPair = minMaxVersionSupplier.get(); + + Set<IgniteProductVersion> vers = new HashSet<>(); + + vers.add(minMaxVerPair.get1()); + + vers.add(minMaxVerPair.get2()); + + if (lastJoiningNode != null) + vers.add(lastJoiningNode.version()); + + if (vers.size() > 1) { + String msg = "Can't disable rolling upgrade with different versions in cluster: " + vers; + log.warning(msg); + throw new IgniteCheckedException(msg); + } + + metastorage.remove(ROLLING_UPGRADE_VERSIONS_KEY); Review Comment: Blocking operation that might block discovery thread ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; Review Comment: Must validate it with timeout ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + if (metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY) == null) { + if (log.isInfoEnabled()) + log.info("Rolling upgrade is already disabled"); + return; + } + + synchronized (lock) { + if (lastJoiningNode != null && U.currentTimeMillis() - lastJoiningNodeTimestamp > JOINING_TIMEOUT) + lastJoiningNode = null; + + if (minMaxVersionSupplier == null) { + if (log.isDebugEnabled()) + log.debug("Using local node version as min/max version"); + + IgniteProductVersion curVer = IgniteProductVersion.fromString(ctx.discovery().localNode().attribute(ATTR_BUILD_VER)); + minMaxVersionSupplier = () -> F.pair(curVer, curVer); + } + + IgnitePair<IgniteProductVersion> minMaxVerPair = minMaxVersionSupplier.get(); + + Set<IgniteProductVersion> vers = new HashSet<>(); + + vers.add(minMaxVerPair.get1()); + + vers.add(minMaxVerPair.get2()); + + if (lastJoiningNode != null) + vers.add(lastJoiningNode.version()); + + if (vers.size() > 1) { + String msg = "Can't disable rolling upgrade with different versions in cluster: " + vers; Review Comment: This message is for admin only, let's not WARN here. ########## modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java: ########## @@ -0,0 +1,264 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.function.Supplier; +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.util.lang.IgnitePair; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; +import org.apache.ignite.spi.IgniteNodeValidationResult; +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 { + /** 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"; + + /** Joining timeout. */ + private static final long JOINING_TIMEOUT = 2_000; + + /** Metastorage with the write access. */ + @Nullable private volatile DistributedMetaStorage metastorage; + + /** Min max version of nodes in cluster supplier. */ + private Supplier<IgnitePair<IgniteProductVersion>> minMaxVersionSupplier; + + /** Last joining node. */ + private ClusterNode lastJoiningNode = null; + + /** Last joining node timestamp. */ + private long lastJoiningNodeTimestamp; + + /** */ + private final Object lock = new Object(); + + /** + * @param ctx Context. + */ + public RollingUpgradeProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public void start() throws IgniteCheckedException { + ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new DistributedMetastorageLifecycleListener() { + @Override public void onReadyForWrite(DistributedMetaStorage metastorage) { + RollingUpgradeProcessor.this.metastorage = metastorage; + } + }); + } + + /** {@inheritDoc} */ + @Override public @Nullable IgniteNodeValidationResult validateNode(ClusterNode node) { + synchronized (lock) { + lastJoiningNode = node; + + lastJoiningNodeTimestamp = U.currentTimeMillis(); + } + return null; + } + + /** + * Enables rolling upgrade with specified target version. + * + * @param target Target version. + * @throws IgniteCheckedException If versions are incorrect or metastorage is not available. + */ + public void enable(IgniteProductVersion target) throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + String curBuildVer = ctx.discovery().localNode().attribute(ATTR_BUILD_VER); + IgniteProductVersion curVer = IgniteProductVersion.fromString(curBuildVer); + + if (!checkVersions(curVer, target)) + return; + + IgnitePair<IgniteProductVersion> newPair = F.pair(curVer, target); + + if (!metastorage.compareAndSet(ROLLING_UPGRADE_VERSIONS_KEY, null, newPair)) { + IgnitePair<IgniteProductVersion> existingVerPair = metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); + + String errMsg = "Rolling upgrade is already enabled with a different current and target version: " + + existingVerPair.get1() + " , " + existingVerPair.get2(); + + log.warning(errMsg); + + throw new IgniteCheckedException(errMsg); + } + + if (log.isInfoEnabled()) + log.info("Rolling upgrade enabled [current=" + curVer + ", target=" + target + ']'); + } + + /** + * Disables rolling upgrade. + * + * @throws IgniteCheckedException If metastorage is not available. + */ + public void disable() throws IgniteCheckedException { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + if (metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY) == null) { + if (log.isInfoEnabled()) + log.info("Rolling upgrade is already disabled"); + return; + } + + synchronized (lock) { + if (lastJoiningNode != null && U.currentTimeMillis() - lastJoiningNodeTimestamp > JOINING_TIMEOUT) + lastJoiningNode = null; + + if (minMaxVersionSupplier == null) { + if (log.isDebugEnabled()) + log.debug("Using local node version as min/max version"); + + IgniteProductVersion curVer = IgniteProductVersion.fromString(ctx.discovery().localNode().attribute(ATTR_BUILD_VER)); + minMaxVersionSupplier = () -> F.pair(curVer, curVer); + } + + IgnitePair<IgniteProductVersion> minMaxVerPair = minMaxVersionSupplier.get(); + + Set<IgniteProductVersion> vers = new HashSet<>(); + + vers.add(minMaxVerPair.get1()); + + vers.add(minMaxVerPair.get2()); + + if (lastJoiningNode != null) + vers.add(lastJoiningNode.version()); + + if (vers.size() > 1) { + String msg = "Can't disable rolling upgrade with different versions in cluster: " + vers; + log.warning(msg); + throw new IgniteCheckedException(msg); + } + + metastorage.remove(ROLLING_UPGRADE_VERSIONS_KEY); + + if (log.isInfoEnabled()) + log.info("Rolling upgrade disabled"); + } + } + + /** + * 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. + */ + public IgnitePair<IgniteProductVersion> versions() { + A.notNull(metastorage, "Metastorage not ready. Node not started?"); + + try { + return metastorage.read(ROLLING_UPGRADE_VERSIONS_KEY); Review Comment: Let's use volatile versions value on class-level -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
