Skip to content

Commit

Permalink
feat: support incremental configuration synchronization client (#90)
Browse files Browse the repository at this point in the history
* feat: support incremental configuration synchronization client

* code format

* add Unknown sync mode

* code format

* code format

* code format

* code format

* code format

* 更新 RemoteConfigRepository.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ApolloConfig.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ApolloConfig.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigurationChange.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigSyncType.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigSyncType.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigSyncType.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigurationChange.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigurationChangeType.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigurationChangeTypeUtils.java

Co-authored-by: Jason Song <[email protected]>

* 更新 ConfigSyncType.java

Co-authored-by: Jason Song <[email protected]>

* code format

* code format

* add UnknownSync action

* Apply suggestions from code review

---------

Co-authored-by: Jason Song <[email protected]>
  • Loading branch information
jackie-coming and nobodyiam authored Dec 28, 2024
1 parent eff90aa commit b476bb8
Show file tree
Hide file tree
Showing 8 changed files with 450 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Apollo Java 2.4.0
* [Feature support pulling configuration information from multiple AppIds](https://github.com/apolloconfig/apollo-java/pull/70)
* [Fix monitor arg cause npe](https://github.com/apolloconfig/apollo-java/pull/86)
* [Fix the concurrent issue in SpringValueRegistry.scanAndClean](https://github.com/apolloconfig/apollo-java/pull/95)
* [Feature support incremental configuration synchronization client](https://github.com/apolloconfig/apollo-java/pull/90)

------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/4?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
import com.ctrip.framework.apollo.core.dto.ConfigurationChange;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.ConfigSyncType;
import com.ctrip.framework.apollo.core.enums.ConfigurationChangeType;
import com.ctrip.framework.apollo.core.schedule.ExponentialSchedulePolicy;
import com.ctrip.framework.apollo.core.schedule.SchedulePolicy;
import com.ctrip.framework.apollo.core.signature.Signature;
Expand All @@ -49,6 +52,7 @@
import com.google.common.util.concurrent.RateLimiter;
import com.google.gson.Gson;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
Expand Down Expand Up @@ -255,6 +259,24 @@ private ApolloConfig loadApolloConfig() {

ApolloConfig result = response.getBody();

if (result != null) {
ConfigSyncType configSyncType = ConfigSyncType.fromString(result.getConfigSyncType());

if (configSyncType == ConfigSyncType.INCREMENTAL_SYNC) {
ApolloConfig previousConfig = m_configCache.get();
Map<String, String> previousConfigurations =
(previousConfig != null) ? previousConfig.getConfigurations() : null;
result.setConfigurations(
mergeConfigurations(previousConfigurations, result.getConfigurationChanges()));
} else if (configSyncType == ConfigSyncType.UNKNOWN) {
String message = String.format(
"Invalid config sync type - %s",
result.getConfigSyncType());
throw new ApolloConfigException(message, exception);
}

}

logger.debug("Loaded config for {}: {}", m_namespace, result);

return result;
Expand All @@ -269,7 +291,7 @@ private ApolloConfig loadApolloConfig() {
statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(),
message);
Tracer.logEvent(APOLLO_CLIENT_NAMESPACE_NOT_FOUND,m_namespace);

}
Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(statusCodeException));
transaction.setStatus(statusCodeException);
Expand Down Expand Up @@ -363,4 +385,34 @@ private List<ServiceDTO> getConfigServices() {

return services;
}

Map<String, String> mergeConfigurations(Map<String, String> previousConfigurations,
List<ConfigurationChange> configurationChanges) {
Map<String, String> newConfigurations = new HashMap<>();

if (previousConfigurations != null) {
newConfigurations = Maps.newHashMap(previousConfigurations);
}

if (configurationChanges == null) {
return newConfigurations;
}

for (ConfigurationChange change : configurationChanges) {
switch (ConfigurationChangeType.fromString(change.getConfigurationChangeType())) {
case ADDED:
case MODIFIED:
newConfigurations.put(change.getKey(), change.getNewValue());
break;
case DELETED:
newConfigurations.remove(change.getKey());
break;
default:
//do nothing
break;
}
}

return newConfigurations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
import com.ctrip.framework.apollo.core.dto.ConfigurationChange;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.ConfigSyncType;
import com.ctrip.framework.apollo.core.signature.Signature;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
Expand All @@ -53,6 +55,7 @@
import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.Gson;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
Expand Down Expand Up @@ -154,6 +157,141 @@ public void testLoadConfig() throws Exception {
assertEquals(ConfigSourceType.REMOTE, remoteConfigRepository.getSourceType());
}

@Test
public void testLoadConfigWithIncrementalSync() throws Exception {

String someKey = "someKey";
String someValue = "someValue";
String someKey1 = "someKey1";
String someValue1 = "someKey1";
Map<String, String> configurations = Maps.newHashMap();
configurations.put(someKey, someValue);
configurations.put(someKey1, someValue1);
ApolloConfig someApolloConfig = assembleApolloConfig(configurations);

when(someResponse.getStatusCode()).thenReturn(200);
when(someResponse.getBody()).thenReturn(someApolloConfig);

RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
someNamespace);

remoteConfigRepository.sync();

List<ConfigurationChange> configurationChanges = new ArrayList<>();
String someNewValue = "someNewValue";
configurationChanges.add(new ConfigurationChange(someKey, someNewValue, "MODIFIED"));
configurationChanges.add(new ConfigurationChange(someKey1, null, "DELETED"));
String someKey2 = "someKey2";
String someValue2 = "someValue2";
configurationChanges.add(new ConfigurationChange(someKey2, someValue2, "ADDED"));
ApolloConfig someApolloConfigWithIncrementalSync = assembleApolloConfigWithIncrementalSync(
configurationChanges);

when(someResponse.getStatusCode()).thenReturn(200);
when(someResponse.getBody()).thenReturn(someApolloConfigWithIncrementalSync);

remoteConfigRepository.sync();

Properties config = remoteConfigRepository.getConfig();

assertEquals(2, config.size());
assertEquals("someNewValue", config.getProperty("someKey"));
assertEquals("someValue2", config.getProperty("someKey2"));
assertEquals(ConfigSourceType.REMOTE, remoteConfigRepository.getSourceType());
remoteConfigLongPollService.stopLongPollingRefresh();
}

@Test
public void testMergeConfigurations() throws Exception {
String key1 = "key1";
String value1 = "value1";
String anotherValue1 = "anotherValue1";

String key3 = "key3";
String value3 = "value3";
Map<String, String> previousConfigurations = ImmutableMap.of(key1, value1, key3, value3);

List<ConfigurationChange> configurationChanges = new ArrayList<>();
configurationChanges.add(new ConfigurationChange(key1, anotherValue1, "MODIFIED"));
String key2 = "key2";
String value2 = "value2";
configurationChanges.add(new ConfigurationChange(key2, value2, "ADDED"));
configurationChanges.add(new ConfigurationChange(key3, null, "DELETED"));

RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
someNamespace);
Map<String, String> result = remoteConfigRepository.mergeConfigurations(previousConfigurations,
configurationChanges);

assertEquals(2, result.size());
assertEquals(anotherValue1, result.get(key1));
assertEquals(value2, result.get(key2));
}

@Test
public void testMergeConfigurationWithPreviousConfigurationsIsNULL() throws Exception {
String key1 = "key1";
String value1 = "value1";

String key3 = "key3";
String value3 = "value3";

Map<String, String> previousConfigurations = ImmutableMap.of(key1, value1, key3, value3);

RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
someNamespace);
Map<String, String> result = remoteConfigRepository.mergeConfigurations(previousConfigurations,
null);

assertEquals(2, result.size());
assertEquals(value1, result.get(key1));
assertEquals(value3, result.get(key3));
}

@Test
public void testMergeConfigurationWithChangesIsNULL() throws Exception {
String key1 = "key1";
String value1 = "value1";
String anotherValue1 = "anotherValue1";

String key3 = "key3";
String value3 = "value3";

List<ConfigurationChange> configurationChanges = new ArrayList<>();
configurationChanges.add(new ConfigurationChange(key1, anotherValue1, "MODIFIED"));
String key2 = "key2";
String value2 = "value2";
configurationChanges.add(new ConfigurationChange(key2, value2, "ADDED"));
configurationChanges.add(new ConfigurationChange(key3, null, "DELETED"));

RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
someNamespace);
Map<String, String> result = remoteConfigRepository.mergeConfigurations(null,
configurationChanges);

assertEquals(2, result.size());
assertEquals(anotherValue1, result.get(key1));
assertEquals(value2, result.get(key2));
}

@Test(expected = ApolloConfigException.class)
public void testGetRemoteConfigWithUnknownSync() throws Exception {

ApolloConfig someApolloConfigWithUnknownSync = assembleApolloConfigWithUnknownSync(
new ArrayList<>());

when(someResponse.getStatusCode()).thenReturn(200);
when(someResponse.getBody()).thenReturn(someApolloConfigWithUnknownSync);

RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
someNamespace);

//must stop the long polling before exception occurred
remoteConfigLongPollService.stopLongPollingRefresh();

remoteConfigRepository.getConfig();
}

