blob: 762ac0ccff27adfef786b3d8a4dfe27613500677 [file] [log] [blame]
/*
* 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.util;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteCluster;
import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.AtomicConfiguration;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.commandline.CommandHandler;
import org.apache.ignite.internal.commandline.CommandList;
import org.apache.ignite.internal.commandline.CommonArgParser;
import org.apache.ignite.internal.commandline.argument.CommandArg;
import org.apache.ignite.internal.commandline.cache.CacheSubcommands;
import org.apache.ignite.internal.pagemem.wal.record.DataEntry;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.CacheObjectImpl;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.datastructures.GridCacheInternalKeyImpl;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.tx.VisorTxTaskResult;
import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.testframework.junits.GridAbstractTest;
import org.apache.ignite.testframework.junits.WithSystemProperty;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionRollbackException;
import org.apache.ignite.transactions.TransactionState;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.toList;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_ENABLE_EXPERIMENTAL_COMMAND;
import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_CONNECTION_FAILED;
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS;
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
import static org.apache.ignite.internal.commandline.CommandHandler.UTILITY_NAME;
import static org.apache.ignite.internal.commandline.CommandList.BASELINE;
import static org.apache.ignite.internal.commandline.CommandList.METADATA;
import static org.apache.ignite.internal.commandline.CommandList.TRACING_CONFIGURATION;
import static org.apache.ignite.internal.commandline.CommandList.WAL;
import static org.apache.ignite.internal.commandline.CommonArgParser.CMD_VERBOSE;
import static org.apache.ignite.internal.commandline.OutputFormat.MULTI_LINE;
import static org.apache.ignite.internal.commandline.OutputFormat.SINGLE_LINE;
import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.HELP;
import static org.apache.ignite.testframework.GridTestUtils.assertContains;
import static org.apache.ignite.testframework.GridTestUtils.assertNotContains;
import static org.apache.ignite.testframework.GridTestUtils.readResource;
import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED;
/**
* Command line handler test.
* You can use this class if you don't need create nodes for each test because
* here create {@link #SERVER_NODE_CNT} server and 1 client nodes at before all
* tests. If you need create nodes for each test you can use
* {@link GridCommandHandlerTest}
*/
public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClusterByClassAbstractTest {
/** Special word for defining any char sequence from special word to the end of line in golden copy of help output */
private static final String ANY = "<!any!>";
/** Error stack trace prefix. */
protected static final String ERROR_STACK_TRACE_PREFIX = "Error stack trace:";
/**
* Very basic tests for running the command in different environment which other command are running in.
*/
@Test
public void testFindAndDeleteGarbage() {
Ignite ignite = crd;
injectTestSystemOut();
ignite.createCaches(Arrays.asList(
new CacheConfiguration<>("garbage1").setGroupName("groupGarbage"),
new CacheConfiguration<>("garbage2").setGroupName("groupGarbage")));
assertEquals(EXIT_CODE_OK, execute("--cache", "find_garbage", "--port", "11212"));
assertContains(log, testOut.toString(), "garbage not found");
assertEquals(EXIT_CODE_OK, execute("--cache", "find_garbage",
ignite(0).localNode().id().toString(), "--port", "11212"));
assertContains(log, testOut.toString(), "garbage not found");
assertEquals(EXIT_CODE_OK, execute("--cache", "find_garbage",
"groupGarbage", "--port", "11212"));
assertContains(log, testOut.toString(), "garbage not found");
}
/**
* Test that baseline auto_adjustment settings update works via control.sh
*/
@Test
public void testBaselineAutoAdjustmentSettings() {
Ignite ignite = crd;
IgniteCluster cl = ignite.cluster();
assertFalse(cl.isBaselineAutoAdjustEnabled());
long timeout = cl.baselineAutoAdjustTimeout();
assertEquals(EXIT_CODE_OK, execute(
"--baseline",
"auto_adjust",
"enable",
"timeout",
Long.toString(timeout + 1)
));
assertTrue(cl.isBaselineAutoAdjustEnabled());
assertEquals(timeout + 1, cl.baselineAutoAdjustTimeout());
assertEquals(EXIT_CODE_OK, execute("--baseline", "auto_adjust", "disable"));
assertFalse(cl.isBaselineAutoAdjustEnabled());
assertEquals(timeout + 1, cl.baselineAutoAdjustTimeout());
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--baseline", "auto_adjust"));
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--baseline", "auto_adjust", "true"));
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--baseline", "auto_adjust", "enable", "x"));
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--baseline", "auto_adjust", "disable", "x"));
}
/**
* Smoke test for --tx --info command.
*/
@Test
public void testTransactionInfo() throws Exception {
client.getOrCreateCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
.setAtomicityMode(TRANSACTIONAL).setBackups(1).setWriteSynchronizationMode(FULL_SYNC));
for (Ignite ig : G.allGrids())
assertNotNull(ig.cache(DEFAULT_CACHE_NAME));
CountDownLatch lockLatch = new CountDownLatch(1);
CountDownLatch unlockLatch = new CountDownLatch(1);
IgniteInternalFuture<?> fut = startTransactions("testTransactionInfo", lockLatch, unlockLatch, false);
try {
U.awaitQuiet(lockLatch);
doSleep(3000); // Should be more than enough for all transactions to appear in contexts.
Set<GridCacheVersion> nearXids = new HashSet<>();
for (int i = 0; i < SERVER_NODE_CNT; i++) {
IgniteEx grid = grid(i);
IgniteTxManager tm = grid.context().cache().context().tm();
for (IgniteInternalTx tx : tm.activeTransactions())
nearXids.add(tx.nearXidVersion());
}
injectTestSystemOut();
for (GridCacheVersion nearXid : nearXids) {
assertEquals(EXIT_CODE_OK, execute("--tx", "--info", nearXid.toString()));
String out = testOut.toString();
assertContains(log, out, nearXid.toString());
}
}
finally {
unlockLatch.countDown();
fut.get();
}
}
/**
* Smoke test for historical mode of --tx --info command.
*/
@Test
public void testTransactionHistoryInfo() throws Exception {
client.getOrCreateCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
.setAtomicityMode(TRANSACTIONAL).setBackups(2).setWriteSynchronizationMode(FULL_SYNC));
for (Ignite ig : G.allGrids())
assertNotNull(ig.cache(DEFAULT_CACHE_NAME));
CountDownLatch lockLatch = new CountDownLatch(1);
CountDownLatch unlockLatch = new CountDownLatch(1);
IgniteInternalFuture<?> fut = startTransactions("testTransactionHistoryInfo", lockLatch, unlockLatch, false);
U.awaitQuiet(lockLatch);
doSleep(3000); // Should be more than enough for all transactions to appear in contexts.
Set<GridCacheVersion> nearXids = new HashSet<>();
for (int i = 0; i < SERVER_NODE_CNT; i++) {
IgniteEx grid = grid(i);
IgniteTxManager tm = grid.context().cache().context().tm();
for (IgniteInternalTx tx : tm.activeTransactions())
nearXids.add(tx.nearXidVersion());
}
unlockLatch.countDown();
fut.get();
doSleep(3000); // Should be more than enough for all transactions to disappear from contexts after finish.
injectTestSystemOut();
boolean commitMatched = false;
boolean rollbackMatched = false;
for (GridCacheVersion nearXid : nearXids) {
assertEquals(EXIT_CODE_OK, execute("--tx", "--info", nearXid.toString()));
String out = testOut.toString();
assertContains(log, out, "Transaction was found in completed versions history of the following nodes:");
if (out.contains(TransactionState.COMMITTED.name())) {
commitMatched = true;
assertNotContains(log, out, TransactionState.ROLLED_BACK.name());
}
if (out.contains(TransactionState.ROLLED_BACK.name())) {
rollbackMatched = true;
assertNotContains(log, out, TransactionState.COMMITTED.name());
}
}
assertTrue(commitMatched);
assertTrue(rollbackMatched);
}
/** */
@Test
public void testCacheHelp() throws Exception {
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "help"));
String output = testOut.toString();
for (CacheSubcommands cmd : CacheSubcommands.values()) {
if (cmd != HELP) {
assertContains(log, output, cmd.toString());
Class<? extends Enum<? extends CommandArg>> args = cmd.getCommandArgs();
if (args != null)
for (Enum<? extends CommandArg> arg : args.getEnumConstants())
assertTrue(cmd + " " + arg, output.contains(arg.toString()));
}
else
assertContains(log, output, CommandHandler.UTILITY_NAME);
}
checkHelp(output, "org.apache.ignite.util/" + getClass().getSimpleName() + "_cache_help.output");
}
/** */
@Test
public void testCorrectCacheOptionsNaming() {
Pattern p = Pattern.compile("^--([a-z]+(-)?)+([a-z]+)");
for (CacheSubcommands cmd : CacheSubcommands.values()) {
Class<? extends Enum<? extends CommandArg>> args = cmd.getCommandArgs();
if (args != null)
for (Enum<? extends CommandArg> arg : args.getEnumConstants())
assertTrue(arg.toString(), p.matcher(arg.toString()).matches());
}
}
/** */
@Test
public void testHelp() throws Exception {
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--help"));
String testOutStr = testOut.toString();
for (CommandList cmd : CommandList.values())
assertContains(log, testOutStr, cmd.toString());
assertNotContains(log, testOutStr, "Control.sh");
checkHelp(testOutStr, "org.apache.ignite.util/" + getClass().getSimpleName() + "_help.output");
}
/**
* Checks that golden copy of output and current output are same.
*
* @param output Current output.
* @param resourceName Name of resource with golden copy on output.
* @throws Exception If something goes wrong.
*/
private void checkHelp(String output, String resourceName) throws Exception {
String correctOutput = new String(readResource(getClass().getClassLoader(), resourceName));
try {
// Split by lines.
List<String> correctOutputLines = U.sealList(correctOutput.split("\\r?\\n"));
List<String> outputLines = U.sealList(output.split("\\r?\\n"));
assertEquals("Wrong number of lines! Golden copy resource: " + resourceName, correctOutputLines.size(), outputLines.size());
for (int i = 0; i < correctOutputLines.size(); i++) {
String cLine = correctOutputLines.get(i);
// Remove all spaces from end of line.
String line = outputLines.get(i).replaceAll("\\s+$", "");
if (cLine.contains(ANY)) {
String cuttedCLine = cLine.substring(0, cLine.length() - ANY.length());
assertTrue("line: " + i, line.startsWith(cuttedCLine));
}
else
assertEquals("line: " + i, cLine, line);
}
}
catch (AssertionError e) {
log.info("Correct output is: " + correctOutput);
throw e;
}
}
/** */
@Test
public void testOldReadOnlyApiNotAvailable() {
injectTestSystemOut();
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--read-only-on"));
assertContains(log, testOut.toString(), "Check arguments. Unexpected argument: --read-only-on");
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--read-only-off"));
assertContains(log, testOut.toString(), "Check arguments. Unexpected argument: --read-only-off");
}
/** */
@Test
public void testPrintTimestampAtEndsOfExecution() {
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute());
String testOutStr = testOut.toString();
assertContains(log, testOutStr, "Control utility has completed execution at: ");
}
/** */
@Test
public void testCacheIdleVerify() {
IgniteEx ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify"));
assertContains(log, testOut.toString(), "no conflicts have been found");
HashSet<Integer> clearKeys = new HashSet<>(asList(1, 2, 3, 4, 5, 6));
ignite.context().cache().cache(DEFAULT_CACHE_NAME).clearLocallyAll(clearKeys, true, true, true);
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify"));
assertContains(log, testOut.toString(), "conflict partitions");
}
/** */
@Test
public void testCacheIdleVerifyNodeFilter() {
IgniteEx ignite = crd;
Object lastNodeCId = ignite.localNode().consistentId();
ignite.createCache(new CacheConfiguration<>(DEFAULT_CACHE_NAME)
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setNodeFilter(node -> !node.consistentId().equals(lastNodeCId))
.setBackups(1));
try (IgniteDataStreamer streamer = ignite.dataStreamer(DEFAULT_CACHE_NAME)) {
for (int i = 0; i < 100; i++)
streamer.addData(i, i);
}
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", DEFAULT_CACHE_NAME));
assertContains(log, testOut.toString(), "no conflicts have been found");
}
/**
* Tests that both update counter and hash conflicts are detected.
*/
@Test
public void testCacheIdleVerifyTwoConflictTypes() {
IgniteEx ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify"));
assertContains(log, testOut.toString(), "no conflicts have been found");
GridCacheContext<Object, Object> cacheCtx = ignite.cachex(DEFAULT_CACHE_NAME).context();
corruptDataEntry(cacheCtx, 1, true, false);
corruptDataEntry(cacheCtx, 1 + cacheCtx.config().getAffinity().partitions() / 2, false, true);
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify"));
assertContains(log, testOut.toString(), "found 2 conflict partitions");
}
/**
* Tests that empty partitions with non-zero update counter are not included into the idle_verify dump.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpSkipZerosUpdateCounters() throws Exception {
IgniteEx ignite = crd;
int emptyPartId = 31;
// Less than parts number for ability to check skipZeros flag.
createCacheAndPreload(ignite, emptyPartId);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--skip-zeros", DEFAULT_CACHE_NAME));
Matcher fileNameMatcher = dumpFileNameMatcher();
assertTrue(fileNameMatcher.find());
String zeroUpdateCntrs = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, zeroUpdateCntrs, "The check procedure has finished, found " + emptyPartId + " partitions");
assertContains(log, zeroUpdateCntrs, "1 partitions was skipped");
assertContains(log, zeroUpdateCntrs, "The check procedure has finished, no conflicts have been found.");
assertSort(emptyPartId, zeroUpdateCntrs);
// The result of the following cache operations is that
// the size of the 32-th partition is equal to zero and update counter is equal to 2.
ignite.cache(DEFAULT_CACHE_NAME).put(emptyPartId, emptyPartId);
ignite.cache(DEFAULT_CACHE_NAME).remove(emptyPartId, emptyPartId);
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--skip-zeros", DEFAULT_CACHE_NAME));
fileNameMatcher = dumpFileNameMatcher();
assertTrue(fileNameMatcher.find());
String nonZeroUpdateCntrs = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, nonZeroUpdateCntrs, "The check procedure has finished, found " + 31 + " partitions");
assertContains(log, nonZeroUpdateCntrs, "1 partitions was skipped");
assertContains(log, nonZeroUpdateCntrs, "The check procedure has finished, no conflicts have been found.");
assertSort(31, zeroUpdateCntrs);
assertEquals(zeroUpdateCntrs, nonZeroUpdateCntrs);
}
/**
* Tests that idle verify print partitions info.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDump() throws Exception {
IgniteEx ignite = crd;
int keysCount = 20; //less than parts number for ability to check skipZeros flag.
createCacheAndPreload(ignite, keysCount);
int parts = ignite.affinity(DEFAULT_CACHE_NAME).partitions();
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "other"));
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", DEFAULT_CACHE_NAME));
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
String dumpWithZeros = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, dumpWithZeros, "The check procedure has finished, found " + parts + " partitions");
assertContains(log, dumpWithZeros, "Partition: PartitionKeyV2 [grpId=1544803905, grpName=default, partId=0]");
assertContains(log, dumpWithZeros, "updateCntr=0, partitionState=OWNING, size=0, partHash=0");
assertContains(log, dumpWithZeros, "no conflicts have been found");
assertSort(parts, dumpWithZeros);
}
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--skip-zeros", DEFAULT_CACHE_NAME));
fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
String dumpWithoutZeros = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, dumpWithoutZeros, "The check procedure has finished, found " + keysCount + " partitions");
assertContains(log, dumpWithoutZeros, (parts - keysCount) + " partitions was skipped");
assertContains(log, dumpWithoutZeros, "Partition: PartitionKeyV2 [grpId=1544803905, grpName=default, partId=");
assertNotContains(log, dumpWithoutZeros, "updateCntr=0, partitionState=OWNING, size=0, partHash=0");
assertContains(log, dumpWithoutZeros, "no conflicts have been found");
assertSort(keysCount, dumpWithoutZeros);
}
else
fail("Should be found both files");
}
/**
* Common method for idle_verify tests with multiple options.
*/
@Test
public void testCacheIdleVerifyMultipleCacheFilterOptions()
throws Exception {
IgniteEx ignite = crd;
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME));
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "_second"));
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 64))
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "_third"));
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 128))
.setBackups(1)
.setName("wrong_cache"));
injectTestSystemOut();
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found",
"The check procedure task was executed with the following args: caches=[], excluded=[wrong.*], cacheFilter=[SYSTEM]",
"--cache", "idle_verify", "--dump", "--cache-filter", "SYSTEM", "--exclude-caches", "wrong.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found 96 partitions",
null,
"--cache", "idle_verify", "--dump", "--exclude-caches", "wrong.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found 32 partitions",
null,
"--cache", "idle_verify", "--dump", "shared.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found 160 partitions",
null,
"--cache", "idle_verify", "--dump", "shared.*,wrong.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found 160 partitions",
null,
"--cache", "idle_verify", "--dump", "shared.*,wrong.*", "--cache-filter", "USER"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, found 160 partitions",
null,
"--cache", "idle_verify", "--dump", "shared.*,wrong.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"There are no caches matching given filter options",
null,
"--cache", "idle_verify", "--exclude-caches", ".*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
false,
"Invalid cache name regexp",
null,
"--cache", "idle_verify", "--dump", "--exclude-caches", "["
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, no conflicts have been found.",
null,
"--cache", "idle_verify", "--exclude-caches", "wrong.*"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"The check procedure has finished, no conflicts have been found.",
null,
"--cache", "idle_verify", "--dump", "--cache-filter", "PERSISTENT"
);
testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
true,
"There are no caches matching given filter options.",
null,
"--cache", "idle_verify", "--cache-filter", "NOT_PERSISTENT"
);
}
/**
* Runs idle_verify with specified arguments and checks the dump if dump option was present.
*
* @param exitOk whether CommandHandler should exit without errors
* @param outputExp expected dump output
* @param cmdExp expected command built from command line arguments
* @param args command handler arguments
* @throws IOException if some of file operations failed
*/
private void testCacheIdleVerifyMultipleCacheFilterOptionsCommon(
boolean exitOk,
String outputExp,
String cmdExp,
String... args
) throws IOException {
Set<String> argsSet = new HashSet<>(asList(args));
int exitCode = execute(args);
assertEquals(testOut.toString(), exitOk, EXIT_CODE_OK == exitCode);
if (exitCode == EXIT_CODE_OK) {
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
assertContains(log, argsSet, "--dump");
Path filePath = Paths.get(fileNameMatcher.group(1));
String dump = new String(Files.readAllBytes(filePath));
Files.delete(filePath);
assertContains(log, dump, outputExp);
if (cmdExp != null)
assertContains(log, dump, cmdExp);
}
else {
assertNotContains(log, argsSet, "--dump");
assertContains(log, testOut.toString(), outputExp);
}
} else
assertContains(log, testOut.toString(), outputExp);
}
/**
* Checking sorting of partitions.
*
* @param expectedPartsCount Expected parts count.
* @param output Output.
*/
private void assertSort(int expectedPartsCount, String output) {
Pattern partIdPattern = Pattern.compile(".*partId=([0-9]*)");
Pattern primaryPattern = Pattern.compile("Partition instances: \\[PartitionHashRecordV2 \\[isPrimary=true");
Matcher partIdMatcher = partIdPattern.matcher(output);
Matcher primaryMatcher = primaryPattern.matcher(output);
int i = 0;
while (partIdMatcher.find()) {
assertEquals(i++, Integer.parseInt(partIdMatcher.group(1)));
assertTrue(primaryMatcher.find()); //primary node should be first in every line
}
assertEquals(expectedPartsCount, i);
}
/**
* Tests that idle verify print partitions info.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpForCorruptedData() throws Exception {
IgniteEx ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
corruptingAndCheckDefaultCache(ignite, "USER", true);
}
/**
* Tests that idle verify print partitions info while launched without dump option.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyForCorruptedData() throws Exception {
IgniteEx ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
corruptingAndCheckDefaultCache(ignite, "USER", false);
}
/**
* @param ignite Ignite.
* @param cacheFilter cacheFilter.
* @param dump Whether idle_verify should be launched with dump option or not.
*/
private void corruptingAndCheckDefaultCache(IgniteEx ignite, String cacheFilter, boolean dump) throws IOException {
injectTestSystemOut();
GridCacheContext<Object, Object> cacheCtx = ignite.cachex(DEFAULT_CACHE_NAME).context();
corruptDataEntry(cacheCtx, 0, true, false);
corruptDataEntry(cacheCtx, cacheCtx.config().getAffinity().partitions() / 2, false, true);
String resReport = null;
if (dump) {
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--cache-filter", cacheFilter));
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
resReport = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
log.info(resReport);
}
else
fail("Should be found dump with conflicts");
}
else {
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--cache-filter", cacheFilter));
resReport = testOut.toString();
}
assertContains(log, resReport, "found 2 conflict partitions: [counterConflicts=1, hashConflicts=1]");
}
/**
* Tests that idle verify print partitions info over system caches.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpForCorruptedDataOnSystemCache() throws Exception {
int parts = 32;
atomicConfiguration = new AtomicConfiguration()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setBackups(2);
IgniteEx ignite = crd;
injectTestSystemOut();
// Adding some assignments without deployments.
for (int i = 0; i < 100; i++) {
ignite.semaphore("s" + i, i, false, true);
ignite.atomicSequence("sq" + i, 0, true)
.incrementAndGet();
}
CacheGroupContext storedSysCacheCtx = ignite.context().cache().cacheGroup(CU.cacheId("default-ds-group"));
assertNotNull(storedSysCacheCtx);
corruptDataEntry(storedSysCacheCtx.caches().get(0), new GridCacheInternalKeyImpl("sq0",
"default-ds-group"), true, false);
corruptDataEntry(storedSysCacheCtx.caches().get(0), new GridCacheInternalKeyImpl("sq" + parts / 2,
"default-ds-group"), false, true);
CacheGroupContext memoryVolatileCacheCtx = ignite.context().cache().cacheGroup(CU.cacheId(
"default-volatile-ds-group@volatileDsMemPlc"));
assertNotNull(memoryVolatileCacheCtx);
assertEquals("volatileDsMemPlc", memoryVolatileCacheCtx.dataRegion().config().getName());
assertEquals(false, memoryVolatileCacheCtx.dataRegion().config().isPersistenceEnabled());
corruptDataEntry(memoryVolatileCacheCtx.caches().get(0), new GridCacheInternalKeyImpl("s0",
"default-volatile-ds-group@volatileDsMemPlc"), true, false);
corruptDataEntry(memoryVolatileCacheCtx.caches().get(0), new GridCacheInternalKeyImpl("s" + parts / 2,
"default-volatile-ds-group@volatileDsMemPlc"), false, true);
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--cache-filter", "SYSTEM"));
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
String dumpWithConflicts = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
U.log(log, dumpWithConflicts);
// Non-persistent caches do not have counter conflicts
assertContains(log, dumpWithConflicts, "found 4 conflict partitions: [counterConflicts=2, " +
"hashConflicts=2]");
}
else
fail("Should be found dump with conflicts");
}
/**
* Tests that idle verify print partitions info over persistence client caches.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpForCorruptedDataOnPersistenceClientCache() throws Exception {
IgniteEx ignite = crd;
createCacheAndPreload(ignite, 100);
corruptingAndCheckDefaultCache(ignite, "PERSISTENT", true);
}
/**
* Tests that idle verify print partitions info with exclude cache group.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpExcludedCacheGrp() throws Exception {
IgniteEx ignite = crd;
int parts = 32;
IgniteCache<Object, Object> cache = ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME));
IgniteCache<Object, Object> secondCache = ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "_second"));
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--exclude-caches", "shared_grp"));
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
String dumpWithConflicts = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, dumpWithConflicts, "There are no caches matching given filter options");
}
else
fail("Should be found dump with conflicts");
}
/**
* Tests that idle verify print partitions info with exclude caches.
*
* @throws Exception If failed.
*/
@Test
public void testCacheIdleVerifyDumpExcludedCaches() throws Exception {
IgniteEx ignite = crd;
int parts = 32;
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME));
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setGroupName("shared_grp")
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "_second"));
ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, parts))
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + "_third"));
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "idle_verify", "--dump", "--exclude-caches", DEFAULT_CACHE_NAME
+ "," + DEFAULT_CACHE_NAME + "_second"));
Matcher fileNameMatcher = dumpFileNameMatcher();
if (fileNameMatcher.find()) {
String dumpWithConflicts = new String(Files.readAllBytes(Paths.get(fileNameMatcher.group(1))));
assertContains(log, dumpWithConflicts, "The check procedure has finished, found 32 partitions");
assertContains(log, dumpWithConflicts, "default_third");
assertNotContains(log, dumpWithConflicts, "shared_grp");
}
else
fail("Should be found dump with conflicts");
}
/**
* @return Build matcher for dump file name.
*/
@NotNull private Matcher dumpFileNameMatcher() {
Pattern fileNamePattern = Pattern.compile(".*VisorIdleVerifyDumpTask successfully written output to '(.*)'");
return fileNamePattern.matcher(testOut.toString());
}
/** */
@Test
public void testCacheContention() throws Exception {
int cnt = 10;
final ExecutorService svc = Executors.newFixedThreadPool(cnt);
try {
Ignite ignite = crd;
final IgniteCache<Object, Object> cache = ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setAtomicityMode(TRANSACTIONAL)
.setBackups(1)
.setName(DEFAULT_CACHE_NAME));
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch l2 = new CountDownLatch(1);
svc.submit(new Runnable() {
@Override public void run() {
try (final Transaction tx = ignite.transactions().txStart()) {
cache.put(0, 0);
l.countDown();
U.awaitQuiet(l2);
tx.commit();
}
}
});
for (int i = 0; i < cnt - 1; i++) {
svc.submit(new Runnable() {
@Override public void run() {
U.awaitQuiet(l);
try (final Transaction tx = ignite.transactions().txStart()) {
cache.get(0);
tx.commit();
}
}
});
}
U.awaitQuiet(l);
Thread.sleep(300);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "contention", "5"));
l2.countDown();
String out = testOut.toString();
assertContains(log, out, "TxEntry");
assertContains(log, out, "op=READ");
assertContains(log, out, "op=CREATE");
assertContains(log, out, "id=" + ignite(0).cluster().localNode().id());
assertContains(log, out, "id=" + ignite(1).cluster().localNode().id());
}
finally {
svc.shutdown();
svc.awaitTermination(100, TimeUnit.DAYS);
}
}
/** */
@Test
public void testCacheGroups() {
Ignite ignite = crd;
IgniteCache<Object, Object> cache = ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setBackups(1)
.setGroupName("G100")
.setName(DEFAULT_CACHE_NAME));
for (int i = 0; i < 100; i++)
cache.put(i, i);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "list", ".*", "--groups"));
assertContains(log, testOut.toString(), "G100");
}
/** */
@Test
public void testCacheAffinity() {
Ignite ignite = crd;
IgniteCache<Object, Object> cache1 = ignite.createCache(new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setBackups(1)
.setName(DEFAULT_CACHE_NAME));
for (int i = 0; i < 100; i++)
cache1.put(i, i);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "list", ".*"));
String out = testOut.toString();
assertContains(log, out, "cacheName=" + DEFAULT_CACHE_NAME);
assertContains(log, out, "prim=32");
assertContains(log, out, "mapped=32");
assertContains(log, out, "affCls=RendezvousAffinityFunction");
}
/** */
@Test
public void testCacheConfigNoOutputFormat() {
testCacheConfig(null, 1, 1);
}
/** */
@Test
public void testCacheConfigSingleLineOutputFormatSingleNodeSignleCache() {
testCacheConfigSingleLineOutputFormat(1, 1);
}
/** */
@Test
public void testCacheConfigSingleLineOutputFormatTwoNodeSignleCache() {
testCacheConfigSingleLineOutputFormat(2, 1);
}
/** */
@Test
public void testCacheConfigSingleLineOutputFormatTwoNodeManyCaches() {
testCacheConfigSingleLineOutputFormat(2, 100);
}
/** */
@Test
public void testCacheConfigMultiLineOutputFormatSingleNodeSingleCache() {
testCacheConfigMultiLineOutputFormat(1, 1);
}
/** */
@Test
public void testCacheConfigMultiLineOutputFormatTwoNodeSingleCache() {
testCacheConfigMultiLineOutputFormat(2, 1);
}
/** */
@Test
public void testCacheConfigMultiLineOutputFormatTwoNodeManyCaches() {
testCacheConfigMultiLineOutputFormat(2, 100);
}
/** */
private void testCacheConfigSingleLineOutputFormat(int nodesCnt, int cachesCnt) {
testCacheConfig("single-line", nodesCnt, cachesCnt);
}
/** */
private void testCacheConfigMultiLineOutputFormat(int nodesCnt, int cachesCnt) {
testCacheConfig("multi-line", nodesCnt, cachesCnt);
}
/** */
private void testCacheConfig(String outputFormat, int nodesCnt, int cachesCnt) {
assertTrue("Invalid number of nodes or caches", nodesCnt > 0 && cachesCnt > 0);
Ignite ignite = crd;
List<CacheConfiguration> ccfgs = new ArrayList<>(cachesCnt);
for (int i = 0; i < cachesCnt; i++) {
ccfgs.add(
new CacheConfiguration<>()
.setAffinity(new RendezvousAffinityFunction(false, 32))
.setBackups(1)
.setName(DEFAULT_CACHE_NAME + i)
);
}
ignite.createCaches(ccfgs);
IgniteCache<Object, Object> cache1 = ignite.cache(DEFAULT_CACHE_NAME + 0);
for (int i = 0; i < 100; i++)
cache1.put(i, i);
injectTestSystemOut();
int exitCode;
if (outputFormat == null)
exitCode = execute("--cache", "list", ".*", "--config");
else
exitCode = execute("--cache", "list", ".*", "--config", "--output-format", outputFormat);
assertEquals(EXIT_CODE_OK, exitCode);
String outStr = testOut.toString();
if (outputFormat == null || SINGLE_LINE.text().equals(outputFormat)) {
for (int i = 0; i < cachesCnt; i++)
assertContains(log, outStr, "name=" + DEFAULT_CACHE_NAME + i);
assertContains(log, outStr, "partitions=32");
assertContains(log, outStr, "function=o.a.i.cache.affinity.rendezvous.RendezvousAffinityFunction");
}
else if (MULTI_LINE.text().equals(outputFormat)) {
for (int i = 0; i < cachesCnt; i++)
assertContains(log, outStr, "[cache = '" + DEFAULT_CACHE_NAME + i + "']");
assertContains(log, outStr, "Affinity Partitions: 32");
assertContains(log, outStr, "Affinity Function: o.a.i.cache.affinity.rendezvous.RendezvousAffinityFunction");
}
else
fail("Unknown output format: " + outputFormat);
}
/** */
@Test
public void testCacheDistribution() {
Ignite ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
// Run distribution for all node and all cache
assertEquals(EXIT_CODE_OK, execute("--cache", "distribution", "null"));
String out = testOut.toString();
// Result include info by cache "default"
assertContains(log, out, "[next group: id=1544803905, name=default]");
// Result include info by cache "ignite-sys-cache"
assertContains(log, out, "[next group: id=-2100569601, name=ignite-sys-cache]");
// Run distribution for all node and all cache and include additional user attribute
assertEquals(EXIT_CODE_OK, execute("--cache", "distribution", "null", "--user-attributes", "ZONE,CELL,DC"));
out += "\n" + testOut.toString();
List<String> outLines = Arrays.stream(out.split("\n"))
.map(String::trim)
.collect(toList());
int firstIndex = outLines.indexOf("[next group: id=1544803905, name=default]");
int lastIndex = outLines.lastIndexOf("[next group: id=1544803905, name=default]");
String dataLine = outLines.get(firstIndex + 1);
String userArrtDataLine = outLines.get(lastIndex + 1);
long commaNum = dataLine.chars().filter(i -> i == ',').count();
long userArrtCommaNum = userArrtDataLine.chars().filter(i -> i == ',').count();
// Check that number of columns increased by 3
assertEquals(3, userArrtCommaNum - commaNum);
}
/** */
@Test
public void testCacheResetLostPartitions() {
Ignite ignite = crd;
createCacheAndPreload(ignite, 100);
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--cache", "reset_lost_partitions", "ignite-sys-cache,default"));
final String out = testOut.toString();
assertContains(log, out, "Reset LOST-partitions performed successfully. Cache group (name = 'ignite-sys-cache'");
assertContains(log, out, "Reset LOST-partitions performed successfully. Cache group (name = 'default'");
}
/**
* @param h Handler.
* @param validateClo Validate clo.
* @param args Args.
*/
private void validate(CommandHandler h, IgniteInClosure<Map<ClusterNode, VisorTxTaskResult>> validateClo,
String... args) {
assertEquals(EXIT_CODE_OK, execute(h, args));
validateClo.apply(h.getLastOperationResult());
}
/**
* @param from From.
* @param cnt Count.
*/
private Map<Object, Object> generate(int from, int cnt) {
Map<Object, Object> map = new TreeMap<>();
for (int i = 0; i < cnt; i++)
map.put(i + from, i + from);
return map;
}
/**
* Test execution of --wal print command.
*/
@Test
public void testUnusedWalPrint() {
Ignite ignite = crd;
List<String> nodes = new ArrayList<>(2);
for (ClusterNode node : ignite.cluster().forServers().nodes())
nodes.add(node.consistentId().toString());
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--wal", "print"));
String out = testOut.toString();
for (String id : nodes)
assertContains(log, out, id);
assertNotContains(log, out, "error");
assertEquals(EXIT_CODE_OK, execute("--wal", "print", nodes.get(0)));
out = testOut.toString();
assertNotContains(log, out, nodes.get(1));
assertNotContains(log, out, "error");
}
/**
* Test execution of --wal delete command.
*/
@Test
public void testUnusedWalDelete() {
Ignite ignite = crd;
List<String> nodes = new ArrayList<>(2);
for (ClusterNode node : ignite.cluster().forServers().nodes())
nodes.add(node.consistentId().toString());
injectTestSystemOut();
assertEquals(EXIT_CODE_OK, execute("--wal", "delete"));
String out = testOut.toString();
for (String id : nodes)
assertContains(log, out, id);
assertNotContains(log, out, "error");
assertEquals(EXIT_CODE_OK, execute("--wal", "delete", nodes.get(0)));
out = testOut.toString();
assertNotContains(log, out, nodes.get(1));
assertNotContains(log, out, "error");
}
/**
* Starts several long transactions in order to test --tx command. Transactions will last until unlock latch is
* released: first transaction will wait for unlock latch directly, some others will wait for key lock acquisition.
*
* @param lockLatch Lock latch. Will be released inside body of the first transaction.
* @param unlockLatch Unlock latch. Should be released externally. First transaction won't be finished until unlock
* latch is released.
* @param topChangeBeforeUnlock <code>true</code> should be passed if cluster topology is expected to change between
* method call and unlock latch release. Commit of the first transaction will be asserted to fail in such case.
* @return Future to be completed after finish of all started transactions.
*/
private IgniteInternalFuture<?> startTransactions(
String testName,
CountDownLatch lockLatch,
CountDownLatch unlockLatch,
boolean topChangeBeforeUnlock
) throws Exception {
IgniteEx client = grid("client");
AtomicInteger idx = new AtomicInteger();
return multithreadedAsync(new Runnable() {
@Override public void run() {
int id = idx.getAndIncrement();
switch (id) {
case 0:
try (Transaction tx = grid(0).transactions().txStart()) {
grid(0).cache(DEFAULT_CACHE_NAME).putAll(generate(0, 100));
lockLatch.countDown();
U.awaitQuiet(unlockLatch);
tx.commit();
if (topChangeBeforeUnlock)
fail("Commit must fail");
}
catch (Exception e) {
if (topChangeBeforeUnlock)
assertTrue(X.hasCause(e, TransactionRollbackException.class));
else
throw e;
}
break;
case 1:
U.awaitQuiet(lockLatch);
doSleep(3000);
try (Transaction tx =
grid(0).transactions().withLabel("label1").txStart(PESSIMISTIC, READ_COMMITTED, Integer.MAX_VALUE, 0)) {
grid(0).cache(DEFAULT_CACHE_NAME).putAll(generate(200, 110));
grid(0).cache(DEFAULT_CACHE_NAME).put(0, 0);
}
break;
case 2:
try (Transaction tx = grid(1).transactions().txStart()) {
U.awaitQuiet(lockLatch);
grid(1).cache(DEFAULT_CACHE_NAME).put(0, 0);
}
break;
case 3:
try (Transaction tx = client.transactions().withLabel("label2").txStart(OPTIMISTIC, READ_COMMITTED, 0, 0)) {
U.awaitQuiet(lockLatch);
client.cache(DEFAULT_CACHE_NAME).putAll(generate(100, 10));
client.cache(DEFAULT_CACHE_NAME).put(0, 0);
tx.commit();
}
break;
}
}
}, 4, "tx-thread-" + testName);
}
/**
* Corrupts data entry.
*
* @param ctx Context.
* @param key Key.
* @param breakCntr Break counter.
* @param breakData Break data.
*/
private void corruptDataEntry(
GridCacheContext<Object, Object> ctx,
Object key,
boolean breakCntr,
boolean breakData
) {
int partId = ctx.affinity().partition(key);
try {
long updateCntr = ctx.topology().localPartition(partId).updateCounter();
Object valToPut = ctx.cache().keepBinary().get(key);
if (breakCntr)
updateCntr++;
if (breakData)
valToPut = valToPut.toString() + " broken";
// Create data entry
DataEntry dataEntry = new DataEntry(
ctx.cacheId(),
new KeyCacheObjectImpl(key, null, partId),
new CacheObjectImpl(valToPut, null),
GridCacheOperation.UPDATE,
new GridCacheVersion(),
new GridCacheVersion(),
0L,
partId,
updateCntr,
DataEntry.EMPTY_FLAGS
);
GridCacheDatabaseSharedManager db = (GridCacheDatabaseSharedManager)ctx.shared().database();
db.checkpointReadLock();
try {
U.invoke(GridCacheDatabaseSharedManager.class, db, "applyUpdate", ctx, dataEntry);
}
finally {
db.checkpointReadUnlock();
}
}
catch (IgniteCheckedException e) {
e.printStackTrace();
}
}
/**
* Test is that when the --help control.sh command is executed, output
* will contain non-experimental commands. In case system property
* {@link IgniteSystemProperties#IGNITE_ENABLE_EXPERIMENTAL_COMMAND} =
* {@code true}.
*/
@Test
@WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "true")
public void testContainsNotExperimentalCmdInHelpOutputWhenEnableExperimentalTrue() {
checkContainsNotExperimentalCmdInHelpOutput();
}
/**
* Test is that when the --help control.sh command is executed, output
* will contain non-experimental commands. In case system property
* {@link IgniteSystemProperties#IGNITE_ENABLE_EXPERIMENTAL_COMMAND} =
* {@code false}.
*/
@Test
@WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "false")
public void testContainsNotExperimentalCmdInHelpOutputWhenEnableExperimentalFalse() {
checkContainsNotExperimentalCmdInHelpOutput();
}
/**
* Test for contains of experimental commands in output of the --help
* control.sh command.
*/
@Test
@WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "true")
public void testContainsExperimentalCmdInHelpOutput() {
checkExperimentalCmdInHelpOutput(true);
}
/**
* Test for not contains of experimental commands in output of the --help
* control.sh command.
*/
@Test
@WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "false")
public void testNotContainsExperimentalCmdInHelpOutput() {
checkExperimentalCmdInHelpOutput(false);
}
/**
* Test to verify that the experimental command will not be executed if
* {@link IgniteSystemProperties#IGNITE_ENABLE_EXPERIMENTAL_COMMAND} =
* {@code false}, a warning will be displayed instead.
* */
@Test
@WithSystemProperty(key = IGNITE_ENABLE_EXPERIMENTAL_COMMAND, value = "false")
public void testContainsWarnInsteadExecExperimentalCmdWhenEnableExperimentalFalse() {
injectTestSystemOut();
Map<CommandList, Collection<String>> cmdArgs = new EnumMap<>(CommandList.class);
cmdArgs.put(WAL, asList("print", "delete"));
cmdArgs.put(METADATA, asList("help", "list"));
cmdArgs.put(TRACING_CONFIGURATION, Collections.singletonList("get_all"));
String warning = String.format(
"To use experimental command add --enable-experimental parameter for %s",
UTILITY_NAME
);
stream(CommandList.values()).filter(cmd -> cmd.command().experimental())
.peek(cmd -> assertTrue("Not contains " + cmd, cmdArgs.containsKey(cmd)))
.forEach(cmd -> cmdArgs.get(cmd).forEach(cmdArg -> {
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute(cmd.text(), cmdArg));
assertContains(log, testOut.toString(), warning);
}));
}
/**
* Test checks that there will be no error when executing the command with
* option {@link CommonArgParser#CMD_VERBOSE}.
*/
@Test
public void testCorrectExecCmdWithVerboseInDiffParamsOrder() {
injectTestSystemOut();
int resCode = EXIT_CODE_OK;
assertEquals(resCode, execute(BASELINE.text(), CMD_VERBOSE));
assertNotContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
assertEquals(resCode, execute(CMD_VERBOSE, BASELINE.text()));
assertNotContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
}
/**
* Test checks that stack trace for incorrect arguments will be output
* only if {@link CommonArgParser#CMD_VERBOSE} flag is present.
*/
@Test
public void testErrInvalidArgumentsWithVerbose() {
injectTestSystemOut();
int resCode = EXIT_CODE_INVALID_ARGUMENTS;
String uuid = UUID.randomUUID().toString();
assertEquals(resCode, execute(BASELINE.text(), uuid));
assertNotContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
assertEquals(resCode, execute(BASELINE.text(), CMD_VERBOSE, uuid));
assertContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
}
/**
* Test checks that stack trace for connection error will be output only
* if {@link CommonArgParser#CMD_VERBOSE} flag is present.
*/
@Test
public void testErrConnectionWithVerbose() {
injectTestSystemOut();
int resCode = EXIT_CODE_CONNECTION_FAILED;
String uuid = UUID.randomUUID().toString();
assertEquals(resCode, execute(BASELINE.text(), "--host", uuid));
assertNotContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
assertEquals(resCode, execute(BASELINE.text(), CMD_VERBOSE, "--host", uuid));
assertContains(log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
}
/**
* Test checks that stack trace for unexpected error will be output with or
* without {@link CommonArgParser#CMD_VERBOSE} flag.
*/
@Test
public void testErrUnexpectedWithWithoutVerbose() {
injectTestSystemOut();
Logger log = CommandHandler.initLogger(null);
log.addHandler(new StreamHandler(System.out, new Formatter() {
/** {@inheritDoc} */
@Override public String format(LogRecord record) {
String msg = record.getMessage();
if (msg.contains("Cluster state:"))
throw new Error();
return msg + "\n";
}
}));
int resCode = EXIT_CODE_UNEXPECTED_ERROR;
CommandHandler cmd = new CommandHandler(log);
assertEquals(resCode, execute(cmd, BASELINE.text()));
assertContains(GridAbstractTest.log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
assertEquals(resCode, execute(cmd, BASELINE.text(), CMD_VERBOSE));
assertContains(GridAbstractTest.log, testOut.toString(), ERROR_STACK_TRACE_PREFIX);
}
/**
* Checking for contains or not of experimental commands in output of the
* --help control.sh command.
*
* @param contains Check contains or not.
*/
private void checkExperimentalCmdInHelpOutput(boolean contains) {
execHelpCmd(helpOut -> {
stream(CommandList.values()).filter(cmd -> cmd.command().experimental())
.forEach(cmd -> {
if (contains)
assertContains(log, helpOut, cmd.text());
else
assertNotContains(log, helpOut, cmd.text());
});
});
}
/**
* Check that when executing the "--help" control.sh command, the output
* will contain non-experimental commands.
*/
private void checkContainsNotExperimentalCmdInHelpOutput() {
execHelpCmd(helpOut -> {
stream(CommandList.values()).filter(cmd -> !cmd.command().experimental())
.forEach(cmd -> assertContains(log, helpOut, cmd.text()));
});
}
/**
* Executing the command "--help" control.sh with transfer of output to
* consumer.
*
* @param consumer Consumer.
*/
private void execHelpCmd(Consumer<String> consumer) {
assert nonNull(consumer);
injectTestSystemOut();
execute("--help");
consumer.accept(testOut.toString());
}
}