Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e7dd2f3

Browse files
committedApr 6, 2025·
[Common][Server] Adapt and add test cases
1 parent 70bda8f commit e7dd2f3

File tree

3 files changed

+258
-24
lines changed

3 files changed

+258
-24
lines changed
 

Diff for: ‎fluss-common/src/main/java/com/alibaba/fluss/config/GlobalConfiguration.java

+8-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.alibaba.fluss.config;
1818

1919
import com.alibaba.fluss.annotation.Internal;
20-
import com.alibaba.fluss.annotation.VisibleForTesting;
2120
import com.alibaba.fluss.exception.IllegalConfigurationException;
2221

2322
import org.slf4j.Logger;
@@ -50,8 +49,8 @@ public class GlobalConfiguration {
5049

5150
private static final Logger LOG = LoggerFactory.getLogger(GlobalConfiguration.class);
5251

53-
@VisibleForTesting
54-
public static final String[] FLUSS_CONF_FILENAME = new String[] {"server.yaml", "common.yaml"};
52+
private static final String[] FLUSS_CONF_FILENAMES =
53+
new String[] {"server.yaml", "common.yaml"};
5554

5655
// --------------------------------------------------------------------------------------------
5756

@@ -136,28 +135,28 @@ public static Configuration loadConfiguration(
136135
}
137136

138137
// get Fluss yaml configuration files from dir
139-
final File serverYamlFile = new File(confDirFile, FLUSS_CONF_FILENAME[0]);
140-
final File commonYamlFile = new File(confDirFile, FLUSS_CONF_FILENAME[1]);
138+
final File serverYamlFile = new File(confDirFile, FLUSS_CONF_FILENAMES[0]);
139+
final File commonYamlFile = new File(confDirFile, FLUSS_CONF_FILENAMES[1]);
141140

142141
// 1. check if old and new configuration files are mixed which is not supported
143142
if (serverYamlFile.exists() && commonYamlFile.exists()) {
144143
throw new IllegalConfigurationException(
145144
"Only one of "
146-
+ FLUSS_CONF_FILENAME[0]
145+
+ FLUSS_CONF_FILENAMES[0]
147146
+ " and "
148-
+ FLUSS_CONF_FILENAME[1]
147+
+ FLUSS_CONF_FILENAMES[1]
149148
+ " may be specified.");
150149
}
151150

152151
// 2. backward compatability, use server.yaml
153152
if (serverYamlFile.exists()) {
154-
yamlConfigFiles.add(new File(confDirFile, FLUSS_CONF_FILENAME[0]));
153+
yamlConfigFiles.add(new File(confDirFile, FLUSS_CONF_FILENAMES[0]));
155154
}
156155

157156
// 3. latest configuration setup: load common.yaml and additionally specified, dedicated
158157
// configuration files
159158
if (commonYamlFile.exists()) {
160-
yamlConfigFiles.add(new File(confDirFile, FLUSS_CONF_FILENAME[1]));
159+
yamlConfigFiles.add(new File(confDirFile, FLUSS_CONF_FILENAMES[1]));
161160

162161
for (String additionalDefaultFile : additionalDefaultFiles) {
163162
yamlConfigFiles.add(new File(confDirFile, additionalDefaultFile));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025 Alibaba Group Holding Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.fluss.config;
18+
19+
import com.alibaba.fluss.exception.IllegalConfigurationException;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.io.TempDir;
23+
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.util.Collections;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
30+
31+
/** Tests for {@link com.alibaba.fluss.config.GlobalConfiguration}. */
32+
public class GlobalConfigurationTest {
33+
34+
@Test
35+
void testLoadConfigurationWithoutAdditionalFiles(@TempDir Path tempFolder) throws Exception {
36+
String confDir = tempFolder.toAbsolutePath().toString();
37+
38+
// backward compatability for old common configuration file
39+
Path serverYaml = tempFolder.resolve("server.yaml");
40+
Files.write(serverYaml, Collections.singleton("coordinator.host: localhost"));
41+
42+
Configuration configuration = GlobalConfiguration.loadConfiguration(confDir, null);
43+
assertThat(configuration.get(ConfigOptions.COORDINATOR_HOST)).isEqualTo("localhost");
44+
45+
// backward compatability for old common configuration file + precedence for dynamic
46+
// properties
47+
Configuration dynamicConfig = new Configuration();
48+
dynamicConfig.set(ConfigOptions.COORDINATOR_HOST, "example.com");
49+
configuration = GlobalConfiguration.loadConfiguration(confDir, dynamicConfig);
50+
assertThat(configuration.get(ConfigOptions.COORDINATOR_HOST)).isEqualTo("example.com");
51+
52+
// old and new common configuration file should not be present at the same time
53+
Path commonYaml = tempFolder.resolve("common.yaml");
54+
Files.write(commonYaml, Collections.singleton("bind.listeners: FLUSS://localhost:9124"));
55+
assertThatThrownBy(() -> GlobalConfiguration.loadConfiguration(confDir, null))
56+
.isInstanceOf(IllegalConfigurationException.class)
57+
.hasMessageContaining("Only one of");
58+
59+
// new common configuration file
60+
Files.delete(serverYaml);
61+
configuration = GlobalConfiguration.loadConfiguration(confDir, null);
62+
assertThat(configuration.get(ConfigOptions.BIND_LISTENERS))
63+
.isEqualTo("FLUSS://localhost:9124");
64+
assertThat(configuration.get(ConfigOptions.COORDINATOR_HOST)).isEqualTo(null);
65+
66+
// new common configuration file + precedence for dynamic properties
67+
dynamicConfig = new Configuration();
68+
dynamicConfig.set(ConfigOptions.BIND_LISTENERS, "FLUSS://example.com:9124");
69+
configuration = GlobalConfiguration.loadConfiguration(confDir, dynamicConfig);
70+
assertThat(configuration.get(ConfigOptions.BIND_LISTENERS))
71+
.isEqualTo("FLUSS://example.com:9124");
72+
assertThat(configuration.get(ConfigOptions.COORDINATOR_HOST)).isEqualTo(null);
73+
}
74+
75+
@Test
76+
void testLoadConfigurationWithAdditionalFiles(@TempDir Path tempFolder) throws Exception {
77+
String confDir = tempFolder.toAbsolutePath().toString();
78+
Path serverYaml = tempFolder.resolve("server.yaml");
79+
Files.write(serverYaml, Collections.singleton("coordinator.host: localhost"));
80+
}
81+
}

Diff for: ‎fluss-server/src/test/java/com/alibaba/fluss/server/utils/ConfigurationParserUtilsTest.java

+169-15
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,48 @@
2020
import com.alibaba.fluss.config.ConfigBuilder;
2121
import com.alibaba.fluss.config.ConfigOptions;
2222
import com.alibaba.fluss.config.Configuration;
23-
import com.alibaba.fluss.config.GlobalConfiguration;
23+
import com.alibaba.fluss.exception.IllegalConfigurationException;
2424
import com.alibaba.fluss.server.exception.FlussParseException;
2525

2626
import org.apache.commons.cli.MissingOptionException;
2727
import org.junit.jupiter.api.io.TempDir;
2828
import org.junit.jupiter.params.ParameterizedTest;
29+
import org.junit.jupiter.params.provider.Arguments;
2930
import org.junit.jupiter.params.provider.EnumSource;
31+
import org.junit.jupiter.params.provider.MethodSource;
3032

3133
import java.nio.file.Files;
3234
import java.nio.file.Path;
3335
import java.util.Collections;
36+
import java.util.stream.Stream;
3437

3538
import static org.assertj.core.api.Assertions.assertThat;
3639
import static org.assertj.core.api.Assertions.assertThatThrownBy;
40+
import static org.assertj.core.api.Assertions.fail;
3741

3842
/** Test for {@link ConfigurationParserUtils}. */
3943
public class ConfigurationParserUtilsTest {
4044

45+
private static final String OLD_COMMON_CONFIG_FILE_NAME = "server.yaml";
46+
private static final String NEW_COMMON_CONFIG_FILE_NAME = "common.yaml";
47+
private static final String COORDINATOR_SERVER_CONF_FILE_NAME = "coordinator-server.yaml";
48+
private static final String TABLET_SERVER_CONF_FILE_NAME = "tablet-server.yaml";
49+
4150
@ParameterizedTest
42-
@EnumSource(ServerType.class)
43-
void testLoadConfiguration(ServerType serverType, @TempDir Path tempFolder) throws Exception {
44-
Path yamlFile = tempFolder.resolve("server.yaml");
45-
Files.write(yamlFile, Collections.singleton("coordinator.port: 9124"));
51+
@MethodSource("parameterizedTestConfiguration")
52+
void testLoadWithCommonConfigurationTile(
53+
ServerType serverType,
54+
String commonConfigFileName,
55+
String additionalConfigFileName,
56+
@TempDir Path tempFolder)
57+
throws Exception {
58+
Path yamlFile = tempFolder.resolve(commonConfigFileName);
59+
Files.write(yamlFile, Collections.singleton("bind.listeners: FLUSS://localhost:9124"));
60+
if (commonConfigFileName.equals(NEW_COMMON_CONFIG_FILE_NAME)) {
61+
Files.write(tempFolder.resolve(additionalConfigFileName), Collections.emptyList());
62+
}
4663
String confDir = tempFolder.toAbsolutePath().toString();
64+
4765
final String key = "key";
4866
final String value = "value";
4967
final String arg1 = "arg1";
@@ -61,21 +79,29 @@ void testLoadConfiguration(ServerType serverType, @TempDir Path tempFolder) thro
6179
.isEqualTo(value);
6280

6381
// should respect the configurations in the server.yaml
64-
assertThat(configuration.getString(ConfigOptions.COORDINATOR_PORT)).isEqualTo("9124");
82+
assertThat(configuration.getString(ConfigOptions.BIND_LISTENERS))
83+
.isEqualTo("FLUSS://localhost:9124");
6584
}
6685

6786
@ParameterizedTest
68-
@EnumSource(ServerType.class)
69-
void testLoadWithUserSpecifiedConfigFile(ServerType serverType, @TempDir Path tempFolder)
87+
@MethodSource("parameterizedTestConfiguration")
88+
void testLoadWithUserSpecifiedConfigFile(
89+
ServerType serverType,
90+
String commonConfigFileName,
91+
String additionalConfigFileName,
92+
@TempDir Path tempFolder)
7093
throws Exception {
71-
Path yamlFile = tempFolder.resolve("server.yaml");
72-
Files.write(yamlFile, Collections.singleton("coordinator.port: 9124"));
94+
Path yamlFile = tempFolder.resolve(commonConfigFileName);
95+
Files.write(yamlFile, Collections.singleton("bind.listeners: FLUSS://localhost:9124"));
96+
if (commonConfigFileName.equals(NEW_COMMON_CONFIG_FILE_NAME)) {
97+
Files.write(tempFolder.resolve(additionalConfigFileName), Collections.emptyList());
98+
}
7399
String confDir = tempFolder.toAbsolutePath().toString();
74100

75101
Path userDefinedConfigFile = tempFolder.resolve("user-defined-server.yaml");
76-
Files.write(yamlFile, Collections.singleton("coordinator.port: 1000"));
102+
Files.write(yamlFile, Collections.singleton("bind.listeners: FLUSS://example.com:1000"));
77103

78-
final String configKey = GlobalConfiguration.FLUSS_CONF_FILENAME[0];
104+
final String configKey = commonConfigFileName;
79105
final String configValue = userDefinedConfigFile.toString();
80106

81107
final String[] args = {
@@ -87,13 +113,13 @@ void testLoadWithUserSpecifiedConfigFile(ServerType serverType, @TempDir Path te
87113
// should use the configurations in the user-defined-server.yaml
88114
assertThat(
89115
configuration.get(
90-
ConfigBuilder.key("coordinator.port").intType().noDefaultValue()))
91-
.isEqualTo(1000);
116+
ConfigBuilder.key("bind.listeners").stringType().noDefaultValue()))
117+
.isEqualTo("FLUSS://example.com:1000");
92118
}
93119

94120
@ParameterizedTest
95121
@EnumSource(ServerType.class)
96-
void testLoadConfigurationThrowException(ServerType serverType) {
122+
void testMissingConfigDirOptionThrowsException(ServerType serverType) {
97123
// should throw exception when miss options 'c'('configDir')
98124
assertThatThrownBy(
99125
() ->
@@ -107,4 +133,132 @@ void testLoadConfigurationThrowException(ServerType serverType) {
107133
.isInstanceOf(MissingOptionException.class)
108134
.hasMessageContaining("Missing required option: c");
109135
}
136+
137+
@ParameterizedTest
138+
@MethodSource("parameterizedTestConfiguration")
139+
void testOnlyLoadRelevantConfigFiles(
140+
ServerType serverType,
141+
String commonConfigFileName,
142+
String additionalConfigFileName,
143+
@TempDir Path tempFolder)
144+
throws Exception {
145+
Path commonYamlFile = tempFolder.resolve(commonConfigFileName);
146+
Files.write(commonYamlFile, Collections.singleton("bind.listeners: FLUSS://common:9124"));
147+
148+
// make all possible config files available in config dir to check only the relevant ones
149+
// are loaded
150+
if (serverType.equals(ServerType.COORDINATOR)) {
151+
Files.write(
152+
tempFolder.resolve(additionalConfigFileName),
153+
Collections.singleton("bind.listeners: FLUSS://dedicated:9124"));
154+
Files.write(
155+
tempFolder.resolve(TABLET_SERVER_CONF_FILE_NAME),
156+
Collections.singleton(
157+
"bind.listeners: FLUSS://expected-to-read-only-coordinator-server-yaml:9124"));
158+
} else {
159+
Files.write(
160+
tempFolder.resolve(additionalConfigFileName),
161+
Collections.singleton("bind.listeners: FLUSS://dedicated:9124"));
162+
Files.write(
163+
tempFolder.resolve(COORDINATOR_SERVER_CONF_FILE_NAME),
164+
Collections.singleton(
165+
"bind.listeners: FLUSS://expected-to-read-only-tablet-server-yaml:9124"));
166+
}
167+
// in addition, put another YAML file on the config dir
168+
Files.write(
169+
tempFolder.resolve("conf.yaml"),
170+
Collections.singleton(
171+
"bind.listeners: FLUSS://expected-conf-yaml-to-be-not-read:9124"));
172+
173+
String confDir = tempFolder.toAbsolutePath().toString();
174+
175+
String[] args = {"--configDir", confDir};
176+
177+
Configuration configuration =
178+
ConfigurationParserUtils.loadConfiguration(
179+
args, ConfigurationParserUtilsTest.class.getSimpleName(), serverType);
180+
181+
if (commonConfigFileName.equals(OLD_COMMON_CONFIG_FILE_NAME)) {
182+
// backward compatability
183+
// should only load from old file, no additional files should be read
184+
assertThat(configuration.getString(ConfigOptions.BIND_LISTENERS))
185+
.isEqualTo("FLUSS://common:9124");
186+
} else if (commonConfigFileName.equals(NEW_COMMON_CONFIG_FILE_NAME)) {
187+
// when using new common config file, dedicated config files should overwrite options
188+
// from common, but only the files for the respective server type should be read
189+
assertThat(configuration.getString(ConfigOptions.BIND_LISTENERS))
190+
.isEqualTo("FLUSS://dedicated:9124");
191+
} else {
192+
fail("Unexpected common config file name: " + commonConfigFileName);
193+
}
194+
195+
args =
196+
new String[] {
197+
"--configDir",
198+
confDir,
199+
String.format(
200+
"-D%s=%s",
201+
"bind.listeners",
202+
"FLUSS://dynamic-option-should-overwrite-config-file:9124"),
203+
String.format("-D%s=%s", "key", 1234)
204+
};
205+
206+
// dynamic configuration options should overwrite config file options
207+
configuration =
208+
ConfigurationParserUtils.loadConfiguration(
209+
args, ConfigurationParserUtilsTest.class.getSimpleName(), serverType);
210+
assertThat(configuration.getString(ConfigOptions.BIND_LISTENERS))
211+
.isEqualTo("FLUSS://dynamic-option-should-overwrite-config-file:9124");
212+
assertThat(configuration.getInt(ConfigBuilder.key("key").intType().noDefaultValue()))
213+
.isEqualTo(1234);
214+
}
215+
216+
@ParameterizedTest
217+
@EnumSource(ServerType.class)
218+
void testUsingOldAndNewCommonConfigThrowsException(
219+
ServerType serverType, @TempDir Path tempFolder) throws Exception {
220+
Path oldYaml = tempFolder.resolve(OLD_COMMON_CONFIG_FILE_NAME);
221+
Files.write(oldYaml, Collections.singleton("bind.listeners: FLUSS://localhost:9124"));
222+
Path newYaml = tempFolder.resolve(NEW_COMMON_CONFIG_FILE_NAME);
223+
Files.write(newYaml, Collections.singleton("bind.listeners: FLUSS://localhost:9124"));
224+
String confDir = tempFolder.toAbsolutePath().toString();
225+
226+
final String key = "key";
227+
final String value = "value";
228+
final String arg1 = "arg1";
229+
final String arg2 = "arg2";
230+
231+
final String[] args = {
232+
"--configDir", confDir, String.format("-D%s=%s", key, value), arg1, arg2
233+
};
234+
235+
assertThatThrownBy(
236+
() ->
237+
ConfigurationParserUtils.loadConfiguration(
238+
args,
239+
ConfigurationParserUtilsTest.class.getSimpleName(),
240+
serverType))
241+
.isInstanceOf(IllegalConfigurationException.class)
242+
.hasMessageContaining("Only one of");
243+
}
244+
245+
private static Stream<Arguments> parameterizedTestConfiguration() {
246+
return Stream.of(
247+
Arguments.of(
248+
ServerType.COORDINATOR,
249+
OLD_COMMON_CONFIG_FILE_NAME,
250+
COORDINATOR_SERVER_CONF_FILE_NAME),
251+
Arguments.of(
252+
ServerType.TABLET_SERVER,
253+
OLD_COMMON_CONFIG_FILE_NAME,
254+
TABLET_SERVER_CONF_FILE_NAME),
255+
Arguments.of(
256+
ServerType.COORDINATOR,
257+
NEW_COMMON_CONFIG_FILE_NAME,
258+
COORDINATOR_SERVER_CONF_FILE_NAME),
259+
Arguments.of(
260+
ServerType.TABLET_SERVER,
261+
NEW_COMMON_CONFIG_FILE_NAME,
262+
TABLET_SERVER_CONF_FILE_NAME));
263+
}
110264
}

0 commit comments

Comments
 (0)
Please sign in to comment.