Skip to content

Commit

Permalink
Enable filtering of snapshots on keyspace, table and snapshot name in…
Browse files Browse the repository at this point in the history
… nodetool listsnapshots

patch by Stefan Miklosovic; reviewed by Jordan West, Bernardo Botella, Cheng Wang, Maxim Muzafarov for CASSANDRA-20151
  • Loading branch information
smiklosovic committed Jan 16, 2025
1 parent b4e9399 commit 407dbac
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 7 deletions.
3 changes: 2 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
5.1
* Enable filtering of snapshots on keyspace, table and snapshot name in nodetool listsnapshots (CASSANDRA-20151)
* Create manifest upon loading where it does not exist or enrich it (CASSANDRA-20150)
* Propagate true size of snapshot in SnapshotDetailsTabularData to not call JMX twice in nodetool listsnapshots (CASSANDRA-20149)
* Implementation of CEP-43 (CASSANDRA-19964)
* Implementation of CEP-43 - copying a table via CQL by CREATE TABLE LIKE (CASSANDRA-19964)
* Periodically disconnect roles that are revoked or have LOGIN=FALSE set (CASSANDRA-19385)
* AST library for CQL-based fuzz tests (CASSANDRA-20198)
* Support audit logging for JMX operations (CASSANDRA-20128)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,19 @@ public interface SnapshotManagerMBean
void clearSnapshot(String tag, Map<String, Object> options, String... keyspaceNames) throws IOException;

