Skip to content

Commit

Permalink
perf: add caching for system configuration fetcher to enhance perform…
Browse files Browse the repository at this point in the history
…ance (#7100)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.20.x

#### What this PR does / why we need it:
为系统配置获取增加缓存以提高路由和主题模板渲染的速度

#### Special notes for your reviewer:
1. 系统能正确初始化
2. 测试修改系统配置后 http://localhost:8090/actuator/globalinfo 和主题端 `${site}` 是否都是新的
3. 更改了文章路由规则后能正确调整到新的规则

#### Does this PR introduce a user-facing change?

```release-note
为系统配置的获取增加缓存以提高路由和主题模板渲染的速度
```
  • Loading branch information
guqing authored Dec 4, 2024
1 parent 7bd9408 commit 2b4d1ab
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public RouterFunction<ServerResponse> endpoint() {
private Mono<ServerResponse> updateConfigByGroup(ServerRequest request) {
final var group = request.pathVariable("group");
return request.bodyToMono(ObjectNode.class)
.flatMap(objectNode -> configurableEnvironmentFetcher.getConfigMap()
.flatMap(objectNode -> configurableEnvironmentFetcher.loadConfigMap()
.flatMap(configMap -> {
var data = configMap.getData();
data.put(group, JsonUtils.objectToJson(objectNode));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,6 @@ public boolean isSystemSetting(String name) {
}

private Optional<ConfigMap> getConfigMap(String name) {
return environmentFetcher.getConfigMapBlocking();
return environmentFetcher.loadConfigMapBlocking();
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package run.halo.app.infra;

import static run.halo.app.extension.index.query.QueryFactory.equal;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.github.fge.jsonpatch.JsonPatchException;
import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ExtensionMatcher;
import run.halo.app.extension.ListOptions;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.controller.Controller;
import run.halo.app.extension.controller.ControllerBuilder;
import run.halo.app.extension.controller.Reconciler;
import run.halo.app.infra.utils.JsonParseException;
import run.halo.app.infra.utils.JsonUtils;

Expand All @@ -27,9 +36,10 @@
* @since 2.0.0
*/
@Component
public class SystemConfigurableEnvironmentFetcher {
public class SystemConfigurableEnvironmentFetcher implements Reconciler<Reconciler.Request> {
private final ReactiveExtensionClient extensionClient;
private final ConversionService conversionService;
private final AtomicReference<ConfigMap> configMapCache = new AtomicReference<>();

public SystemConfigurableEnvironmentFetcher(ReactiveExtensionClient extensionClient,
ConversionService conversionService) {
Expand Down Expand Up @@ -71,31 +81,27 @@ private Mono<Map<String, String>> getValuesInternal() {
.defaultIfEmpty(Map.of());
}

public Mono<ConfigMap> getConfigMap() {
return Mono.fromSupplier(configMapCache::get)
.switchIfEmpty(Mono.defer(this::loadConfigMapInternal));
}

/**
* Gets config map.
* Load the system config map from the extension client.
*
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
* @return latest configMap from {@link ReactiveExtensionClient} without any cache.
*/
public Mono<ConfigMap> getConfigMap() {
Mono<ConfigMap> mapMono =
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
if (mapMono == null) {
return Mono.empty();
}
return mapMono.flatMap(systemDefault ->
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
.map(system -> {
Map<String, String> defaultData = systemDefault.getData();
Map<String, String> data = system.getData();
Map<String, String> mergedData = mergeData(defaultData, data);
system.setData(mergedData);
return system;
})
.switchIfEmpty(Mono.just(systemDefault)));
public Mono<ConfigMap> loadConfigMap() {
return loadConfigMapInternal();
}

public Optional<ConfigMap> getConfigMapBlocking() {
return getConfigMap().blockOptional();
/**
* Gets the system config map without any cache.
*
* @return load configMap from {@link ReactiveExtensionClient}
*/
public Optional<ConfigMap> loadConfigMapBlocking() {
return loadConfigMapInternal().blockOptional();
}

private Map<String, String> mergeData(Map<String, String> defaultData,
Expand Down Expand Up @@ -132,7 +138,7 @@ private Map<String, String> mergeData(Map<String, String> defaultData,
return copiedDefault;
}

String mergeRemappingFunction(String dataV, String defaultV) {
private String mergeRemappingFunction(String dataV, String defaultV) {
JsonNode dataJsonValue = nullSafeToJsonNode(dataV);
// original
JsonNode defaultJsonValue = nullSafeToJsonNode(defaultV);
Expand All @@ -147,8 +153,57 @@ String mergeRemappingFunction(String dataV, String defaultV) {
}
}

JsonNode nullSafeToJsonNode(String json) {
private JsonNode nullSafeToJsonNode(String json) {
return StringUtils.isBlank(json) ? JsonNodeFactory.instance.nullNode()
: JsonUtils.jsonToObject(json, JsonNode.class);
}

@Override
public Result reconcile(Request request) {
loadConfigMapInternal()
// should never happen
.switchIfEmpty(Mono.error(new IllegalStateException("System configMap not found.")))
.doOnNext(configMapCache::set)
.block();
return Result.doNotRetry();
}

@Override
public Controller setupWith(ControllerBuilder builder) {
ExtensionMatcher matcher = extension -> Objects.equals(extension.getMetadata().getName(),
SystemSetting.SYSTEM_CONFIG);
return builder
.extension(new ConfigMap())
.syncAllOnStart(true)
.syncAllListOptions(ListOptions.builder()
.fieldQuery(equal("metadata.name", SystemSetting.SYSTEM_CONFIG))
.build())
.onAddMatcher(matcher)
.onUpdateMatcher(matcher)
.onDeleteMatcher(matcher)
.build();
}

/**
* Gets config map.
*
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
*/
private Mono<ConfigMap> loadConfigMapInternal() {
Mono<ConfigMap> mapMono =
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
if (mapMono == null) {
return Mono.empty();
}
return mapMono.flatMap(systemDefault ->
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
.map(system -> {
Map<String, String> defaultData = systemDefault.getData();
Map<String, String> data = system.getData();
Map<String, String> mergedData = mergeData(defaultData, data);
system.setData(mergedData);
return system;
})
.switchIfEmpty(Mono.just(systemDefault)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private Mono<Void> doInitialization(SetupRequest body) {
)
.subscribeOn(Schedulers.boundedElastic());

var basicConfigMono = Mono.defer(() -> systemConfigFetcher.getConfigMap()
var basicConfigMono = Mono.defer(() -> systemConfigFetcher.loadConfigMap()
.flatMap(configMap -> {
mergeToBasicConfig(body, configMap);
return client.update(configMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;
import run.halo.app.core.reconciler.SystemSettingReconciler;
import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.Metadata;
Expand Down Expand Up @@ -59,7 +58,7 @@ void reconcileArchivesRouteRule() {
rules.setArchives("archives-new");
return rules;
});
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
.thenReturn(Optional.of(configMap));
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
Expand All @@ -83,7 +82,7 @@ void reconcileTagsRule() {
rules.setTags("tags-new");
return rules;
});
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
.thenReturn(Optional.of(configMap));
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
Expand All @@ -104,7 +103,7 @@ void reconcileCategoriesRule() {
rules.setCategories("categories-new");
return rules;
});
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
.thenReturn(Optional.of(configMap));
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
Expand All @@ -125,7 +124,7 @@ void reconcilePostRule() {
rules.setPost("/post-new/{slug}");
return rules;
});
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
.thenReturn(Optional.of(configMap));
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ void setUp() {
}

@Test
void getConfigMap() {
environmentFetcher.getConfigMap()
void loadConfigMap() {
environmentFetcher.loadConfigMap()
.as(StepVerifier::create)
.consumeNextWith(configMap -> {
assertThat(configMap.getMetadata().getName())
Expand Down

0 comments on commit 2b4d1ab

Please sign in to comment.