Skip to content

Commit

Permalink
Continue extension updation if modification conflict occurs (#2536)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/kind bug
/area core
/milestone 2.0

#### What this PR does / why we need it:

When we initialize default extensions at Halo startup, updation of one extension is very likely to be failed due to modification conflict. Then, remaining extensions won't be updated anymore.

Therefore, this PR resolve this problem by retrying updation with 3 times with interval 100ms and continue extension updation if modification conflict occurs.

#### How to test

1. Fully initialize system(e.g.: Clear `~/halo-next` before starting Halo)
2. Update Role `authenticated` as you wish via Extension API
3. Restart Halo and check it again
4. Retry multiple times

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

```release-note
修复系统默认数据无法正常更新的问题
```
  • Loading branch information
JohnNiang authored Oct 12, 2022
1 parent dbaa087 commit cc221d3
Showing 1 changed file with 30 additions and 10 deletions.
40 changes: 30 additions & 10 deletions src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,10 +10,12 @@
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;
Expand Down Expand Up @@ -60,18 +63,17 @@ public Mono<Void> initialize(ApplicationReadyEvent readyEvent) {
.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(),
Expand All @@ -81,6 +83,24 @@ public Mono<Void> initialize(ApplicationReadyEvent readyEvent) {
.then();
}

private Mono<Unstructured> 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<Resource> listResources(String location) {
var resolver = new PathMatchingResourcePatternResolver();
try {
Expand Down

0 comments on commit cc221d3

Please sign in to comment.