Skip to content

Commit

Permalink
perf: add caching for extension getter to enhance performance
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Dec 2, 2024
1 parent 033a77e commit 5faeab8
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -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<E extends Extension>
implements Reconciler<Reconciler.Request>, DisposableBean {

protected final ConcurrentMap<String, E> 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<E>) 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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -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 <T extends ExtensionPoint> Flux<T> getExtensions(Class<T> extensionPoint) {
Expand Down Expand Up @@ -86,9 +80,7 @@ private <T extends ExtensionPoint> Flux<T> 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(
Expand All @@ -101,15 +93,7 @@ private <T extends ExtensionPoint> Flux<T> getEnabledExtensions(String epdName,

private Mono<ExtensionPointDefinition> fetchExtensionPointDefinition(
Class<? extends ExtensionPoint> 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());
}

}
Original file line number Diff line number Diff line change
@@ -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<ExtensionDefinition> get(String name);
}
Original file line number Diff line number Diff line change
@@ -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<ExtensionDefinition>
implements ExtensionDefinitionGetter {

public ExtensionDefinitionGetterImpl(ExtensionClient client) {
super(client, new ExtensionDefinition());
}

@Override
public Mono<ExtensionDefinition> get(String name) {
return Mono.fromSupplier(() -> cache.get(name));
}

@Override
void putCache(ExtensionDefinition definition) {
cache.put(definition.getMetadata().getName(), definition);
}
}
Original file line number Diff line number Diff line change
@@ -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.
* <p>Retrieve by filedSelector: <code>spec.className</code></p>
*
* @param className extension point class name
*/
Mono<ExtensionPointDefinition> getByClassName(String className);
}
Original file line number Diff line number Diff line change
@@ -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<ExtensionPointDefinition>
implements ExtensionPointDefinitionGetter {

public ExtensionPointDefinitionGetterImpl(ExtensionClient client) {
super(client, new ExtensionPointDefinition());
}

@Override
public Mono<ExtensionPointDefinition> getByClassName(String className) {
return Mono.fromSupplier(() -> cache.get(className));
}

@Override
void putCache(ExtensionPointDefinition definition) {
var className = definition.getSpec().getClassName();
cache.put(className, definition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -34,7 +31,10 @@
class DefaultExtensionGetterTest {

@Mock
ReactiveExtensionClient client;
ExtensionPointDefinitionGetter extensionPointDefinitionGetter;

@Mock
ExtensionDefinitionGetter extensionDefinitionGetter;

@Mock
PluginManager pluginManager;
Expand All @@ -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,
Expand Down Expand Up @@ -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());
Expand All @@ -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,
Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit 5faeab8

Please sign in to comment.