mirror of https://github.com/halo-dev/halo
				
				
				
			fix: unique index conflict issue after backup restoration preventing startup (#6701)
#### What type of PR is this? /kind bug /area core /milestone 2.20.x /sig docs #### What this PR does / why we need it: 修复恢复备份后可能会因为与之前的数据冲突导致无法启动的问题 如果恢复时发生不可预知的错误,需要重启之后重新初始化再进行恢复 #### Which issue(s) this PR fixes: Fixes #6672 #### Does this PR introduce a user-facing change? ```release-note 修复恢复备份后可能会因为与恢复之前存在的数据冲突导致无法启动的问题 ```pull/6771/head^2
							parent
							
								
									ea491f2386
								
							
						
					
					
						commit
						04e195f034
					
				| 
						 | 
				
			
			@ -141,7 +141,14 @@ public class MigrationServiceImpl implements MigrationService, InitializingBean
 | 
			
		|||
        return Mono.usingWhen(
 | 
			
		||||
            createTempDir("halo-restore-", scheduler),
 | 
			
		||||
            tempDir -> unpackBackup(content, tempDir)
 | 
			
		||||
                .then(Mono.defer(() -> restoreExtensions(tempDir)))
 | 
			
		||||
                .then(Mono.defer(() ->
 | 
			
		||||
                    // This step skips index verification such as unique index.
 | 
			
		||||
                    // In order to avoid index conflicts after recovery or
 | 
			
		||||
                    // OptimisticLockingFailureException when updating the same record,
 | 
			
		||||
                    // so we need to truncate all extension stores before saving(create or update).
 | 
			
		||||
                    repository.deleteAll()
 | 
			
		||||
                        .then(restoreExtensions(tempDir)))
 | 
			
		||||
                )
 | 
			
		||||
                .then(Mono.defer(() -> restoreWorkdir(tempDir))),
 | 
			
		||||
            tempDir -> deleteRecursivelyAndSilently(tempDir, scheduler)
 | 
			
		||||
        );
 | 
			
		||||
| 
						 | 
				
			
			@ -241,13 +248,9 @@ public class MigrationServiceImpl implements MigrationService, InitializingBean
 | 
			
		|||
                            sink.complete();
 | 
			
		||||
                        })
 | 
			
		||||
                    // reset version
 | 
			
		||||
                    .doOnNext(extensionStore -> extensionStore.setVersion(null)).buffer(100)
 | 
			
		||||
                    // We might encounter OptimisticLockingFailureException when saving extension
 | 
			
		||||
                    // store,
 | 
			
		||||
                    // So we have to delete all extension stores before saving.
 | 
			
		||||
                    .flatMap(extensionStores -> repository.deleteAll(extensionStores)
 | 
			
		||||
                        .thenMany(repository.saveAll(extensionStores))
 | 
			
		||||
                    )
 | 
			
		||||
                    .doOnNext(extensionStore -> extensionStore.setVersion(null))
 | 
			
		||||
                    .buffer(100)
 | 
			
		||||
                    .flatMap(repository::saveAll)
 | 
			
		||||
                    .doOnNext(extensionStore -> log.info("Restored extension store: {}",
 | 
			
		||||
                        extensionStore.getName()))
 | 
			
		||||
                    .then(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,7 +120,7 @@ class MigrationServiceImplTest {
 | 
			
		|||
        expectStore.setVersion(null);
 | 
			
		||||
 | 
			
		||||
        when(haloProperties.getWorkDir()).thenReturn(workdir);
 | 
			
		||||
        when(repository.deleteAll(List.of(expectStore))).thenReturn(Mono.empty());
 | 
			
		||||
        when(repository.deleteAll()).thenReturn(Mono.empty());
 | 
			
		||||
        when(repository.saveAll(List.of(expectStore))).thenReturn(Flux.empty());
 | 
			
		||||
 | 
			
		||||
        var content = DataBufferUtils.read(backupFile,
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ class MigrationServiceImplTest {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
        verify(haloProperties).getWorkDir();
 | 
			
		||||
        verify(repository).deleteAll(List.of(expectStore));
 | 
			
		||||
        verify(repository).deleteAll();
 | 
			
		||||
        verify(repository).saveAll(List.of(expectStore));
 | 
			
		||||
 | 
			
		||||
        // make sure the workdir is recovered.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,7 +114,9 @@ useQuery({
 | 
			
		|||
          <ul>
 | 
			
		||||
            <li>{{ $t("core.backup.restore.tips.first") }}</li>
 | 
			
		||||
            <li>
 | 
			
		||||
              {{ $t("core.backup.restore.tips.second") }}
 | 
			
		||||
              <strong>
 | 
			
		||||
                {{ $t("core.backup.restore.tips.second") }}
 | 
			
		||||
              </strong>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
              {{ $t("core.backup.restore.tips.third") }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ const handleClose = () => {
 | 
			
		|||
  <VModal
 | 
			
		||||
    :visible="visible"
 | 
			
		||||
    :width="450"
 | 
			
		||||
    :layer-closable="true"
 | 
			
		||||
    :layer-closable="false"
 | 
			
		||||
    :data-unique-id="uniqueId"
 | 
			
		||||
    @close="handleCancel()"
 | 
			
		||||
  >
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1402,14 +1402,12 @@ core:
 | 
			
		|||
        first: >-
 | 
			
		||||
          1. The restore process may last for a long time, please do not refresh
 | 
			
		||||
          the page during this period.
 | 
			
		||||
        second: >-
 | 
			
		||||
          2. During the restore process, although the existing data will not be
 | 
			
		||||
          cleaned up, if there is a conflict, the data will be overwritten.
 | 
			
		||||
        second: 2. Before performing the restore, all existing data will be cleared. Please ensure that there is no data that needs to be retained.
 | 
			
		||||
        third: >-
 | 
			
		||||
          3. After the restore is completed, you need to restart Halo to load
 | 
			
		||||
          the system resources normally.
 | 
			
		||||
        complete: Restore completed, waiting for restart...
 | 
			
		||||
      start: Start restore
 | 
			
		||||
      start: I have read the above instructions, start restore
 | 
			
		||||
      tabs:
 | 
			
		||||
        local:
 | 
			
		||||
          label: Upload
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1302,10 +1302,10 @@ core:
 | 
			
		|||
    restore:
 | 
			
		||||
      tips:
 | 
			
		||||
        first: 1. 恢复过程可能会持续较长时间,期间请勿刷新页面。
 | 
			
		||||
        second: 2. 在恢复的过程中,虽然已有的数据不会被清理掉,但如果有冲突的数据将被覆盖。
 | 
			
		||||
        second: 2. 在执行恢复之前,会清空现有的所有数据,请确保当前没有需要保留的数据。
 | 
			
		||||
        third: 3. 恢复完成之后需要重启 Halo 才能够正常加载系统资源。
 | 
			
		||||
        complete: 恢复完成,等待重启...
 | 
			
		||||
      start: 开始恢复
 | 
			
		||||
      start: 我已阅读上方提示,开始恢复
 | 
			
		||||
      tabs:
 | 
			
		||||
        local:
 | 
			
		||||
          label: 上传
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1281,10 +1281,10 @@ core:
 | 
			
		|||
    restore:
 | 
			
		||||
      tips:
 | 
			
		||||
        first: 1. 還原過程可能需要較長時間,期間請勿重新整理頁面。
 | 
			
		||||
        second: 2. 在還原過程中,雖然已有的資料不會被清除,但若有衝突的資料將被覆蓋。
 | 
			
		||||
        second: 2. 在執行恢復之前,會清空現有的所有數據,請確保當前沒有需要保留的數據。
 | 
			
		||||
        third: 3. 還原完成後需要重新啟動 Halo 才能正常載入系統資源。
 | 
			
		||||
        complete: 還原完成,等待重啟...
 | 
			
		||||
      start: 開始還原
 | 
			
		||||
      start: 我已閱讀上方提示,開始恢復
 | 
			
		||||
      tabs:
 | 
			
		||||
        local:
 | 
			
		||||
          label: 上傳
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue