From 8e433a8e76514001a59bb9a174c8fe965c36a37c Mon Sep 17 00:00:00 2001 From: srlch Date: Fri, 25 Oct 2024 10:28:06 +0800 Subject: [PATCH] [BugFix] Fix several problem for logical view restore This pr several problem: 1. analyze exception will be throw if we specify view name after ON clause. 2. can not set alias for logical view when doing restore 3. can not choice part of view to be restored, we must restored all view, this pr also fix this problem. 4. Forget to remove metadata for View if restore is failed. 5. do not add restore view again when replay log for restorejob. 6. pure snapshot analyze will be failed Signed-off-by: srlch --- .../com/starrocks/backup/BackupHandler.java | 31 ++++++++++++++++--- .../com/starrocks/backup/BackupJobInfo.java | 10 ++++++ .../java/com/starrocks/backup/RestoreJob.java | 23 +++++++++----- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/fe/fe-core/src/main/java/com/starrocks/backup/BackupHandler.java b/fe/fe-core/src/main/java/com/starrocks/backup/BackupHandler.java index e435e88b0f6e9..c00803e5e9d1b 100644 --- a/fe/fe-core/src/main/java/com/starrocks/backup/BackupHandler.java +++ b/fe/fe-core/src/main/java/com/starrocks/backup/BackupHandler.java @@ -51,6 +51,7 @@ import com.starrocks.catalog.OlapTable; import com.starrocks.catalog.Partition; import com.starrocks.catalog.Table; +import com.starrocks.catalog.View; import com.starrocks.common.Config; import com.starrocks.common.DdlException; import com.starrocks.common.ErrorCode; @@ -98,6 +99,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import static com.starrocks.scheduler.MVActiveChecker.MV_BACKUP_INACTIVE_REASON; @@ -437,14 +439,21 @@ private void restore(Repository repository, Database db, RestoreStmt stmt) throw // Also remove all unrelated objs Preconditions.checkState(infos.size() == 1); BackupJobInfo jobInfo = infos.get(0); + + BackupMeta backupMeta = downloadAndDeserializeMetaInfo(jobInfo, repository, stmt); + // If TableRefs is empty, it means that we do not specify any table in Restore stmt. // So, we should restore all table in current database. - if (stmt.getTableRefs().size() != 0) { - checkAndFilterRestoreObjsExistInSnapshot(jobInfo, stmt.getTableRefs()); + List restoredViews = Lists.newArrayList(); + if (backupMeta != null) { + if (stmt.getTableRefs().size() != 0) { + checkAndFilterRestoreObjsExistInSnapshot(jobInfo, stmt.getTableRefs(), backupMeta, restoredViews); + } else { + restoredViews = backupMeta.getTables().values().stream().filter(Table::isOlapView) + .map(x -> (View) x).collect(Collectors.toList()); + } } - BackupMeta backupMeta = downloadAndDeserializeMetaInfo(jobInfo, repository, stmt); - // Create a restore job RestoreJob restoreJob = null; if (backupMeta != null) { @@ -461,6 +470,7 @@ private void restore(Repository repository, Database db, RestoreStmt stmt) throw restoreJob = new RestoreJob(stmt.getLabel(), stmt.getBackupTimestamp(), db.getId(), db.getOriginName(), jobInfo, stmt.allowLoad(), stmt.getReplicationNum(), stmt.getTimeoutMs(), globalStateMgr, repository.getId(), backupMeta, mvRestoreContext); + restoreJob.setRestoredViews(restoredViews); globalStateMgr.getEditLog().logRestoreJob(restoreJob); // must put to dbIdToBackupOrRestoreJob after edit log, otherwise the state of job may be changed. @@ -488,11 +498,22 @@ private BackupMeta downloadAndDeserializeMetaInfo(BackupJobInfo jobInfo, Reposit return backupMetas.get(0); } - private void checkAndFilterRestoreObjsExistInSnapshot(BackupJobInfo jobInfo, List tblRefs) + private void checkAndFilterRestoreObjsExistInSnapshot(BackupJobInfo jobInfo, List tblRefs, BackupMeta backupMeta, + List restoredViews) throws DdlException { Set allTbls = Sets.newHashSet(); for (TableRef tblRef : tblRefs) { String tblName = tblRef.getName().getTbl(); + Table tbl = backupMeta.getTable(tblName); + if (tbl != null && tbl.isOlapView()) { + if (tblRef.hasExplicitAlias()) { + // simple reset alias for view in backupMeta to be restored. + tbl.setName(tblRef.getExplicitAlias()); + } + restoredViews.add((View) tbl); + continue; + } + if (!jobInfo.containsTbl(tblName)) { ErrorReport.reportDdlException(ErrorCode.ERR_COMMON_ERROR, "Table " + tblName + " does not exist in snapshot " + jobInfo.name); diff --git a/fe/fe-core/src/main/java/com/starrocks/backup/BackupJobInfo.java b/fe/fe-core/src/main/java/com/starrocks/backup/BackupJobInfo.java index 431b8603a63b1..64598e3a9d714 100644 --- a/fe/fe-core/src/main/java/com/starrocks/backup/BackupJobInfo.java +++ b/fe/fe-core/src/main/java/com/starrocks/backup/BackupJobInfo.java @@ -439,6 +439,16 @@ private static void genFromJson(String json, BackupJobInfo jobInfo) { JSONObject backupObjs = root.getJSONObject("backup_objects"); String[] tblNames = JSONObject.getNames(backupObjs); + if (tblNames == null) { + // it is possible for snapshot without any OlapTable or MV + String result = root.getString("backup_result"); + if (result.equals("succeed")) { + jobInfo.success = true; + } else { + jobInfo.success = false; + } + return; + } for (String tblName : tblNames) { BackupTableInfo tblInfo = new BackupTableInfo(); tblInfo.name = tblName; diff --git a/fe/fe-core/src/main/java/com/starrocks/backup/RestoreJob.java b/fe/fe-core/src/main/java/com/starrocks/backup/RestoreJob.java index a91b415e5f2f3..ca632e00d4114 100644 --- a/fe/fe-core/src/main/java/com/starrocks/backup/RestoreJob.java +++ b/fe/fe-core/src/main/java/com/starrocks/backup/RestoreJob.java @@ -203,6 +203,9 @@ public enum RestoreJobState { private AgentBatchTask batchTask; boolean enableColocateRestore = Config.enable_colocate_restore; + + @SerializedName(value = "restoredViews") + private List restoredViews = Lists.newArrayList(); public RestoreJob() { super(JobType.RESTORE); @@ -789,9 +792,7 @@ private void checkAndPrepareMeta() { } // add all restored olap view into globalStateMgr - List restoredOlapViews = backupMeta.getTables().values().stream().filter(Table::isOlapView) - .map(x -> (View) x).collect(Collectors.toList()); - addRestoreOlapView(restoredOlapViews); + addRestoreOlapView(restoredViews); if (!status.ok()) { return; } @@ -1170,14 +1171,13 @@ private void replayCheckAndPrepareMeta() { modifyInvertedIndex((OlapTable) restoreTbl, restorePart); } } + + // restored view need not to be added again here, because + // another edit log created by createView will done. } finally { locker.unLockDatabase(db.getId(), LockType.WRITE); } - List restoredOlapViews = backupMeta.getTables().values().stream().filter(Table::isOlapView) - .map(x -> (View) x).collect(Collectors.toList()); - addRestoreOlapView(restoredOlapViews); - LOG.info("replay check and prepare meta. {}", this); } @@ -1622,6 +1622,10 @@ private void replayWaitingAllTabletsCommitted() { allTabletCommitted(true /* is replay */); } + public void setRestoredViews(List restoredViews) { + this.restoredViews = restoredViews; + } + public List getInfo() { List info = Lists.newArrayList(); info.add(String.valueOf(jobId)); @@ -1740,6 +1744,11 @@ public void cancelInternal(boolean isReplay) { restoreTbl.dropPartition(dbId, entry.second.getName(), true /* is restore */); } + + // remove restored View + for (View restoredView : restoredViews) { + db.dropTable(restoredView.getName()); + } } finally { locker.unLockDatabase(db.getId(), LockType.WRITE); }