Skip to content

Commit

Permalink
IGNITE-18546 Add cache clear command to control.sh (apache#10577)
Browse files Browse the repository at this point in the history
  • Loading branch information
timoninmaxim authored Mar 3, 2023
1 parent 1f23884 commit e220e80
Show file tree
Hide file tree
Showing 10 changed files with 466 additions and 1 deletion.
24 changes: 24 additions & 0 deletions docs/_docs/tools/control-script.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,30 @@ control.sh|bat --cache destroy --caches cache1,cache2
control.sh|bat --cache destroy --destroy-all-caches
----

== Clearing Caches

You can use the control script to clear specific caches.

[source, shell]
----
control.sh|bat --cache clear --caches cache1,...,cacheN
----

Parameters:

[cols="1,3",opts="header"]
|===
| Parameter | Description
| `--caches cache1,...,cacheN`| Specifies a comma-separated list of cache names to be cleared.
|===

Examples:
[source, shell]
----
# Clear cache1 and cache2.
control.sh|bat --cache clear --caches cache1,cache2
----

== Resetting Lost Partitions

You can use the control script to reset lost partitions for specific caches.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.commandline.cache;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.client.GridClient;
import org.apache.ignite.internal.client.GridClientConfiguration;
import org.apache.ignite.internal.commandline.AbstractCommand;
import org.apache.ignite.internal.commandline.Command;
import org.apache.ignite.internal.commandline.CommandArgIterator;
import org.apache.ignite.internal.commandline.TaskExecutor;
import org.apache.ignite.internal.processors.cache.ClearCachesTask;
import org.apache.ignite.internal.processors.cache.ClearCachesTaskArg;
import org.apache.ignite.internal.processors.cache.ClearCachesTaskResult;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;

import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.CLEAR;

/** Command that clears specified caches. */
public class CacheClear extends AbstractCommand<ClearCachesTaskArg> {
/** Message that contains cleared caches. */
public static final String CLEAR_MSG = "The following caches have been cleared: %s";

/** Message that contains not-cleared caches (they don't exist). */
public static final String SKIP_CLEAR_MSG = "The following caches don't exist: %s";

/** Comma-separated list of cache names. */
public static final String CACHES = "--caches";

/** Command parsed arguments. */
private ClearCachesTaskArg arg;

/** {@inheritDoc} */
@Override public Object execute(GridClientConfiguration clientCfg, IgniteLogger log) throws Exception {
try (GridClient client = Command.startClient(clientCfg)) {
ClearCachesTaskResult res = TaskExecutor.executeTask(client, ClearCachesTask.class, arg(), clientCfg);

if (!F.isEmpty(res.clearedCaches()))
U.log(log, String.format(CLEAR_MSG, String.join(", ", res.clearedCaches())));

if (!F.isEmpty(res.nonExistentCaches()))
U.warn(log, String.format(SKIP_CLEAR_MSG, String.join(", ", res.nonExistentCaches())));
}

return null;
}

/** {@inheritDoc} */
@Override public ClearCachesTaskArg arg() {
return arg;
}

/** {@inheritDoc} */
@Override public void printUsage(IgniteLogger logger) {
usageCache(
logger,
CacheSubcommands.CLEAR,
"Clear specified caches.",
F.asMap(CACHES, "specifies a comma-separated list of cache names to be cleared."));
}

/** {@inheritDoc} */
@Override public void parseArguments(CommandArgIterator argIter) {
String cmdArg = argIter.nextArg("Command arguments are expected.");

if (cmdArg == null)
throw new IllegalArgumentException("Unknown argument: " + cmdArg);

if (CACHES.equals(cmdArg)) {
String cacheNamesArg = argIter.nextArg("Expected a comma-separated cache names.");

List<String> caches = Arrays.stream(cacheNamesArg.split(",")).collect(Collectors.toList());

arg = new ClearCachesTaskArg(caches);
}
else
throw new IllegalArgumentException("Unknown argument: " + cmdArg);

if (argIter.hasNextSubArg()) {
throw new IllegalArgumentException(
"Invalid argument \"" + argIter.peekNextArg() + "\", no more arguments are expected.");
}
}

/** {@inheritDoc} */
@Override public String name() {
return CLEAR.text().toUpperCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public enum CacheCommandList {
*/
DESTROY("destroy", new CacheDestroy()),

/**
* Clear caches.
*/
CLEAR("clear", new CacheClear()),

/**
* Validates indexes attempting to read each indexed entry.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ public enum CacheSubcommands {
/**
* Enable / disable cache metrics collection or show metrics collection status.
*/
METRICS("metrics", null, new CacheMetrics());
METRICS("metrics", null, new CacheMetrics()),

/**
* Clear caches.
*/
CLEAR("clear", null, new CacheClear());

/** Enumerated values. */
private static final CacheSubcommands[] VALS = values();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.logging.StreamHandler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
Expand All @@ -63,6 +64,7 @@
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.CacheClear;
import org.apache.ignite.internal.commandline.cache.CacheDestroy;
import org.apache.ignite.internal.commandline.cache.CacheSubcommands;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
Expand Down Expand Up @@ -114,6 +116,7 @@
import static org.apache.ignite.internal.commandline.OutputFormat.SINGLE_LINE;
import static org.apache.ignite.internal.commandline.cache.CacheDestroy.CACHE_NAMES_ARG;
import static org.apache.ignite.internal.commandline.cache.CacheDestroy.DESTROY_ALL_ARG;
import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.CLEAR;
import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.DESTROY;
import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.HELP;
import static org.apache.ignite.internal.commandline.cdc.CdcCommand.DELETE_LOST_SEGMENT_LINKS;
Expand Down Expand Up @@ -1290,6 +1293,96 @@ public void testCacheDestroy() throws IgniteCheckedException {
assertTrue("Caches must be destroyed: " + crd.cacheNames().toString(), crd.cacheNames().isEmpty());
}

/** */
@Test
public void testCacheClear() {
injectTestSystemOut();

assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", CLEAR.text()));

assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", CLEAR.text(), "cacheX"));
assertContains(log, testOut.toString(), "Unknown argument: cacheX");

assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", CLEAR.text(), CacheClear.CACHES, "X,Y", "Z"));
assertContains(log, testOut.toString(), "Invalid argument \"Z\", no more arguments are expected.");

List<String> caches = F.asList("cache1", "cache2", "cache3");

for (boolean sql: new boolean[] {false, true}) {
for (String cache: caches)
checkCacheClearCommand(caches, F.asList(cache), sql);

checkCacheClearCommand(caches, F.asList("cache1", "cache2"), sql);
checkCacheClearCommand(caches, F.asList("cache1", "cache2", "cache3"), sql);
checkCacheClearCommand(caches, F.asList("cacheX"), sql);
checkCacheClearCommand(caches, F.asList("cacheX", "cache1"), sql);
}
}

/** */
private void checkCacheClearCommand(List<String> caches, List<String> clearCaches, boolean sql) {
int cnt = 100;

for (String cache: caches) {
if (sql) {
sql("CREATE TABLE tbl_" + cache + "(id INT PRIMARY KEY, val INT) WITH \"CACHE_NAME=" + cache + "\";");

for (int i = 0; i < cnt; i++)
sql("insert into tbl_" + cache + "(id, val) values (?, ?)", i, i);
}
else {
IgniteCache<Integer, Integer> c = crd.createCache(new CacheConfiguration<>(cache));

for (int i = 0; i < cnt; i++)
c.put(i, i);
}
}

assertEquals(EXIT_CODE_OK, execute("--cache", CLEAR.text(), CacheClear.CACHES, String.join(",", clearCaches)));

List<String> nonExistentCaches = clearCaches.stream()
.filter(c -> !caches.contains(c))
.collect(Collectors.toList());

List<String> clearedCaches = clearCaches.stream()
.filter(caches::contains)
.collect(Collectors.toList());

if (!nonExistentCaches.isEmpty())
assertContains(log, testOut.toString(), String.format(CacheClear.SKIP_CLEAR_MSG, String.join(", ", nonExistentCaches)));
else
assertNotContains(log, testOut.toString(), String.format(CacheClear.SKIP_CLEAR_MSG, ""));

if (!clearedCaches.isEmpty())
assertContains(log, testOut.toString(), String.format(CacheClear.CLEAR_MSG, String.join(", ", clearedCaches)));
else
assertNotContains(log, testOut.toString(), String.format(CacheClear.CLEAR_MSG, ""));

for (String cache: caches) {
int count;

if (sql)
count = sql("select * from tbl_" + cache).size();
else
count = crd.cache(cache).size();

assertEquals(cache, clearCaches.contains(cache) ? 0 : cnt, count);
}

if (sql) {
for (String cache: caches)
sql("drop table tbl_" + cache);
}
else
crd.destroyCaches(caches);
}

/** */
private List<?> sql(String sql, Object... args) {
return crd.context().query().querySqlFields(new SqlFieldsQuery(sql).setArgs(args), false, false)
.get(0).getAll();
}

/**
* @param off Name index offset.
* @param cnt Count.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.cache;

import java.util.ArrayList;
import java.util.List;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.processors.task.GridInternal;
import org.apache.ignite.internal.visor.VisorJob;
import org.apache.ignite.internal.visor.VisorOneNodeTask;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.jetbrains.annotations.Nullable;

/** Clears specified caches. */
@GridInternal
public class ClearCachesTask extends VisorOneNodeTask<ClearCachesTaskArg, ClearCachesTaskResult> {
/** */
private static final long serialVersionUID = 0L;

/** */
@Override protected VisorJob<ClearCachesTaskArg, ClearCachesTaskResult> job(ClearCachesTaskArg arg) {
return new ClearCacheJob(arg, debug);
}

/** Job clears specified caches. */
private static class ClearCacheJob extends VisorJob<ClearCachesTaskArg, ClearCachesTaskResult> {
/** */
private static final long serialVersionUID = 0L;

/** Local Ignite instance. */
private Ignite ignite;

/** */
private ClearCacheJob(ClearCachesTaskArg arg, boolean debug) {
super(arg, debug);
}

/** {@inheritDoc} */
@Override protected ClearCachesTaskResult run(@Nullable ClearCachesTaskArg arg) throws IgniteException {
List<String> clearedCaches = new ArrayList<>();
List<String> nonExistentCaches = new ArrayList<>();

for (String cache: arg.caches()) {
IgniteCache<?, ?> ignCache = ignite.cache(cache);

if (ignCache == null)
nonExistentCaches.add(cache);
else {
ignCache.clear();

clearedCaches.add(cache);
}
}

return new ClearCachesTaskResult(clearedCaches, nonExistentCaches);
}

/** */
@IgniteInstanceResource
public void setIgnite(Ignite ignite) {
this.ignite = ignite;
}
}
}
Loading

0 comments on commit e220e80

Please sign in to comment.