diff --git a/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java b/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java index f5a575399..03b5bfcbd 100644 --- a/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java +++ b/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java @@ -1,6 +1,7 @@ package run.halo.app.infra; import java.io.IOException; +import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -9,10 +10,12 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Unstructured; import run.halo.app.infra.properties.HaloProperties; @@ -60,18 +63,17 @@ public class ExtensionResourceInitializer { .map(this::listResources) .distinct() .flatMapIterable(resources -> resources) - .doOnNext(resource -> log.debug("Initializing extension resource: {}", resource)) + .doOnNext(resource -> log.debug("Initializing extension resource from location: {}", + resource)) .map(resource -> new YamlUnstructuredLoader(resource).load()) .flatMapIterable(extensions -> extensions) - .flatMap(extension -> extensionClient.fetch(extension.groupVersionKind(), - extension.getMetadata().getName()) - .flatMap(createdExtension -> { - extension.getMetadata() - .setVersion(createdExtension.getMetadata().getVersion()); - return extensionClient.update(extension); - }) - .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))) - ) + .doOnNext(extension -> { + if (log.isDebugEnabled()) { + log.debug("Initializing extension resource: {}/{}", + extension.groupVersionKind(), extension.getMetadata().getName()); + } + }) + .flatMap(this::createOrUpdate) .doOnNext(extension -> { if (log.isDebugEnabled()) { log.debug("Initialized extension resource: {}/{}", extension.groupVersionKind(), @@ -81,6 +83,24 @@ public class ExtensionResourceInitializer { .then(); } + private Mono createOrUpdate(Unstructured extension) { + return Mono.just(extension) + .flatMap(ext -> extensionClient.fetch(extension.groupVersionKind(), + extension.getMetadata().getName())) + .flatMap(existingExt -> { + extension.getMetadata().setVersion(existingExt.getMetadata().getVersion()); + return extensionClient.update(extension); + }) + .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))) + .retryWhen(Retry.fixedDelay(3, Duration.ofMillis(100)) + .filter(t -> t instanceof OptimisticLockingFailureException)) + .onErrorContinue(OptimisticLockingFailureException.class, (throwable, o) -> { + log.warn("Failed to create or update extension resource: {}/{} due to modification " + + "conflict", + extension.groupVersionKind(), extension.getMetadata().getName()); + }); + } + private List listResources(String location) { var resolver = new PathMatchingResourcePatternResolver(); try {