/**
* Get the details of all the snapshots
* Get the details of all the snapshots. Options might be:
*
* <pre>
* no_ttl: "true" or "false"
* include_ephemeral: "true" or "false"
* keyspace: name of keyspace to get snapshots of
* table: name of table to get tables of
* snapshot: name of snapshot to list
* </pre>
*
* There is no requirement as what option has to be specified.
* Values of 'null' for keyspace, table or snapshot do not have any effect / will
* not be part of the filtering.
*
* @param options map of options used for filtering of snapshots
* @return A map of snapshotName to all its details in Tabular form.
Expand Down
21 changes: 21 additions & 0 deletions src/java/org/apache/cassandra/tools/nodetool/ListSnapshots.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ public class ListSnapshots extends NodeToolCmd
description = "Include ephememeral snapshots")
private boolean includeEphemeral = false;

@Option(title = "keyspace",
name = { "-k", "--keyspace" },
description = "Include snapshots of specified keyspace name")
private String keyspace = null;

@Option(title = "table",
name = { "-t", "--table" },
description = "Include snapshots of specified table name")
private String table = null;

@Option(title = "snapshot",
name = { "-n", "--snapshot"},
description = "Include snapshots of specified name")
private String snapshotName = null;

@Override
public void execute(NodeProbe probe)
{
Expand All @@ -55,6 +70,12 @@ public void execute(NodeProbe probe)
Map<String, String> options = new HashMap<>();
options.put("no_ttl", Boolean.toString(noTTL));
options.put("include_ephemeral", Boolean.toString(includeEphemeral));
if (keyspace != null)
options.put("keyspace", keyspace);
if (table != null)
options.put("table", table);
if (snapshotName != null)
options.put("snapshot", snapshotName);

final Map<String, TabularData> snapshotDetails = probe.getSnapshotDetails(options);
if (snapshotDetails.isEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,49 @@ public void testTakingSnapshoWithSameNameOnDifferentTablesDoesNotFail()
cluster.get(1).nodetoolResult("snapshot", "-t", "somename", "-kt", String.format("%s.tbl2", KEYSPACE)).asserts().success();
}

@Test
public void testListingOfSnapshotsByKeyspaceAndTable()
{
IInvokableInstance instance = cluster.get(1);
cluster.schemaChange("CREATE KEYSPACE IF NOT EXISTS ks1 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};");
cluster.schemaChange("CREATE KEYSPACE IF NOT EXISTS ks2 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};");
cluster.schemaChange("CREATE TABLE IF NOT EXISTS ks1.tbl (key int, value text, PRIMARY KEY (key))");
cluster.schemaChange("CREATE TABLE IF NOT EXISTS ks1.tbl2 (key int, value text, PRIMARY KEY (key))");
cluster.schemaChange("CREATE TABLE IF NOT EXISTS ks2.tbl (key int, value text, PRIMARY KEY (key))");
cluster.schemaChange("CREATE TABLE IF NOT EXISTS ks2.tbl2 (key int, value text, PRIMARY KEY (key))");

populate(cluster, "ks1", "tbl");
populate(cluster, "ks1", "tbl2");
populate(cluster, "ks2", "tbl");
populate(cluster, "ks2", "tbl2");

instance.nodetoolResult("snapshot", "-t", "tagks1tbl", "-kt", "ks1.tbl").asserts().success();
instance.nodetoolResult("snapshot", "-t", "tagks1tbl2", "-kt", "ks1.tbl2").asserts().success();
instance.nodetoolResult("snapshot", "-t", "tagks2tbl", "-kt", "ks2.tbl").asserts().success();
instance.nodetoolResult("snapshot", "-t", "tagks2tbl2", "-kt", "ks2.tbl2").asserts().success();

waitForSnapshot("ks1", null, "tagks1tbl", true, false);
waitForSnapshot("ks1", null, "tagks1tbl2", true, false);
waitForSnapshot("ks1", null, "tagks2tbl", false, false);
waitForSnapshot("ks1", null, "tagks2tbl2", false, false);

waitForSnapshot("ks1", "tbl", "tagks1tbl", true, false);
waitForSnapshot("ks1", "tbl", "tagks1tbl2", false, false);
waitForSnapshot("ks1", "tbl", "tagks2tbl", false, false);
waitForSnapshot("ks1", "tbl", "tagks2tbl2", false, false);

waitForSnapshot(null, "tbl", "tagks1tbl", true, false);
waitForSnapshot(null, "tbl", "tagks1tbl2", false, false);
waitForSnapshot(null, "tbl", "tagks2tbl", true, false);
waitForSnapshot(null, "tbl", "tagks2tbl2", false, false);

NodeToolResult nodeToolResult = instance.nodetoolResult("listsnapshots", "-n", "tagks1tbl");
nodeToolResult.asserts().success();
List<String> snapshots = extractSnapshots(nodeToolResult.getStdout());
assertEquals(1, snapshots.size());
assertTrue(snapshots.get(0).contains("tagks1tbl"));
}

private void populate(Cluster cluster)
{
for (int i = 0; i < 100; i++)
Expand Down Expand Up @@ -555,13 +598,27 @@ private boolean waitForSnapshotInternal(String keyspaceName, String tableName, S
if (noTTL)
args.add("-nt");

if (keyspaceName != null)
{
args.add("-k");
args.add(keyspaceName);
}

if (tableName != null)
{
args.add("-t");
args.add(tableName);
}

if (snapshotName != null)
{
args.add("-n");
args.add(snapshotName);
}

listsnapshots = cluster.get(1).nodetoolResult(args.toArray(new String[0]));

List<String> lines = Arrays.stream(listsnapshots.getStdout().split("\n"))
.filter(line -> !line.isEmpty())
.filter(line -> !line.startsWith("Snapshot Details:") && !line.startsWith("There are no snapshots"))
.filter(line -> !line.startsWith("Snapshot name") && !line.startsWith("Total TrueDiskSpaceUsed"))
.collect(toList());
List<String> lines = extractSnapshots(listsnapshots.getStdout());

return expectPresent == lines.stream().anyMatch(line -> line.startsWith(snapshotName));
}
Expand Down Expand Up @@ -632,4 +689,15 @@ else if (LOCAL_SYSTEM_KEYSPACE_NAMES.contains(keyspace.getName()) || REPLICATED_
return result.toArray(new String[0]);
}, forSystemKeyspaces);
}

private List<String> extractSnapshots(String listSnapshotsStdOut)
{
return Arrays.stream(listSnapshotsStdOut.split("\n"))
.filter(line -> !line.isEmpty())
.filter(line -> !line.startsWith("Snapshot Details:"))
.filter(line -> !line.startsWith("There are no snapshots"))
.filter(line -> !line.startsWith("Snapshot name"))
.filter(line -> !line.startsWith("Total TrueDiskSpaceUsed"))
.collect(toList());
}
}

0 comments on commit 407dbac

Please sign in to comment.