@Test
public void testLoadConfigWithOrderedProperties() throws Exception {
String someKey = "someKey";
Expand Down Expand Up @@ -371,6 +509,32 @@ private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
return apolloConfig;
}

private ApolloConfig assembleApolloConfigWithIncrementalSync(
List<ConfigurationChange> configurationChanges) {
String someAppId = "appId";
String someClusterName = "cluster";
String someReleaseKey = "1";
ApolloConfig apolloConfig =
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseKey);

apolloConfig.setConfigSyncType(ConfigSyncType.INCREMENTAL_SYNC.getValue());
apolloConfig.setConfigurationChanges(configurationChanges);
return apolloConfig;
}

private ApolloConfig assembleApolloConfigWithUnknownSync(
List<ConfigurationChange> configurationChanges) {
String someAppId = "appId";
String someClusterName = "cluster";
String someReleaseKey = "1";
ApolloConfig apolloConfig =
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseKey);

apolloConfig.setConfigSyncType(ConfigSyncType.UNKNOWN.getValue());
apolloConfig.setConfigurationChanges(configurationChanges);
return apolloConfig;
}

public static class MockConfigUtil extends ConfigUtil {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.core.dto;

import java.util.List;
import java.util.Map;

/**
Expand All @@ -33,6 +34,10 @@ public class ApolloConfig {

private String releaseKey;

private String configSyncType;

private List<ConfigurationChange> configurationChanges;

public ApolloConfig() {
}

Expand Down Expand Up @@ -62,6 +67,14 @@ public String getReleaseKey() {
return releaseKey;
}

public String getConfigSyncType() {
return configSyncType;
}

public List<ConfigurationChange> getConfigurationChanges() {
return configurationChanges;
}

public Map<String, String> getConfigurations() {
return configurations;
}
Expand All @@ -82,10 +95,18 @@ public void setReleaseKey(String releaseKey) {
this.releaseKey = releaseKey;
}

public void setConfigSyncType(String configSyncType) {
this.configSyncType = configSyncType;
}

public void setConfigurations(Map<String, String> configurations) {
this.configurations = configurations;
}

public void setConfigurationChanges(List<ConfigurationChange> configurationChanges) {
this.configurationChanges = configurationChanges;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ApolloConfig{");
Expand Down
Loading

0 comments on commit b476bb8

Please sign in to comment.