|
19 | 19 | import com.google.common.collect.Lists; |
20 | 20 | import com.google.common.collect.Maps; |
21 | 21 | import com.google.common.collect.Sets; |
| 22 | +import com.google.gson.Gson; |
| 23 | +import com.google.gson.annotations.SerializedName; |
| 24 | +import com.starrocks.catalog.Column; |
22 | 25 | import com.starrocks.catalog.Database; |
23 | 26 | import com.starrocks.catalog.MaterializedIndex; |
24 | 27 | import com.starrocks.catalog.MaterializedIndex.IndexExtState; |
|
43 | 46 | import com.starrocks.proto.TabletMetadataPB; |
44 | 47 | import com.starrocks.proto.TabletMetadataRepairStatus; |
45 | 48 | import com.starrocks.proto.TabletResult; |
| 49 | +import com.starrocks.qe.ShowResultSetMetaData; |
46 | 50 | import com.starrocks.rpc.BrpcProxy; |
47 | 51 | import com.starrocks.rpc.LakeService; |
48 | 52 | import com.starrocks.rpc.RpcException; |
49 | 53 | import com.starrocks.server.GlobalStateMgr; |
50 | 54 | import com.starrocks.sql.ast.AdminRepairTableStmt; |
51 | 55 | import com.starrocks.system.ComputeNode; |
52 | 56 | import com.starrocks.thrift.TStatusCode; |
| 57 | +import com.starrocks.type.TypeFactory; |
53 | 58 | import com.starrocks.warehouse.cngroup.ComputeResource; |
54 | 59 | import org.apache.logging.log4j.LogManager; |
55 | 60 | import org.apache.logging.log4j.Logger; |
@@ -69,6 +74,18 @@ public class TabletRepairHelper { |
69 | 74 | private static final long BATCH_VERSION_NUM = 5L; |
70 | 75 | private static final String SST_FILE_SUFFIX = ".sst"; |
71 | 76 |
|
| 77 | + private static ShowResultSetMetaData DRY_RUN_REPAIR_RESULT_META_DATA; |
| 78 | + |
| 79 | + static { |
| 80 | + ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); |
| 81 | + builder.addColumn(new Column("PartitionId", TypeFactory.createVarcharType(20))); |
| 82 | + builder.addColumn(new Column("VisibleVersion", TypeFactory.createVarcharType(20))); |
| 83 | + builder.addColumn(new Column("RepairStatus", TypeFactory.createVarcharType(60))); |
| 84 | + builder.addColumn(new Column("TabletRecoverInfo", TypeFactory.createVarcharType(65535))); |
| 85 | + builder.addColumn(new Column("ErrorMsg", TypeFactory.createVarcharType(65535))); |
| 86 | + DRY_RUN_REPAIR_RESULT_META_DATA = builder.build(); |
| 87 | + } |
| 88 | + |
72 | 89 | // the version range [minVersion, maxVersion] is used to find valid tablet metadatas, both are included |
73 | 90 | record PhysicalPartitionInfo( |
74 | 91 | long physicalPartitionId, |
@@ -585,24 +602,8 @@ private static Map<Long, String> repairTabletMetadata(PhysicalPartitionInfo info |
585 | 602 | return tabletErrors; |
586 | 603 | } |
587 | 604 |
|
588 | | - /** |
589 | | - * Repairs the tablet metadata for a single physical partition. |
590 | | - * This function attempts to find valid tablet metadata for all tablets within the given physical partition. |
591 | | - * It iterates through versions to retrieve tablet metadatas from compute nodes by batches. |
592 | | - * If valid metadata is found for all tablets, or |
593 | | - * if `allowEmptyTabletRecovery` is true and empty metadata can be created for missing tablets, |
594 | | - * it then sends these valid metadatas to the compute nodes to perform the repair operation. |
595 | | - * |
596 | | - * @param info Information about the physical partition. |
597 | | - * @param enforceConsistentVersion Whether to enforce consistent metadata version across all tablets. |
598 | | - * @param allowEmptyTabletRecovery Whether to allow creating empty metadata for tablets without valid metadata. |
599 | | - * @param isFileBundling Whether file bundling is enabled for the table. |
600 | | - * @return A map of tablet IDs to error messages for any tablets that failed to repair. |
601 | | - * @throws Exception If an error occurs during the repair process. |
602 | | - */ |
603 | | - private static Map<Long, String> repairPhysicalPartition(PhysicalPartitionInfo info, boolean enforceConsistentVersion, |
604 | | - boolean allowEmptyTabletRecovery, boolean isFileBundling) |
605 | | - throws Exception { |
| 605 | + private static Map<Long, TabletMetadataPB> getValidTabletMetadatas(PhysicalPartitionInfo info, |
| 606 | + boolean enforceConsistentVersion) throws Exception { |
606 | 607 | List<Long> allTablets = info.allTablets; |
607 | 608 | Set<Long> unverifiedTablets = info.unverifiedTablets; |
608 | 609 | long partitionMaxVersion = info.maxVersion; |
@@ -626,6 +627,28 @@ private static Map<Long, String> repairPhysicalPartition(PhysicalPartitionInfo i |
626 | 627 | break; |
627 | 628 | } |
628 | 629 | } |
| 630 | + return tabletToValidMetadata; |
| 631 | + } |
| 632 | + |
| 633 | + /** |
| 634 | + * Repairs the tablet metadata for a single physical partition. |
| 635 | + * This function attempts to find valid tablet metadata for all tablets within the given physical partition. |
| 636 | + * It iterates through versions to retrieve tablet metadatas from compute nodes by batches. |
| 637 | + * If valid metadata is found for all tablets, or |
| 638 | + * if `allowEmptyTabletRecovery` is true and empty metadata can be created for missing tablets, |
| 639 | + * it then sends these valid metadatas to the compute nodes to perform the repair operation. |
| 640 | + * |
| 641 | + * @param info Information about the physical partition. |
| 642 | + * @param enforceConsistentVersion Whether to enforce consistent metadata version across all tablets. |
| 643 | + * @param allowEmptyTabletRecovery Whether to allow creating empty metadata for tablets without valid metadata. |
| 644 | + * @param isFileBundling Whether file bundling is enabled for the table. |
| 645 | + * @return A map of tablet IDs to error messages for any tablets that failed to repair. |
| 646 | + * @throws Exception If an error occurs during the repair process. |
| 647 | + */ |
| 648 | + private static Map<Long, String> repairPhysicalPartition(PhysicalPartitionInfo info, boolean enforceConsistentVersion, |
| 649 | + boolean allowEmptyTabletRecovery, boolean isFileBundling) |
| 650 | + throws Exception { |
| 651 | + Map<Long, TabletMetadataPB> tabletToValidMetadata = getValidTabletMetadatas(info, enforceConsistentVersion); |
629 | 652 |
|
630 | 653 | // check the valid tablet metadata, and create empty tablet metadata if no valid metadata is found |
631 | 654 | checkOrCreateEmptyTabletMetadata(info, tabletToValidMetadata, enforceConsistentVersion, allowEmptyTabletRecovery); |
@@ -706,4 +729,88 @@ public static void repair(AdminRepairTableStmt stmt, Database db, OlapTable tabl |
706 | 729 | errorMsgsSize, errorMsgsSize > 1 ? "s" : "", Joiner.on(", ").join(errorMsgs))); |
707 | 730 | } |
708 | 731 | } |
| 732 | + |
| 733 | + public static ShowResultSetMetaData getDryRunRepairResultMetaData() { |
| 734 | + return DRY_RUN_REPAIR_RESULT_META_DATA; |
| 735 | + } |
| 736 | + |
| 737 | + private static class TabletRecoverInfo { |
| 738 | + @SerializedName("tabletId") |
| 739 | + public long tabletId; |
| 740 | + @SerializedName("recoverVersion") |
| 741 | + public long recoverVersion; |
| 742 | + |
| 743 | + public TabletRecoverInfo(long tabletId, long recoverVersion) { |
| 744 | + this.tabletId = tabletId; |
| 745 | + this.recoverVersion = recoverVersion; |
| 746 | + } |
| 747 | + } |
| 748 | + |
| 749 | + enum RepairStatus { |
| 750 | + NORMAL, // All tablets meta and data files are normal |
| 751 | + UNKNOWN, // Dry run repair exception |
| 752 | + RECOVERABLE, // Tablet has missing meta or data files, and is recoverable |
| 753 | + UNRECOVERABLE // Tablet has missing meta or data files, and is unrecoverable |
| 754 | + } |
| 755 | + |
| 756 | + /** |
| 757 | + * Return the repair plan without executing it |
| 758 | + */ |
| 759 | + public static List<List<String>> dryRunRepair(AdminRepairTableStmt stmt, Database db, OlapTable table, |
| 760 | + List<String> partitionNames, ComputeResource computeResource) |
| 761 | + throws StarRocksException { |
| 762 | + boolean enforceConsistentVersion = stmt.isEnforceConsistentVersion(); |
| 763 | + boolean allowEmptyTabletRecovery = stmt.isAllowEmptyTabletRecovery(); |
| 764 | + Gson gson = new Gson(); |
| 765 | + |
| 766 | + // get physical partition ids in db table read lock |
| 767 | + List<Long> physicalPartitionIds = getPhysicalPartitionIds(db, table, partitionNames); |
| 768 | + |
| 769 | + List<List<String>> result = Lists.newArrayList(); |
| 770 | + for (Long physicalPartitionId : physicalPartitionIds) { |
| 771 | + long visibleVersion = -1; |
| 772 | + RepairStatus repairStatus = RepairStatus.UNKNOWN; |
| 773 | + List<TabletRecoverInfo> tabletRecoverInfos = Lists.newArrayList(); |
| 774 | + String errorMsg = ""; |
| 775 | + |
| 776 | + try { |
| 777 | + PhysicalPartitionInfo info = |
| 778 | + getPhysicalPartitionInfo(db, table, physicalPartitionId, enforceConsistentVersion, computeResource); |
| 779 | + visibleVersion = info.maxVersion; |
| 780 | + |
| 781 | + Map<Long, TabletMetadataPB> validMetadatas = getValidTabletMetadatas(info, enforceConsistentVersion); |
| 782 | + |
| 783 | + // check the valid tablet metadata, and create empty tablet metadata if no valid metadata is found |
| 784 | + checkOrCreateEmptyTabletMetadata(info, validMetadatas, enforceConsistentVersion, allowEmptyTabletRecovery); |
| 785 | + |
| 786 | + // physical partition is recoverable |
| 787 | + repairStatus = RepairStatus.RECOVERABLE; |
| 788 | + errorMsg = ""; |
| 789 | + for (Long tabletId : info.allTablets) { |
| 790 | + TabletMetadataPB metadata = validMetadatas.get(tabletId); |
| 791 | + Preconditions.checkNotNull(metadata); |
| 792 | + long recoverVersion = metadata.version; |
| 793 | + tabletRecoverInfos.add(new TabletRecoverInfo(tabletId, recoverVersion)); |
| 794 | + } |
| 795 | + } catch (AlreadyExistsException e) { |
| 796 | + repairStatus = RepairStatus.NORMAL; |
| 797 | + tabletRecoverInfos.clear(); |
| 798 | + errorMsg = ""; |
| 799 | + } catch (StarRocksException e) { |
| 800 | + repairStatus = RepairStatus.UNRECOVERABLE; |
| 801 | + tabletRecoverInfos.clear(); |
| 802 | + errorMsg = e.getMessage(); |
| 803 | + } catch (Exception e) { |
| 804 | + LOG.warn("Fail to dry run repair tablet metadata for partition {}", physicalPartitionId, e); |
| 805 | + repairStatus = RepairStatus.UNKNOWN; |
| 806 | + tabletRecoverInfos.clear(); |
| 807 | + errorMsg = String.format("ERROR: %s", e.getMessage()); |
| 808 | + } |
| 809 | + |
| 810 | + result.add(Lists.newArrayList(String.valueOf(physicalPartitionId), String.valueOf(visibleVersion), |
| 811 | + repairStatus.name(), gson.toJson(tabletRecoverInfos), errorMsg)); |
| 812 | + } |
| 813 | + |
| 814 | + return result; |
| 815 | + } |
709 | 816 | } |