Skip to content

Commit c9d4d76

Browse files
committed
Make a command group
1 parent dd9de06 commit c9d4d76

File tree

7 files changed

+194
-139
lines changed

7 files changed

+194
-139
lines changed

doc/modules/cassandra/pages/managing/operating/compression.adoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ supports both manual and automatic training approaches.
114114

115115
==== Manual Dictionary Training
116116

117-
Use the `nodetool traincompressiondictionary` command to manually train
117+
Use the `nodetool compressiondictionary train` command to manually train
118118
a compression dictionary:
119119

120120
[source,bash]
121121
----
122-
nodetool traincompressiondictionary <keyspace> <table>
122+
nodetool compressiondictionary train <keyspace> <table>
123123
----
124124

125125
The command trains a dictionary by sampling from existing SSTables. If no
@@ -135,7 +135,7 @@ To force training even with insufficient samples, use the `--force` or `-f` opti
135135

136136
[source,bash]
137137
----
138-
nodetool traincompressiondictionary --force <keyspace> <table>
138+
nodetool compressiondictionary train --force <keyspace> <table>
139139
----
140140

141141
This can be useful for testing or when you want to train a dictionary from
@@ -387,7 +387,7 @@ versions.
387387
according to `compression_dictionary_cache_size`. Memory overhead is
388388
typically minimal (default 64KB per dictionary × cache size).
389389
* *Dictionary Training Overhead*: Manual training via
390-
`nodetool traincompressiondictionary` samples SSTable chunk data and
390+
`nodetool compressiondictionary train` samples SSTable chunk data and
391391
performs CPU-intensive dictionary training. Consider running training
392392
during off-peak hours.
393393
* *Automatic Training Impact*: When
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.cassandra.tools.nodetool;
19+
20+
import java.io.PrintStream;
21+
import java.util.concurrent.TimeUnit;
22+
23+
import com.google.common.util.concurrent.Uninterruptibles;
24+
25+
import org.apache.cassandra.db.compression.ICompressionDictionaryTrainer.TrainingStatus;
26+
import org.apache.cassandra.db.compression.TrainingState;
27+
import org.apache.cassandra.tools.NodeProbe;
28+
import org.apache.cassandra.utils.Clock;
29+
import picocli.CommandLine.Command;
30+
import picocli.CommandLine.Option;
31+
import picocli.CommandLine.Parameters;
32+
33+
@Command(name = "compressiondictionary",
34+
description = "Manage compression dictionaries",
35+
subcommands = { CompressionDictionary.Train.class })
36+
public class CompressionDictionary
37+
{
38+
@Command(name = "train",
39+
description = "Manually trigger compression dictionary training for a table. If no SSTables are available, the memtable will be flushed first.")
40+
public static class Train extends AbstractCommand
41+
{
42+
@Parameters(index = "0", description = "The keyspace name", arity = "1")
43+
private String keyspace;
44+
45+
@Parameters(index = "1", description = "The table name", arity = "1")
46+
private String table;
47+
48+
@Option(names = { "-f", "--force" }, description = "Force the dictionary training even if there are not enough samples")
49+
private boolean force = false;
50+
51+
@Override
52+
public void execute(NodeProbe probe)
53+
{
54+
PrintStream out = probe.output().out;
55+
PrintStream err = probe.output().err;
56+
57+
try
58+
{
59+
out.printf("Starting compression dictionary training for %s.%s...%n", keyspace, table);
60+
out.printf("Training from existing SSTables (flushing first if needed)%n");
61+
62+
probe.trainCompressionDictionary(keyspace, table, force);
63+
64+
// Wait for training completion (10 minutes timeout for SSTable-based training)
65+
out.println("Sampling from existing SSTables and training.");
66+
long maxWaitMillis = TimeUnit.MINUTES.toMillis(10);
67+
long startTime = Clock.Global.currentTimeMillis();
68+
69+
while (Clock.Global.currentTimeMillis() - startTime < maxWaitMillis)
70+
{
71+
TrainingState trainingState = probe.getCompressionDictionaryTrainingState(keyspace, table);
72+
TrainingStatus status = trainingState.getStatus();
73+
displayProgress(trainingState, startTime, out, status);
74+
if (TrainingStatus.COMPLETED == status)
75+
{
76+
out.printf("%nTraining completed successfully for %s.%s%n", keyspace, table);
77+
return;
78+
}
79+
else if (TrainingStatus.FAILED == status)
80+
{
81+
err.printf("%nTraining failed for %s.%s%n", keyspace, table);
82+
try
83+
{
84+
String failureMessage = trainingState.getFailureMessage();
85+
if (failureMessage != null && !failureMessage.isEmpty())
86+
{
87+
err.printf("Reason: %s%n", failureMessage);
88+
}
89+
}
90+
catch (Exception e)
91+
{
92+
// If we can't get the failure message, just continue without it
93+
}
94+
System.exit(1);
95+
}
96+
97+
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
98+
}
99+
100+
err.printf("%nTraining did not complete within expected timeframe (10 minutes).%n");
101+
System.exit(1);
102+
}
103+
catch (Exception e)
104+
{
105+
err.printf("Failed to trigger training: %s%n", e.getMessage());
106+
System.exit(1);
107+
}
108+
}
109+
110+
private static void displayProgress(TrainingState trainingState, long startTime, PrintStream out, TrainingStatus status)
111+
{
112+
// Display meaningful statistics
113+
long sampleCount = trainingState.getSampleCount();
114+
long totalSampleSize = trainingState.getTotalSampleSize();
115+
long elapsedSeconds = (Clock.Global.currentTimeMillis() - startTime) / 1000;
116+
double sampleSizeMB = totalSampleSize / (1024.0 * 1024.0);
117+
118+
out.printf("\rStatus: %s | Samples: %d | Size: %.2f MiB | Elapsed: %ds",
119+
status, sampleCount, sampleSizeMB, elapsedSeconds);
120+
}
121+
}
122+
}

src/java/org/apache/cassandra/tools/nodetool/NodetoolCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
TableStats.class,
208208
TopPartitions.class,
209209
TpStats.class,
210-
TrainCompressionDictionary.class,
210+
CompressionDictionary.class,
211211
TruncateHints.class,
212212
UpdateCIDRGroup.class,
213213
UpgradeSSTable.class,

src/java/org/apache/cassandra/tools/nodetool/TrainCompressionDictionary.java

Lines changed: 0 additions & 116 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
NAME
2+
nodetool compressiondictionary - Manage compression dictionaries
3+
4+
SYNOPSIS
5+
nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]
6+
[(-pp | --print-port)] [(-pw <password> | --password <password>)]
7+
[(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]
8+
[(-u <username> | --username <username>)] compressiondictionary <command> [<args>]
9+
10+
nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]
11+
[(-pp | --print-port)] [(-pw <password> | --password <password>)]
12+
[(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]
13+
[(-u <username> | --username <username>)] compressiondictionary train
14+
[(-f | --force)] [--] <keyspace> <table>
15+
16+
OPTIONS
17+
-h <host>, --host <host>
18+
Node hostname or ip address
19+
20+
-p <port>, --port <port>
21+
Remote jmx agent port number
22+
23+
-pp, --print-port
24+
Operate in 4.0 mode with hosts disambiguated by port number
25+
26+
-pw <password>, --password <password>
27+
Remote jmx agent password
28+
29+
-pwf <passwordFilePath>, --password-file <passwordFilePath>
30+
Path to the JMX password file
31+
32+
-u <username>, --username <username>
33+
Remote jmx agent username
34+
35+
COMMANDS
36+
With no arguments, Display help information
37+
38+
train
39+
Manually trigger compression dictionary training for a table. If no
40+
SSTables are available, the memtable will be flushed first.
41+
42+
With -f or --force option, Force the dictionary training even if there
43+
are not enough samples

test/resources/nodetool/help/traincompressiondictionary renamed to test/resources/nodetool/help/compressiondictionary$train

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
NAME
2-
nodetool traincompressiondictionary - Manually trigger compression
2+
nodetool compressiondictionary train - Manually trigger compression
33
dictionary training for a table. If no SSTables are available, the
44
memtable will be flushed first.
55

66
SYNOPSIS
77
nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]
88
[(-pp | --print-port)] [(-pw <password> | --password <password>)]
99
[(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]
10-
[(-u <username> | --username <username>)] traincompressiondictionary
10+
[(-u <username> | --username <username>)] compressiondictionary train
1111
[(-f | --force)] [--] <keyspace> <table>
1212

1313
OPTIONS

0 commit comments

Comments
 (0)