Skip to content

Commit 17374f2

Browse files
committed
Fix incorrect validation causing maxThreads to be treated as required (#3128)
1 parent 74ae181 commit 17374f2

File tree

11 files changed

+250
-15
lines changed

11 files changed

+250
-15
lines changed

data-loader/cli/src/main/java/com/scalar/db/dataloader/cli/command/dataexport/ExportCommand.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ public Integer call() throws Exception {
6161
FileUtils.validateFilePath(scalarDbPropertiesFilePath);
6262
validatePositiveValue(
6363
spec.commandLine(), dataChunkSize, DataLoaderError.INVALID_DATA_CHUNK_SIZE);
64-
validatePositiveValue(spec.commandLine(), maxThreads, DataLoaderError.INVALID_MAX_THREADS);
64+
// Only validate the argument when provided by the user, if not set a default
65+
if (maxThreads != null) {
66+
validatePositiveValue(spec.commandLine(), maxThreads, DataLoaderError.INVALID_MAX_THREADS);
67+
} else {
68+
maxThreads = Runtime.getRuntime().availableProcessors();
69+
}
6570

6671
StorageFactory storageFactory = StorageFactory.create(scalarDbPropertiesFilePath);
6772
TableMetadataService metaDataService =
@@ -126,6 +131,11 @@ private void validateDeprecatedOptions() {
126131
DEPRECATED_END_EXCLUSIVE_OPTION,
127132
END_INCLUSIVE_OPTION,
128133
END_INCLUSIVE_OPTION_SHORT);
134+
validateDeprecatedOptionPair(
135+
spec.commandLine(),
136+
DEPRECATED_THREADS_OPTION,
137+
MAX_THREADS_OPTION,
138+
MAX_THREADS_OPTION_SHORT);
129139
}
130140

131141
private String getScalarDbPropertiesFilePath() {

data-loader/cli/src/main/java/com/scalar/db/dataloader/cli/command/dataexport/ExportCommandOptions.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public class ExportCommandOptions {
1616
public static final String END_INCLUSIVE_OPTION = "--end-inclusive";
1717
public static final String END_INCLUSIVE_OPTION_SHORT = "-ei";
1818
public static final String DEPRECATED_END_EXCLUSIVE_OPTION = "--end-exclusive";
19+
public static final String MAX_THREADS_OPTION = "--max-threads";
20+
public static final String MAX_THREADS_OPTION_SHORT = "-mt";
21+
public static final String DEPRECATED_THREADS_OPTION = "--threads";
1922

2023
@CommandLine.Option(
2124
names = {"--config", "-c"},
@@ -77,7 +80,18 @@ public class ExportCommandOptions {
7780
paramLabel = "<MAX_THREADS>",
7881
description =
7982
"Maximum number of threads to use for parallel processing (default: number of available processors)")
80-
protected int maxThreads;
83+
protected Integer maxThreads;
84+
85+
/**
86+
* @deprecated As of release 3.6.2. Will be removed in release 4.0.0. Use --max-threads instead
87+
*/
88+
@Deprecated
89+
@CommandLine.Option(
90+
names = {DEPRECATED_THREADS_OPTION},
91+
paramLabel = "<THREADS>",
92+
description = "Deprecated: Use --max-threads instead",
93+
hidden = true)
94+
protected Integer threadsDeprecated;
8195

8296
@CommandLine.Option(
8397
names = {"--start-key", "-sk"},
@@ -184,5 +198,10 @@ public void applyDeprecatedOptions() {
184198
if (endExclusiveDeprecated != null) {
185199
scanEndInclusive = !endExclusiveDeprecated;
186200
}
201+
202+
// If the deprecated option is set, use its value
203+
if (threadsDeprecated != null) {
204+
maxThreads = threadsDeprecated;
205+
}
187206
}
188207
}

data-loader/cli/src/main/java/com/scalar/db/dataloader/cli/command/dataimport/ImportCommand.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ public Integer call() throws Exception {
6161
spec.commandLine(), dataChunkSize, DataLoaderError.INVALID_DATA_CHUNK_SIZE);
6262
validatePositiveValue(
6363
spec.commandLine(), transactionSize, DataLoaderError.INVALID_TRANSACTION_SIZE);
64-
validatePositiveValue(spec.commandLine(), maxThreads, DataLoaderError.INVALID_MAX_THREADS);
64+
// Only validate the argument when provided by the user, if not set a default
65+
if (maxThreads != null) {
66+
validatePositiveValue(spec.commandLine(), maxThreads, DataLoaderError.INVALID_MAX_THREADS);
67+
} else {
68+
maxThreads = Runtime.getRuntime().availableProcessors();
69+
}
6570
validatePositiveValue(
6671
spec.commandLine(), dataChunkQueueSize, DataLoaderError.INVALID_DATA_CHUNK_QUEUE_SIZE);
6772
ControlFile controlFile = parseControlFileFromPath(controlFilePath).orElse(null);

data-loader/cli/src/main/java/com/scalar/db/dataloader/cli/command/dataimport/ImportCommandOptions.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,18 @@ public class ImportCommandOptions {
4242
names = {"--max-threads", "-mt"},
4343
paramLabel = "<MAX_THREADS>",
4444
description =
45-
"Maximum number of threads to use for parallel processing (default: number of available processors)",
46-
defaultValue = "16")
47-
protected int maxThreads;
45+
"Maximum number of threads to use for parallel processing (default: number of available processors)")
46+
protected Integer maxThreads;
4847

49-
// Deprecated option - kept for backward compatibility
48+
/**
49+
* @deprecated As of release 3.6.2. Will be removed in release 4.0.0. Use --max-threads instead
50+
*/
51+
@Deprecated
5052
@CommandLine.Option(
5153
names = {DEPRECATED_THREADS_OPTION},
5254
paramLabel = "<THREADS>",
5355
description = "Deprecated: Use --max-threads instead",
5456
hidden = true)
55-
@Deprecated
5657
protected Integer threadsDeprecated;
5758

5859
@CommandLine.Option(

data-loader/cli/src/test/java/com/scalar/db/dataloader/cli/command/dataexport/ExportCommandTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,87 @@ void call_withOnlyDeprecatedEndExclusive_shouldApplyInvertedValue() {
190190
// end-exclusive=false should become end-inclusive=true
191191
assertEquals(true, command.scanEndInclusive);
192192
}
193+
194+
@Test
195+
void call_withOnlyDeprecatedThreads_shouldApplyValue() {
196+
// Simulate command line parsing with only deprecated option
197+
String[] args = {
198+
"--config",
199+
"scalardb.properties",
200+
"--namespace",
201+
"scalar",
202+
"--table",
203+
"asset",
204+
"--format",
205+
"JSON",
206+
"--threads",
207+
"12"
208+
};
209+
ExportCommand command = new ExportCommand();
210+
CommandLine cmd = new CommandLine(command);
211+
cmd.parseArgs(args);
212+
213+
// Verify the deprecated value was parsed
214+
assertEquals(12, command.threadsDeprecated);
215+
216+
// Apply deprecated options (this is what the command does after validation)
217+
command.applyDeprecatedOptions();
218+
219+
// Verify the value was applied to maxThreads
220+
assertEquals(12, command.maxThreads);
221+
}
222+
223+
@Test
224+
void call_withMaxThreadsSpecified_shouldUseSpecifiedValue() {
225+
// Simulate command line parsing with --max-threads
226+
String[] args = {
227+
"--config",
228+
"scalardb.properties",
229+
"--namespace",
230+
"scalar",
231+
"--table",
232+
"asset",
233+
"--format",
234+
"JSON",
235+
"--max-threads",
236+
"8"
237+
};
238+
ExportCommand command = new ExportCommand();
239+
CommandLine cmd = new CommandLine(command);
240+
cmd.parseArgs(args);
241+
242+
// Verify the value was parsed
243+
assertEquals(8, command.maxThreads);
244+
}
245+
246+
@Test
247+
void call_withoutMaxThreads_shouldDefaultToAvailableProcessors() {
248+
// Simulate command line parsing without --max-threads
249+
String[] args = {
250+
"--config",
251+
"scalardb.properties",
252+
"--namespace",
253+
"scalar",
254+
"--table",
255+
"asset",
256+
"--format",
257+
"JSON"
258+
};
259+
ExportCommand command = new ExportCommand();
260+
CommandLine cmd = new CommandLine(command);
261+
cmd.parseArgs(args);
262+
263+
// Verify maxThreads is null before validation
264+
assertEquals(null, command.maxThreads);
265+
266+
// Simulate what happens in call() after validation
267+
command.spec = cmd.getCommandSpec();
268+
command.applyDeprecatedOptions();
269+
if (command.maxThreads == null) {
270+
command.maxThreads = Runtime.getRuntime().availableProcessors();
271+
}
272+
273+
// Verify it was set to available processors
274+
assertEquals(Runtime.getRuntime().availableProcessors(), command.maxThreads);
275+
}
193276
}

data-loader/cli/src/test/java/com/scalar/db/dataloader/cli/command/dataimport/ImportCommandTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,54 @@ void call_withEnableLogSuccessShortForm_shouldSetToTrueWithoutValue() throws Exc
229229
// Verify the short form flag was parsed correctly without requiring a value
230230
assertTrue(command.enableLogSuccessRecords);
231231
}
232+
233+
@Test
234+
void call_withMaxThreadsSpecified_shouldUseSpecifiedValue() {
235+
// Simulate command line parsing with --max-threads
236+
String[] args = {
237+
"--config",
238+
"scalardb.properties",
239+
"--file",
240+
"import.json",
241+
"--namespace",
242+
"scalar",
243+
"--table",
244+
"asset",
245+
"--max-threads",
246+
"8"
247+
};
248+
ImportCommand command = new ImportCommand();
249+
CommandLine cmd = new CommandLine(command);
250+
cmd.parseArgs(args);
251+
252+
// Verify the value was parsed
253+
assertEquals(8, command.maxThreads);
254+
}
255+
256+
@Test
257+
void call_withoutMaxThreads_shouldDefaultToAvailableProcessors() {
258+
// Simulate command line parsing without --max-threads
259+
String[] args = {
260+
"--config", "scalardb.properties",
261+
"--file", "import.json",
262+
"--namespace", "scalar",
263+
"--table", "asset"
264+
};
265+
ImportCommand command = new ImportCommand();
266+
CommandLine cmd = new CommandLine(command);
267+
cmd.parseArgs(args);
268+
269+
// Verify maxThreads is null before validation
270+
assertEquals(null, command.maxThreads);
271+
272+
// Simulate what happens in call() after validation
273+
command.spec = cmd.getCommandSpec();
274+
command.applyDeprecatedOptions();
275+
if (command.maxThreads == null) {
276+
command.maxThreads = Runtime.getRuntime().availableProcessors();
277+
}
278+
279+
// Verify it was set to available processors
280+
assertEquals(Runtime.getRuntime().availableProcessors(), command.maxThreads);
281+
}
232282
}

data-loader/core/src/main/java/com/scalar/db/dataloader/core/dataexport/ExportManager.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,8 @@ public ExportReport startExport(
7777
handleTransactionMetadata(exportOptions, tableMetadata);
7878
processHeader(exportOptions, tableMetadata, writer);
7979

80-
int maxThreadCount =
81-
exportOptions.getMaxThreadCount() == 0
82-
? Runtime.getRuntime().availableProcessors()
83-
: exportOptions.getMaxThreadCount();
84-
ExecutorService executorService = Executors.newFixedThreadPool(maxThreadCount);
80+
ExecutorService executorService =
81+
Executors.newFixedThreadPool(exportOptions.getMaxThreadCount());
8582

8683
BufferedWriter bufferedWriter = new BufferedWriter(writer);
8784
boolean isJson = exportOptions.getOutputFileFormat() == FileFormat.JSON;

data-loader/core/src/main/java/com/scalar/db/dataloader/core/dataexport/ExportOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ public class ExportOptions {
2121
private final FileFormat outputFileFormat;
2222
private final ScanRange scanRange;
2323
private final int limit;
24-
private final int maxThreadCount;
2524
private final boolean prettyPrintJson;
2625

2726
@Builder.Default private final int dataChunkSize = 200;
27+
@Builder.Default private final int maxThreadCount = Runtime.getRuntime().availableProcessors();
2828
@Builder.Default private final String delimiter = ";";
2929
@Builder.Default private final boolean excludeHeaderRow = false;
3030
@Builder.Default private final boolean includeTransactionMetadata = false;

data-loader/core/src/main/java/com/scalar/db/dataloader/core/dataimport/ImportOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class ImportOptions {
3434
private final ControlFile controlFile;
3535
private final String namespace;
3636
private final String tableName;
37-
private final int maxThreads;
37+
@Builder.Default private final int maxThreads = Runtime.getRuntime().availableProcessors();
3838
private final String customHeaderRow;
3939
private final int dataChunkQueueSize;
4040
}

data-loader/core/src/test/java/com/scalar/db/dataloader/core/dataexport/JsonExportManagerTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,32 @@ void startExport_givenPartitionKey_shouldGenerateOutputFile()
130130
Assertions.assertTrue(file.exists());
131131
Assertions.assertTrue(file.delete());
132132
}
133+
134+
@Test
135+
void exportOptions_withoutMaxThreadCount_shouldUseDefaultAvailableProcessors() {
136+
// Create ExportOptions without explicitly setting maxThreadCount
137+
ExportOptions exportOptions =
138+
ExportOptions.builder("namespace", "table", null, FileFormat.JSON)
139+
.sortOrders(Collections.emptyList())
140+
.scanRange(new ScanRange(null, null, false, false))
141+
.build();
142+
143+
// Verify the default was applied
144+
Assertions.assertEquals(
145+
Runtime.getRuntime().availableProcessors(), exportOptions.getMaxThreadCount());
146+
}
147+
148+
@Test
149+
void exportOptions_withExplicitMaxThreadCount_shouldUseProvidedValue() {
150+
// Create ExportOptions with explicit maxThreadCount
151+
ExportOptions exportOptions =
152+
ExportOptions.builder("namespace", "table", null, FileFormat.JSON)
153+
.sortOrders(Collections.emptyList())
154+
.scanRange(new ScanRange(null, null, false, false))
155+
.maxThreadCount(8)
156+
.build();
157+
158+
// Verify the explicit value was used
159+
Assertions.assertEquals(8, exportOptions.getMaxThreadCount());
160+
}
133161
}

0 commit comments

Comments
 (0)