Skip to content

Commit c95e753

Browse files
authored
Enhance error for out of bounds byte size settings (#29338)
Today when you input a byte size setting that is out of bounds for the setting, you get an error message that indicates the maximum value of the setting. The problem is that because we use ByteSize#toString, we end up with a representation of the value that does not really tell you what the bound is. For example, if the bound is 2^31 - 1 bytes, the output would be 1.9gb which does not really tell you want the limit as there are many byte size values that we format to the same 1.9gb with ByteSize#toString. We have a method ByteSize#getStringRep that uses the input units to the value as the output units for the string representation, so we end up with no loss if we use this to report the bound. This commit does this.
1 parent c21057b commit c95e753

File tree

5 files changed

+74
-34
lines changed

5 files changed

+74
-34
lines changed

plugins/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureRepositorySettingsTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,17 @@ public void testChunkSize() throws StorageException, IOException, URISyntaxExcep
112112
// zero bytes is not allowed
113113
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () ->
114114
azureRepository(Settings.builder().put("chunk_size", "0").build()));
115-
assertEquals("Failed to parse value [0] for setting [chunk_size] must be >= 1b", e.getMessage());
115+
assertEquals("failed to parse value [0] for setting [chunk_size], must be >= [1b]", e.getMessage());
116116

117117
// negative bytes not allowed
118118
e = expectThrows(IllegalArgumentException.class, () ->
119119
azureRepository(Settings.builder().put("chunk_size", "-1").build()));
120-
assertEquals("Failed to parse value [-1] for setting [chunk_size] must be >= 1b", e.getMessage());
120+
assertEquals("failed to parse value [-1] for setting [chunk_size], must be >= [1b]", e.getMessage());
121121

122122
// greater than max chunk size not allowed
123123
e = expectThrows(IllegalArgumentException.class, () ->
124124
azureRepository(Settings.builder().put("chunk_size", "65mb").build()));
125-
assertEquals("Failed to parse value [65mb] for setting [chunk_size] must be <= 64mb", e.getMessage());
125+
assertEquals("failed to parse value [65mb] for setting [chunk_size], must be <= [64mb]", e.getMessage());
126126
}
127127

128128
}

plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,22 +110,22 @@ public void testChunkSize() {
110110
Settings.builder().put("chunk_size", "0").build());
111111
GoogleCloudStorageRepository.getSetting(GoogleCloudStorageRepository.CHUNK_SIZE, repoMetaData);
112112
});
113-
assertEquals("Failed to parse value [0] for setting [chunk_size] must be >= 1b", e.getMessage());
113+
assertEquals("failed to parse value [0] for setting [chunk_size], must be >= [1b]", e.getMessage());
114114

115115
// negative bytes not allowed
116116
e = expectThrows(IllegalArgumentException.class, () -> {
117117
RepositoryMetaData repoMetaData = new RepositoryMetaData("repo", GoogleCloudStorageRepository.TYPE,
118118
Settings.builder().put("chunk_size", "-1").build());
119119
GoogleCloudStorageRepository.getSetting(GoogleCloudStorageRepository.CHUNK_SIZE, repoMetaData);
120120
});
121-
assertEquals("Failed to parse value [-1] for setting [chunk_size] must be >= 1b", e.getMessage());
121+
assertEquals("failed to parse value [-1] for setting [chunk_size], must be >= [1b]", e.getMessage());
122122

123123
// greater than max chunk size not allowed
124124
e = expectThrows(IllegalArgumentException.class, () -> {
125125
RepositoryMetaData repoMetaData = new RepositoryMetaData("repo", GoogleCloudStorageRepository.TYPE,
126126
Settings.builder().put("chunk_size", "101mb").build());
127127
GoogleCloudStorageRepository.getSetting(GoogleCloudStorageRepository.CHUNK_SIZE, repoMetaData);
128128
});
129-
assertEquals("Failed to parse value [101mb] for setting [chunk_size] must be <= 100mb", e.getMessage());
129+
assertEquals("failed to parse value [101mb] for setting [chunk_size], must be <= [100mb]", e.getMessage());
130130
}
131131
}

plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public void testInvalidChunkBufferSizeSettings() throws IOException {
7070
assertValidBuffer(5, 5);
7171
// buffer < 5mb should fail
7272
assertInvalidBuffer(4, 10, IllegalArgumentException.class,
73-
"Failed to parse value [4mb] for setting [buffer_size] must be >= 5mb");
73+
"failed to parse value [4mb] for setting [buffer_size], must be >= [5mb]");
7474
// chunk > 5tb should fail
7575
assertInvalidBuffer(5, 6000000, IllegalArgumentException.class,
76-
"Failed to parse value [6000000mb] for setting [chunk_size] must be <= 5tb");
76+
"failed to parse value [6000000mb] for setting [chunk_size], must be <= [5tb]");
7777
}
7878

7979
private void assertValidBuffer(long bufferMB, long chunkMB) throws IOException {

server/src/main/java/org/elasticsearch/common/settings/Setting.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import java.util.IdentityHashMap;
4848
import java.util.Iterator;
4949
import java.util.List;
50+
import java.util.Locale;
5051
import java.util.Map;
5152
import java.util.Objects;
5253
import java.util.Set;
@@ -1070,10 +1071,22 @@ public static Setting<ByteSizeValue> byteSizeSetting(String key, Function<Settin
10701071
public static ByteSizeValue parseByteSize(String s, ByteSizeValue minValue, ByteSizeValue maxValue, String key) {
10711072
ByteSizeValue value = ByteSizeValue.parseBytesSizeValue(s, key);
10721073
if (value.getBytes() < minValue.getBytes()) {
1073-
throw new IllegalArgumentException("Failed to parse value [" + s + "] for setting [" + key + "] must be >= " + minValue);
1074+
final String message = String.format(
1075+
Locale.ROOT,
1076+
"failed to parse value [%s] for setting [%s], must be >= [%s]",
1077+
s,
1078+
key,
1079+
minValue.getStringRep());
1080+
throw new IllegalArgumentException(message);
10741081
}
10751082
if (value.getBytes() > maxValue.getBytes()) {
1076-
throw new IllegalArgumentException("Failed to parse value [" + s + "] for setting [" + key + "] must be <= " + maxValue);
1083+
final String message = String.format(
1084+
Locale.ROOT,
1085+
"failed to parse value [%s] for setting [%s], must be <= [%s]",
1086+
s,
1087+
key,
1088+
maxValue.getStringRep());
1089+
throw new IllegalArgumentException(message);
10771090
}
10781091
return value;
10791092
}

server/src/test/java/org/elasticsearch/common/settings/SettingTests.java

+51-24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.elasticsearch.common.collect.Tuple;
2222
import org.elasticsearch.common.settings.Setting.Property;
23+
import org.elasticsearch.common.unit.ByteSizeUnit;
2324
import org.elasticsearch.common.unit.ByteSizeValue;
2425
import org.elasticsearch.common.unit.TimeValue;
2526
import org.elasticsearch.monitor.jvm.JvmInfo;
@@ -52,35 +53,61 @@ public void testGet() {
5253
assertTrue(booleanSetting.get(Settings.builder().put("foo.bar", true).build()));
5354
}
5455

55-
public void testByteSize() {
56-
Setting<ByteSizeValue> byteSizeValueSetting =
57-
Setting.byteSizeSetting("a.byte.size", new ByteSizeValue(1024), Property.Dynamic, Property.NodeScope);
56+
public void testByteSizeSetting() {
57+
final Setting<ByteSizeValue> byteSizeValueSetting =
58+
Setting.byteSizeSetting("a.byte.size", new ByteSizeValue(1024), Property.Dynamic, Property.NodeScope);
5859
assertFalse(byteSizeValueSetting.isGroupSetting());
59-
ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY);
60-
assertEquals(byteSizeValue.getBytes(), 1024);
61-
62-
byteSizeValueSetting = Setting.byteSizeSetting("a.byte.size", s -> "2048b", Property.Dynamic, Property.NodeScope);
63-
byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY);
64-
assertEquals(byteSizeValue.getBytes(), 2048);
65-
66-
60+
final ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY);
61+
assertThat(byteSizeValue.getBytes(), equalTo(1024L));
62+
}
63+
64+
public void testByteSizeSettingMinValue() {
65+
final Setting<ByteSizeValue> byteSizeValueSetting =
66+
Setting.byteSizeSetting(
67+
"a.byte.size",
68+
new ByteSizeValue(100, ByteSizeUnit.MB),
69+
new ByteSizeValue(20_000_000, ByteSizeUnit.BYTES),
70+
new ByteSizeValue(Integer.MAX_VALUE, ByteSizeUnit.BYTES));
71+
final long value = 20_000_000 - randomIntBetween(1, 1024);
72+
final Settings settings = Settings.builder().put("a.byte.size", value + "b").build();
73+
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> byteSizeValueSetting.get(settings));
74+
final String expectedMessage = "failed to parse value [" + value + "b] for setting [a.byte.size], must be >= [20000000b]";
75+
assertThat(e, hasToString(containsString(expectedMessage)));
76+
}
77+
78+
public void testByteSizeSettingMaxValue() {
79+
final Setting<ByteSizeValue> byteSizeValueSetting =
80+
Setting.byteSizeSetting(
81+
"a.byte.size",
82+
new ByteSizeValue(100, ByteSizeUnit.MB),
83+
new ByteSizeValue(16, ByteSizeUnit.MB),
84+
new ByteSizeValue(Integer.MAX_VALUE, ByteSizeUnit.BYTES));
85+
final long value = (1L << 31) - 1 + randomIntBetween(1, 1024);
86+
final Settings settings = Settings.builder().put("a.byte.size", value + "b").build();
87+
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> byteSizeValueSetting.get(settings));
88+
final String expectedMessage = "failed to parse value [" + value + "b] for setting [a.byte.size], must be <= [2147483647b]";
89+
assertThat(e, hasToString(containsString(expectedMessage)));
90+
}
91+
92+
public void testByteSizeSettingValidation() {
93+
final Setting<ByteSizeValue> byteSizeValueSetting =
94+
Setting.byteSizeSetting("a.byte.size", s -> "2048b", Property.Dynamic, Property.NodeScope);
95+
final ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY);
96+
assertThat(byteSizeValue.getBytes(), equalTo(2048L));
6797
AtomicReference<ByteSizeValue> value = new AtomicReference<>(null);
6898
ClusterSettings.SettingUpdater<ByteSizeValue> settingUpdater = byteSizeValueSetting.newUpdater(value::set, logger);
69-
try {
70-
settingUpdater.apply(Settings.builder().put("a.byte.size", 12).build(), Settings.EMPTY);
71-
fail("no unit");
72-
} catch (IllegalArgumentException ex) {
73-
assertThat(ex, hasToString(containsString("illegal value can't update [a.byte.size] from [2048b] to [12]")));
74-
assertNotNull(ex.getCause());
75-
assertThat(ex.getCause(), instanceOf(IllegalArgumentException.class));
76-
final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
77-
final String expected =
78-
"failed to parse setting [a.byte.size] with value [12] as a size in bytes: unit is missing or unrecognized";
79-
assertThat(cause, hasToString(containsString(expected)));
80-
}
8199

100+
final IllegalArgumentException e = expectThrows(
101+
IllegalArgumentException.class,
102+
() -> settingUpdater.apply(Settings.builder().put("a.byte.size", 12).build(), Settings.EMPTY));
103+
assertThat(e, hasToString(containsString("illegal value can't update [a.byte.size] from [2048b] to [12]")));
104+
assertNotNull(e.getCause());
105+
assertThat(e.getCause(), instanceOf(IllegalArgumentException.class));
106+
final IllegalArgumentException cause = (IllegalArgumentException) e.getCause();
107+
final String expected = "failed to parse setting [a.byte.size] with value [12] as a size in bytes: unit is missing or unrecognized";
108+
assertThat(cause, hasToString(containsString(expected)));
82109
assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "12b").build(), Settings.EMPTY));
83-
assertEquals(new ByteSizeValue(12), value.get());
110+
assertThat(value.get(), equalTo(new ByteSizeValue(12)));
84111
}
85112

86113
public void testMemorySize() {

0 commit comments

Comments
 (0)