From 5faeab8785853e01d9a69d6c877d96a0912872cb Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Mon, 2 Dec 2024 12:31:52 +0800 Subject: [PATCH] perf: add caching for extension getter to enhance performance --- .../AbstractDefinitionGetter.java | 44 ++++++++++++++ .../DefaultExtensionGetter.java | 26 ++------ .../ExtensionDefinitionGetter.java | 13 ++++ .../ExtensionDefinitionGetterImpl.java | 25 ++++++++ .../ExtensionPointDefinitionGetter.java | 14 +++++ .../ExtensionPointDefinitionGetterImpl.java | 26 ++++++++ .../DefaultExtensionGetterTest.java | 60 +++++++++---------- 7 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 application/src/main/java/run/halo/app/plugin/extensionpoint/AbstractDefinitionGetter.java create mode 100644 application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetter.java create mode 100644 application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetterImpl.java create mode 100644 application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetter.java create mode 100644 application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetterImpl.java diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/AbstractDefinitionGetter.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/AbstractDefinitionGetter.java new file mode 100644 index 0000000000..96545966dd --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/AbstractDefinitionGetter.java @@ -0,0 +1,44 @@ +package run.halo.app.plugin.extensionpoint; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.DisposableBean; +import run.halo.app.extension.Extension; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.Reconciler; + +@RequiredArgsConstructor +abstract class AbstractDefinitionGetter + implements Reconciler, DisposableBean { + + protected final ConcurrentMap cache = new ConcurrentHashMap<>(); + + private final ExtensionClient client; + + private final E watchType; + + abstract void putCache(E definition); + + @Override + @SuppressWarnings("unchecked") + public Result reconcile(Request request) { + client.fetch((Class) watchType.getClass(), request.name()) + .ifPresent(this::putCache); + return Result.doNotRetry(); + } + + @Override + public Controller setupWith(ControllerBuilder builder) { + return builder.extension(watchType) + .syncAllOnStart(true) + .build(); + } + + @Override + public void destroy() throws Exception { + cache.clear(); + } +} diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java index 42b833e16d..52163e7770 100644 --- a/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java @@ -1,7 +1,5 @@ package run.halo.app.plugin.extensionpoint; -import static run.halo.app.extension.index.query.QueryFactory.equal; - import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -11,15 +9,9 @@ import org.pf4j.PluginManager; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.halo.app.extension.ListOptions; -import run.halo.app.extension.ListResult; -import run.halo.app.extension.PageRequestImpl; -import run.halo.app.extension.ReactiveExtensionClient; -import run.halo.app.extension.router.selector.FieldSelector; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemSetting.ExtensionPointEnabled; @@ -33,7 +25,9 @@ public class DefaultExtensionGetter implements ExtensionGetter { private final BeanFactory beanFactory; - private final ReactiveExtensionClient client; + private final ExtensionDefinitionGetter extensionDefinitionGetter; + + private final ExtensionPointDefinitionGetter extensionPointDefinitionGetter; @Override public Flux getExtensions(Class extensionPoint) { @@ -86,9 +80,7 @@ private Flux getEnabledExtensions(String epdName, } var extensions = getExtensions(extensionPoint).cache(); return Flux.fromIterable(extensionDefNames) - .flatMapSequential(extensionDefName -> - client.fetch(ExtensionDefinition.class, extensionDefName) - ) + .flatMapSequential(extensionDefinitionGetter::get) .flatMapSequential(extensionDef -> { var className = extensionDef.getSpec().getClassName(); return extensions.filter( @@ -101,15 +93,7 @@ private Flux getEnabledExtensions(String epdName, private Mono fetchExtensionPointDefinition( Class extensionPoint) { - var listOptions = new ListOptions(); - listOptions.setFieldSelector(FieldSelector.of( - equal("spec.className", extensionPoint.getName()) - )); - var sort = Sort.by("metadata.creationTimestamp", "metadata.name").ascending(); - return client.listBy(ExtensionPointDefinition.class, listOptions, - PageRequestImpl.ofSize(1).withSort(sort) - ) - .flatMap(list -> Mono.justOrEmpty(ListResult.first(list))); + return extensionPointDefinitionGetter.getByClassName(extensionPoint.getName()); } } diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetter.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetter.java new file mode 100644 index 0000000000..fb534cbb1b --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetter.java @@ -0,0 +1,13 @@ +package run.halo.app.plugin.extensionpoint; + +import reactor.core.publisher.Mono; + +public interface ExtensionDefinitionGetter { + + /** + * Gets extension definition by extension definition name. + * + * @param name extension definition name + */ + Mono get(String name); +} diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetterImpl.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetterImpl.java new file mode 100644 index 0000000000..83e4e0b004 --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionDefinitionGetterImpl.java @@ -0,0 +1,25 @@ +package run.halo.app.plugin.extensionpoint; + +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ExtensionClient; + +@Component +public class ExtensionDefinitionGetterImpl + extends AbstractDefinitionGetter + implements ExtensionDefinitionGetter { + + public ExtensionDefinitionGetterImpl(ExtensionClient client) { + super(client, new ExtensionDefinition()); + } + + @Override + public Mono get(String name) { + return Mono.fromSupplier(() -> cache.get(name)); + } + + @Override + void putCache(ExtensionDefinition definition) { + cache.put(definition.getMetadata().getName(), definition); + } +} diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetter.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetter.java new file mode 100644 index 0000000000..d2fb248e1d --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetter.java @@ -0,0 +1,14 @@ +package run.halo.app.plugin.extensionpoint; + +import reactor.core.publisher.Mono; + +public interface ExtensionPointDefinitionGetter { + + /** + * Gets extension point definition by extension point class. + *

Retrieve by filedSelector: spec.className

+ * + * @param className extension point class name + */ + Mono getByClassName(String className); +} diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetterImpl.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetterImpl.java new file mode 100644 index 0000000000..075aadf64a --- /dev/null +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/ExtensionPointDefinitionGetterImpl.java @@ -0,0 +1,26 @@ +package run.halo.app.plugin.extensionpoint; + +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ExtensionClient; + +@Component +public class ExtensionPointDefinitionGetterImpl + extends AbstractDefinitionGetter + implements ExtensionPointDefinitionGetter { + + public ExtensionPointDefinitionGetterImpl(ExtensionClient client) { + super(client, new ExtensionPointDefinition()); + } + + @Override + public Mono getByClassName(String className) { + return Mono.fromSupplier(() -> cache.get(className)); + } + + @Override + void putCache(ExtensionPointDefinition definition) { + var className = definition.getSpec().getClassName(); + cache.put(className, definition); + } +} diff --git a/application/src/test/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetterTest.java b/application/src/test/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetterTest.java index b876aa10c7..e78dc787b5 100644 --- a/application/src/test/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetterTest.java +++ b/application/src/test/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetterTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.same; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static run.halo.app.infra.SystemSetting.ExtensionPointEnabled.GROUP; @@ -22,10 +22,7 @@ import org.springframework.core.annotation.Order; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import run.halo.app.extension.ListOptions; -import run.halo.app.extension.ListResult; import run.halo.app.extension.Metadata; -import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemSetting.ExtensionPointEnabled; import run.halo.app.plugin.extensionpoint.ExtensionPointDefinition.ExtensionPointType; @@ -34,7 +31,10 @@ class DefaultExtensionGetterTest { @Mock - ReactiveExtensionClient client; + ExtensionPointDefinitionGetter extensionPointDefinitionGetter; + + @Mock + ExtensionDefinitionGetter extensionDefinitionGetter; @Mock PluginManager pluginManager; @@ -54,15 +54,14 @@ class DefaultExtensionGetterTest { @Test void shouldGetExtensionBySingletonDefinitionWhenExtensionPointEnabledSet() { // prepare extension point definition - when(client.listBy(same(ExtensionPointDefinition.class), any(ListOptions.class), any())) - .thenReturn(Mono.fromSupplier(() -> { - var epd = createExtensionPointDefinition("fake-extension-point", + when(extensionPointDefinitionGetter.getByClassName(any())) + .thenReturn(Mono.fromSupplier( + () -> createExtensionPointDefinition("fake-extension-point", FakeExtensionPoint.class, - ExtensionPointType.SINGLETON); - return new ListResult<>(List.of(epd)); - })); + ExtensionPointType.SINGLETON)) + ); - when(client.fetch(ExtensionDefinition.class, "fake-extension")) + when(extensionDefinitionGetter.get(eq("fake-extension"))) .thenReturn(Mono.fromSupplier(() -> createExtensionDefinition( "fake-extension", FakeExtensionPointImpl.class, @@ -94,13 +93,12 @@ void shouldGetExtensionBySingletonDefinitionWhenExtensionPointEnabledSet() { @Test void shouldGetDefaultSingletonDefinitionWhileExtensionPointEnabledNotSet() { - when(client.listBy(same(ExtensionPointDefinition.class), any(ListOptions.class), any())) - .thenReturn(Mono.fromSupplier(() -> { - var epd = createExtensionPointDefinition("fake-extension-point", + when(extensionPointDefinitionGetter.getByClassName(any())) + .thenReturn(Mono.fromSupplier( + () -> createExtensionPointDefinition("fake-extension-point", FakeExtensionPoint.class, - ExtensionPointType.SINGLETON); - return new ListResult<>(List.of(epd)); - })); + ExtensionPointType.SINGLETON)) + ); when(configFetcher.fetch(GROUP, ExtensionPointEnabled.class)) .thenReturn(Mono.empty()); @@ -124,21 +122,20 @@ void shouldGetDefaultSingletonDefinitionWhileExtensionPointEnabledNotSet() { @Test void shouldGetMultiInstanceExtensionWhileExtensionPointEnabledSet() { // prepare extension point definition - when(client.listBy(same(ExtensionPointDefinition.class), any(ListOptions.class), any())) - .thenReturn(Mono.fromSupplier(() -> { - var epd = createExtensionPointDefinition("fake-extension-point", + when(extensionPointDefinitionGetter.getByClassName(any())) + .thenReturn(Mono.fromSupplier( + () -> createExtensionPointDefinition("fake-extension-point", FakeExtensionPoint.class, - ExtensionPointType.MULTI_INSTANCE); - return new ListResult<>(List.of(epd)); - })); + ExtensionPointType.MULTI_INSTANCE)) + ); - when(client.fetch(ExtensionDefinition.class, "fake-extension")) + when(extensionDefinitionGetter.get(eq("fake-extension"))) .thenReturn(Mono.fromSupplier(() -> createExtensionDefinition( "fake-extension", FakeExtensionPointImpl.class, "fake-extension-point"))); - when(client.fetch(ExtensionDefinition.class, "default-fake-extension")) + when(extensionDefinitionGetter.get(eq("default-fake-extension"))) .thenReturn(Mono.fromSupplier(() -> createExtensionDefinition( "default-fake-extension", FakeExtensionPointDefaultImpl.class, @@ -177,13 +174,12 @@ void shouldGetMultiInstanceExtensionWhileExtensionPointEnabledSet() { @Test void shouldGetMultiInstanceExtensionWhileExtensionPointEnabledNotSet() { // prepare extension point definition - when(client.listBy(same(ExtensionPointDefinition.class), any(ListOptions.class), any())) - .thenReturn(Mono.fromSupplier(() -> { - var epd = createExtensionPointDefinition("fake-extension-point", + when(extensionPointDefinitionGetter.getByClassName(any())) + .thenReturn(Mono.fromSupplier( + () -> createExtensionPointDefinition("fake-extension-point", FakeExtensionPoint.class, - ExtensionPointType.MULTI_INSTANCE); - return new ListResult<>(List.of(epd)); - })); + ExtensionPointType.MULTI_INSTANCE)) + ); when(configFetcher.fetch(GROUP, ExtensionPointEnabled.class)) .thenReturn(Mono.empty());