Skip to content

Commit

Permalink
Config service cache and namespace name normalization
Browse files Browse the repository at this point in the history
1. Enable config cache to improve config service performance, just set
config-service.cache.enabled to true in ServerConfig
2. Normalize the namespace name in case the one provided has character
case issue
  • Loading branch information
nobodyiam committed Aug 27, 2017
1 parent f68cb45 commit 699009f
Show file tree
Hide file tree
Showing 44 changed files with 2,363 additions and 500 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -70,6 +71,7 @@ public GrayReleaseRuleDTO findBranchGrayRules(@PathVariable String appId,
return ruleDTO;
}

@Transactional
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules", method = RequestMethod.PUT)
public void updateBranchGrayRules(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String branchName,
Expand All @@ -87,6 +89,7 @@ public void updateBranchGrayRules(@PathVariable String appId, @PathVariable Stri
Topics.APOLLO_RELEASE_TOPIC);
}

@Transactional
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}", method = RequestMethod.DELETE)
public void deleteBranch(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String branchName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public ReleaseDTO getLatest(@PathVariable("appId") String appId,
return BeanUtils.transfrom(ReleaseDTO.class, release);
}

@Transactional
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
public ReleaseDTO publish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
Expand Down Expand Up @@ -160,6 +161,7 @@ public ReleaseDTO updateAndPublish(@PathVariable("appId") String appId,

}

@Transactional
@RequestMapping(path = "/releases/{releaseId}/rollback", method = RequestMethod.PUT)
public void rollback(@PathVariable("releaseId") long releaseId,
@RequestParam("operator") String operator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
@Component
public class BizConfig extends RefreshableConfig {

private static final int DEFAULT_ITEM_KEY_LENGTH = 128;
private static final int DEFAULT_ITEM_VALUE_LENGTH = 20000;
private static final int DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL = 60; //60s
private static final int DEFAULT_GRAY_RELEASE_RULE_SCAN_INTERVAL = 60; //60s
private static final int DEFAULT_APPNAMESPACE_CACHE_SCAN_INTERVAL = 1; //1s
private static final int DEFAULT_RELEASE_MESSAGE_CACHE_SCAN_INTERVAL = 1; //1s
private static final int DEFAULT_RELEASE_MESSAGE_SCAN_INTERVAL_IN_MS = 1000; //1000ms
private static final int DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH = 100;
private static final int DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH_INTERVAL_IN_MILLI = 100;//100ms

private Gson gson = new Gson();
private static final Type namespaceValueLengthOverrideTypeReference =
new TypeToken<Map<Long, Integer>>() {
Expand All @@ -44,18 +54,18 @@ public List<String> eurekaServiceUrls() {
}

public int grayReleaseRuleScanInterval() {
int interval = getIntProperty("apollo.gray-release-rule-scan.interval", 60);
return checkInt(interval, 1, Integer.MAX_VALUE, 60);
int interval = getIntProperty("apollo.gray-release-rule-scan.interval", DEFAULT_GRAY_RELEASE_RULE_SCAN_INTERVAL);
return checkInt(interval, 1, Integer.MAX_VALUE, DEFAULT_GRAY_RELEASE_RULE_SCAN_INTERVAL);
}

public int itemKeyLengthLimit() {
int limit = getIntProperty("item.key.length.limit", 128);
return checkInt(limit, 5, Integer.MAX_VALUE, 128);
int limit = getIntProperty("item.key.length.limit", DEFAULT_ITEM_KEY_LENGTH);
return checkInt(limit, 5, Integer.MAX_VALUE, DEFAULT_ITEM_KEY_LENGTH);
}

public int itemValueLengthLimit() {
int limit = getIntProperty("item.value.length.limit", 20000);
return checkInt(limit, 5, Integer.MAX_VALUE, 20000);
int limit = getIntProperty("item.value.length.limit", DEFAULT_ITEM_VALUE_LENGTH);
return checkInt(limit, 5, Integer.MAX_VALUE, DEFAULT_ITEM_VALUE_LENGTH);
}

public Map<Long, Integer> namespaceValueLengthLimitOverride() {
Expand Down Expand Up @@ -85,40 +95,49 @@ public String cloggingPort() {
}

public int appNamespaceCacheScanInterval() {
int interval = getIntProperty("apollo.app-namespace-cache-scan.interval", 1);
return checkInt(interval, 1, Integer.MAX_VALUE, 1);
int interval = getIntProperty("apollo.app-namespace-cache-scan.interval", DEFAULT_APPNAMESPACE_CACHE_SCAN_INTERVAL);
return checkInt(interval, 1, Integer.MAX_VALUE, DEFAULT_APPNAMESPACE_CACHE_SCAN_INTERVAL);
}

public TimeUnit appNamespaceCacheScanIntervalTimeUnit() {
return TimeUnit.SECONDS;
}

public int appNamespaceCacheRebuildInterval() {
int interval = getIntProperty("apollo.app-namespace-cache-rebuild.interval", 60);
return checkInt(interval, 1, Integer.MAX_VALUE, 60);
int interval = getIntProperty("apollo.app-namespace-cache-rebuild.interval", DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL);
return checkInt(interval, 1, Integer.MAX_VALUE, DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL);
}

public TimeUnit appNamespaceCacheRebuildIntervalTimeUnit() {
return TimeUnit.SECONDS;
}

public int releaseMessageCacheScanInterval() {
int interval = getIntProperty("apollo.release-message-cache-scan.interval", 1);
return checkInt(interval, 1, Integer.MAX_VALUE, 1);
int interval = getIntProperty("apollo.release-message-cache-scan.interval", DEFAULT_RELEASE_MESSAGE_CACHE_SCAN_INTERVAL);
return checkInt(interval, 1, Integer.MAX_VALUE, DEFAULT_RELEASE_MESSAGE_CACHE_SCAN_INTERVAL);
}

public TimeUnit releaseMessageCacheScanIntervalTimeUnit() {
return TimeUnit.SECONDS;
}

public int releaseMessageScanIntervalInMilli() {
int interval = getIntProperty("apollo.message-scan.interval", DEFAULT_RELEASE_MESSAGE_SCAN_INTERVAL_IN_MS);
return checkInt(interval, 100, Integer.MAX_VALUE, DEFAULT_RELEASE_MESSAGE_SCAN_INTERVAL_IN_MS);
}

public int releaseMessageNotificationBatch() {
int batch = getIntProperty("apollo.release-message.notification.batch", 100);
return checkInt(batch, 1, Integer.MAX_VALUE, 100);
int batch = getIntProperty("apollo.release-message.notification.batch", DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH);
return checkInt(batch, 1, Integer.MAX_VALUE, DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH);
}

public int releaseMessageNotificationBatchIntervalInMilli() {
int interval = getIntProperty("apollo.release-message.notification.batch.interval", 100);
return checkInt(interval, 1, Integer.MAX_VALUE, 100);
int interval = getIntProperty("apollo.release-message.notification.batch.interval", DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH_INTERVAL_IN_MILLI);
return checkInt(interval, 10, Integer.MAX_VALUE, DEFAULT_RELEASE_MESSAGE_NOTIFICATION_BATCH_INTERVAL_IN_MILLI);
}

public boolean isConfigServiceCacheEnabled() {
return getBooleanProperty("config-service.cache.enabled", false);
}

int checkInt(int value, int min, int max, int defaultValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public void sendMessage(String message, String channel) {
} catch (Throwable ex) {
logger.error("Sending message to database failed", ex);
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
package com.ctrip.framework.apollo.biz.message;

import com.google.common.collect.Lists;

import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.google.common.collect.Lists;

/**
* @author Jason Song([email protected])
*/
public class ReleaseMessageScanner implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(ReleaseMessageScanner.class);
private static final int DEFAULT_SCAN_INTERVAL_IN_MS = 1000;
@Autowired
private Environment env;
private BizConfig bizConfig;
@Autowired
private ReleaseMessageRepository releaseMessageRepository;
private int databaseScanInterval;
Expand All @@ -44,7 +41,7 @@ public ReleaseMessageScanner() {

@Override
public void afterPropertiesSet() throws Exception {
populateDataBaseInterval();
databaseScanInterval = bizConfig.releaseMessageScanIntervalInMilli();
maxIdScanned = loadLargestMessageId();
executorService.scheduleWithFixedDelay((Runnable) () -> {
Transaction transaction = Tracer.newTransaction("Apollo.ReleaseMessageScanner", "scanMessage");
Expand All @@ -57,7 +54,7 @@ public void afterPropertiesSet() throws Exception {
} finally {
transaction.complete();
}
}, getDatabaseScanIntervalMs(), getDatabaseScanIntervalMs(), TimeUnit.MILLISECONDS);
}, databaseScanInterval, databaseScanInterval, TimeUnit.MILLISECONDS);

}

Expand Down Expand Up @@ -124,21 +121,4 @@ private void fireMessageScanned(List<ReleaseMessage> messages) {
}
}
}

private void populateDataBaseInterval() {
databaseScanInterval = DEFAULT_SCAN_INTERVAL_IN_MS;
try {
String interval = env.getProperty("apollo.message-scan.interval");
if (!Objects.isNull(interval)) {
databaseScanInterval = Integer.parseInt(interval);
}
} catch (Throwable ex) {
Tracer.logError(ex);
logger.error("Load apollo message scan interval from system property failed", ex);
}
}

private int getDatabaseScanIntervalMs() {
return databaseScanInterval;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.biz.utils.ReleaseMessageKeyGenerator;
import com.ctrip.framework.apollo.common.constants.GsonType;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
Expand Down Expand Up @@ -59,6 +62,8 @@ public class NamespaceService {
private NamespaceLockService namespaceLockService;
@Autowired
private InstanceService instanceService;
@Autowired
private MessageSender messageSender;


public Namespace findOne(Long namespaceId) {
Expand Down Expand Up @@ -282,7 +287,13 @@ public Namespace deleteNamespace(Namespace namespace, String operator) {

auditService.audit(Namespace.class.getSimpleName(), namespace.getId(), Audit.OP.DELETE, operator);

return namespaceRepository.save(namespace);
Namespace deleted = namespaceRepository.save(namespace);

//Publish release message to do some clean up in config service, such as updating the cache
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);

return deleted;
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* @author Jason Song([email protected])
Expand All @@ -33,6 +35,11 @@ public void setUp() throws Exception {
@Test
public void testSendMessage() throws Exception {
String someMessage = "some-message";
long someId = 1;
ReleaseMessage someReleaseMessage = mock(ReleaseMessage.class);
when(someReleaseMessage.getId()).thenReturn(someId);
when(releaseMessageRepository.save(any(ReleaseMessage.class))).thenReturn(someReleaseMessage);

ArgumentCaptor<ReleaseMessage> captor = ArgumentCaptor.forClass(ReleaseMessage.class);

messageSender.sendMessage(someMessage, Topics.APOLLO_RELEASE_TOPIC);
Expand All @@ -50,4 +57,12 @@ public void testSendUnsupportedMessage() throws Exception {

verify(releaseMessageRepository, never()).save(any(ReleaseMessage.class));
}

@Test(expected = RuntimeException.class)
public void testSendMessageFailed() throws Exception {
String someMessage = "some-message";
when(releaseMessageRepository.save(any(ReleaseMessage.class))).thenThrow(new RuntimeException());

messageSender.sendMessage(someMessage, Topics.APOLLO_RELEASE_TOPIC);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ctrip.framework.apollo.biz.message;

import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.SettableFuture;

Expand All @@ -26,17 +27,17 @@ public class ReleaseMessageScannerTest extends AbstractUnitTest {
@Mock
private ReleaseMessageRepository releaseMessageRepository;
@Mock
private Environment env;
private BizConfig bizConfig;
private int databaseScanInterval;

@Before
public void setUp() throws Exception {
releaseMessageScanner = new ReleaseMessageScanner();
ReflectionTestUtils
.setField(releaseMessageScanner, "releaseMessageRepository", releaseMessageRepository);
ReflectionTestUtils.setField(releaseMessageScanner, "env", env);
ReflectionTestUtils.setField(releaseMessageScanner, "bizConfig", bizConfig);
databaseScanInterval = 100; //100 ms
when(env.getProperty("apollo.message-scan.interval")).thenReturn(String.valueOf(databaseScanInterval));
when(bizConfig.releaseMessageScanIntervalInMilli()).thenReturn(databaseScanInterval);
releaseMessageScanner.afterPropertiesSet();
}

Expand Down
Loading

0 comments on commit 699009f

Please sign in to comment.