diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWalTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWalTest.java index 4417847b18d96..b57e0176dda10 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWalTest.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerWalTest.java @@ -173,33 +173,117 @@ public void testWalStateInMemoryCdcCluster() throws Exception { } /** - * Test WAL mode change for a cache group contains multiple caches. + * Test for WAL enable/disable commands. * @throws Exception If failed. */ @Test - public void testWalChangeForMultiCacheGroup() throws Exception { + public void testWalManagementOperations() throws Exception { clusterState = 0; // PDS cluster. IgniteEx srv = startGrids(2); srv.cluster().state(ClusterState.ACTIVE); srv.createCache(new CacheConfiguration<>("cache1") - .setGroupName("testGroup")); + .setGroupName("group1")); srv.createCache(new CacheConfiguration<>("cache2") - .setGroupName("testGroup")); + .setGroupName("group1")); + srv.createCache(new CacheConfiguration<>("cache3") + .setGroupName("group2")); + srv.createCache("cache4"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "group1,group2,cache4")); + outputContains(".*group1.*true.*true.*true.*true.*false"); + outputContains(".*group2.*true.*true.*true.*true.*false"); + outputContains(".*cache4.*true.*true.*true.*true.*false"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "disable", "--groups", "group1")); + outputContains("Successfully disabled WAL for groups:"); + outputContains("group1"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "group1")); + outputContains(".*group1.*true.*false.*true.*true.*false"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "disable", "--groups", "group1,group2")); + outputContains("Successfully disabled WAL for groups:"); + outputContains("group1"); + outputContains("group2"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "group1,group2")); + outputContains(".*group1.*true.*false.*true.*true.*false"); + outputContains(".*group2.*true.*false.*true.*true.*false"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "disable", "--groups", "cache4,nonExistentGroup")); + outputContains("Successfully disabled WAL for groups:"); + outputContains("cache4"); + outputContains("Errors occurred:"); + outputContains("Cache group not found: nonExistentGroup"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "cache4")); + outputContains(".*cache4.*true.*false.*true.*true.*false"); + + //Error when using cache name instead of group name + assertEquals(EXIT_CODE_OK, execute("--wal", "enable", "--groups", "cache3")); + outputContains("Errors occurred:"); + outputContains("Cache group not found: cache3"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "enable", "--groups", "group2,cache4")); + outputContains("Successfully enabled WAL for groups:"); + outputContains("group2"); + outputContains("cache4"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "group2,cache4")); + outputContains(".*group2.*true.*true.*true.*true.*false"); + outputContains(".*cache4.*true.*true.*true.*true.*false"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "disable")); + outputContains("Successfully disabled WAL for groups:"); + outputContains("group1"); + outputContains("group2"); + outputContains("cache4"); - assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "testGroup")); - outputContains(".*testGroup.*true.*true.*true.*true.*false"); + assertEquals(EXIT_CODE_OK, execute("--wal", "state")); + outputContains(".*group1.*true.*false.*true.*true.*false"); + outputContains(".*group2.*true.*false.*true.*true.*false"); + outputContains(".*cache4.*true.*false.*true.*true.*false"); + + assertEquals(EXIT_CODE_OK, execute("--wal", "enable")); + outputContains("Successfully enabled WAL for groups:"); + outputContains("group1"); + outputContains("group2"); + outputContains("cache4"); - assertEquals(EXIT_CODE_OK, execute("--wal", "disable", "--groups", "testGroup")); + assertEquals(EXIT_CODE_OK, execute("--wal", "state")); + outputContains(".*group1.*true.*true.*true.*true.*false"); + outputContains(".*group2.*true.*true.*true.*true.*false"); + outputContains(".*cache4.*true.*true.*true.*true.*false"); + } + + /** + * Test WAL mode change attempts for non-persistent cache groups. + * @throws Exception If failed. + */ + @Test + public void testWalChangeForNonPersistentCaches() throws Exception { + IgniteConfiguration cfg = getConfiguration(getTestIgniteInstanceName(0)); + cfg.setDataStorageConfiguration(new DataStorageConfiguration() + .setDefaultDataRegionConfiguration(new DataRegionConfiguration() + .setPersistenceEnabled(false))); + + IgniteEx srv = startGrid(cfg); + srv.cluster().state(ClusterState.ACTIVE); + + srv.createCache("cache1"); - assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "testGroup")); - outputContains(".*testGroup.*true.*false.*true.*true.*false"); + assertEquals(EXIT_CODE_OK, execute("--wal", "enable", "--groups", "cache1")); + outputContains("Errors occurred:"); + outputContains("cache1.*Cannot change WAL mode because persistence is not enabled for cache\\(s\\)"); - assertEquals(EXIT_CODE_OK, execute("--wal", "enable", "--groups", "testGroup")); + assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "cache1")); + outputContains(".*cache1.*false.*false.*true.*true.*false"); - assertEquals(EXIT_CODE_OK, execute("--wal", "state", "--groups", "testGroup")); - outputContains(".*testGroup.*true.*true.*true.*true.*false"); + assertEquals(EXIT_CODE_OK, execute("--wal", "disable", "--groups", "cache1")); + outputContains("Errors occurred:"); + outputContains("cache1.*Cannot change WAL mode because persistence is not enabled for cache\\(s\\)"); } /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalDisableCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalDisableCommand.java index 9802dc318646c..be71045056b77 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalDisableCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalDisableCommand.java @@ -17,10 +17,12 @@ package org.apache.ignite.internal.management.wal; +import java.util.List; +import java.util.function.Consumer; import org.apache.ignite.internal.management.api.ComputeCommand; /** */ -public class WalDisableCommand implements ComputeCommand { +public class WalDisableCommand implements ComputeCommand { /** {@inheritDoc} */ @Override public Class taskClass() { return WalSetStateTask.class; @@ -41,6 +43,25 @@ public class WalDisableCommand implements ComputeCommand printer) { + String operation = arg instanceof WalEnableCommand.WalEnableCommandArg ? "enable" : "disable"; + List successGrps = res.successGroups(); + List errors = res.errorMessages(); + + if (!successGrps.isEmpty()) { + printer.accept("Successfully " + operation + "d WAL for groups:"); + for (String grp : successGrps) + printer.accept(" " + grp); + } + + if (errors != null && !errors.isEmpty()) { + printer.accept("Errors occurred:"); + for (String error : errors) + printer.accept(" " + error); + } + } + /** */ public static class WalDisableCommandArg extends WalStateCommandArg { /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalEnableCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalEnableCommand.java index 950f416e54a9c..1ea9a7d527c52 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalEnableCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalEnableCommand.java @@ -17,11 +17,12 @@ package org.apache.ignite.internal.management.wal; +import java.util.function.Consumer; import org.apache.ignite.internal.management.api.ComputeCommand; import org.apache.ignite.internal.management.wal.WalDisableCommand.WalDisableCommandArg; /** */ -public class WalEnableCommand implements ComputeCommand { +public class WalEnableCommand implements ComputeCommand { /** {@inheritDoc} */ @Override public Class taskClass() { return WalSetStateTask.class; @@ -37,6 +38,12 @@ public class WalEnableCommand implements ComputeCommand printer) { + WalDisableCommand cmd = new WalDisableCommand(); + cmd.printResult(arg, res, printer); + } + /** */ public static class WalEnableCommandArg extends WalDisableCommandArg { /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTask.java b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTask.java index 017dd729bb50c..6075a043eaea2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTask.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.management.wal; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -32,22 +33,42 @@ import org.jetbrains.annotations.Nullable; /** */ -public class WalSetStateTask extends VisorMultiNodeTask { +public class WalSetStateTask extends VisorMultiNodeTask { /** */ private static final long serialVersionUID = 0; /** {@inheritDoc} */ - @Override protected VisorJob job(WalDisableCommandArg arg) { - return new WalDisableJob(arg, false); + @Override protected VisorJob job(WalDisableCommandArg arg) { + return new WalDisableJob(arg, debug); } /** {@inheritDoc} */ - @Override protected @Nullable Void reduce0(List res) throws IgniteException { - return null; + @Override protected @Nullable WalSetStateTaskResult reduce0(List res) throws IgniteException { + Set successGrps = new HashSet<>(); + List errors = new ArrayList<>(); + + for (ComputeJobResult jobRes : res) { + if (jobRes.getException() != null) { + Throwable e = jobRes.getException(); + errors.add("Node " + jobRes.getNode().consistentId() + ": Task execution failed - " + e.getMessage()); + } + else { + WalSetStateTaskResult result = jobRes.getData(); + if (result.successGroups() != null) + successGrps.addAll(result.successGroups()); + if (!Boolean.TRUE.equals(result.success()) && result.errorMessages() != null) + errors.addAll(result.errorMessages()); + } + } + + if (errors.isEmpty()) + return new WalSetStateTaskResult(new ArrayList<>(successGrps)); + else + return new WalSetStateTaskResult(new ArrayList<>(successGrps), errors); } /** */ - private static class WalDisableJob extends VisorJob { + private static class WalDisableJob extends VisorJob { /** */ private static final long serialVersionUID = 0; @@ -57,22 +78,50 @@ protected WalDisableJob(@Nullable WalDisableCommandArg arg, boolean debug) { } /** {@inheritDoc} */ - @Override protected Void run(@Nullable WalDisableCommandArg arg) throws IgniteException { - Set grps = F.isEmpty(arg.groups()) ? null : new HashSet<>(Arrays.asList(arg.groups())); + @Override protected WalSetStateTaskResult run(@Nullable WalDisableCommandArg arg) throws IgniteException { + Set requestedGrps = F.isEmpty(arg.groups()) ? null : new HashSet<>(Arrays.asList(arg.groups())); + boolean isEnable = arg instanceof WalEnableCommandArg; + List successGrps = new ArrayList<>(); + List errors = new ArrayList<>(); + + try { + Set availableGrps = new HashSet<>(); - for (CacheGroupContext gctx : ignite.context().cache().cacheGroups()) { - String grpName = gctx.cacheOrGroupName(); + for (CacheGroupContext gctx : ignite.context().cache().cacheGroups()) { + String grpName = gctx.cacheOrGroupName(); + availableGrps.add(grpName); - if (grps != null && !grps.contains(grpName)) - continue; + if (requestedGrps != null && !requestedGrps.contains(grpName)) + continue; - if (arg instanceof WalEnableCommandArg) - ignite.cluster().enableWal(grpName); + try { + if (isEnable) + ignite.cluster().enableWal(grpName); + else + ignite.cluster().disableWal(grpName); + + successGrps.add(grpName); + } + catch (Exception e) { + errors.add("Failed to " + (isEnable ? "enable" : "disable") + + " WAL for cache group: " + grpName + " - " + e.getMessage()); + } + } + + for (String requestedGrp : requestedGrps) { + if (!availableGrps.contains(requestedGrp)) + errors.add("Cache group not found: " + requestedGrp); + } + + if (errors.isEmpty()) + return new WalSetStateTaskResult(successGrps); else - ignite.cluster().disableWal(grpName); + return new WalSetStateTaskResult(successGrps, errors); + } + catch (Exception e) { + errors.add("Failed to execute operation - " + e.getMessage()); + return new WalSetStateTaskResult(successGrps, errors); } - - return null; } } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTaskResult.java b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTaskResult.java new file mode 100644 index 0000000000000..6cf254c5e7a16 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/wal/WalSetStateTaskResult.java @@ -0,0 +1,106 @@ +/* + * 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.management.wal; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.ArrayList; +import java.util.List; +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** + * Result of WAL enable/disable operation. + */ +public class WalSetStateTaskResult extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0L; + + /** Success flag. */ + private Boolean success; + + /** Successfully processed groups. */ + private List successGrps; + + /** Error messages if operation failed. */ + private List errMsgs; + + /** Default constructor. */ + public WalSetStateTaskResult() { + // No-op. + } + + /** + * Constructor for success. + * + * @param successGrps Successfully processed groups. + */ + public WalSetStateTaskResult(List successGrps) { + this.success = true; + this.successGrps = new ArrayList<>(successGrps); + this.errMsgs = null; + } + + /** + * Constructor for failure. + * + * @param successGrps Successfully processed groups. + * @param errMsgs Error messages. + */ + public WalSetStateTaskResult(List successGrps, List errMsgs) { + this.success = false; + this.successGrps = new ArrayList<>(successGrps); + this.errMsgs = new ArrayList<>(errMsgs); + } + + /** {@inheritDoc} */ + @Override protected void writeExternalData(ObjectOutput out) throws IOException { + out.writeObject(success); + U.writeCollection(out, successGrps); + U.writeCollection(out, errMsgs); + } + + /** {@inheritDoc} */ + @Override protected void readExternalData(ObjectInput in) throws IOException, ClassNotFoundException { + success = (Boolean)in.readObject(); + successGrps = U.readList(in); + errMsgs = U.readList(in); + } + + /** + * @return Success flag. + */ + public Boolean success() { + return success; + } + + /** + * @return Successfully processed groups. + */ + public List successGroups() { + return successGrps; + } + + /** + * @return Error messages if operation failed. + */ + public List errorMessages() { + return errMsgs; + } +}