Skip to content

Commit c2588e1

Browse files
authored
br: error out if pitr with table filter has exchange partition in/out filter range (#59683)
close #59723
1 parent b31b12d commit c2588e1

File tree

5 files changed

+780
-8
lines changed

5 files changed

+780
-8
lines changed

br/pkg/restore/log_client/batch_meta_processor.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@ func (mp *MetaKVInfoProcessor) ProcessBatch(
206206

207207
// add to table rename history
208208
mp.tableHistoryManager.AddTableHistory(tableInfo.ID, tableInfo.Name.String(), dbID)
209+
210+
// track partitions if this is a partitioned table
211+
if tableInfo.Partition != nil {
212+
for _, def := range tableInfo.Partition.Definitions {
213+
mp.tableHistoryManager.AddPartitionHistory(def.ID, tableInfo.Name.String(), dbID, tableInfo.ID)
214+
}
215+
}
209216
}
210217
}
211218
}

br/pkg/stream/table_history.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ package stream
1616

1717
// TableLocationInfo stores the table name, db id, and parent table id if is a partition
1818
type TableLocationInfo struct {
19-
DbID int64
20-
TableName string
19+
DbID int64
20+
TableName string
21+
IsPartition bool
22+
ParentTableID int64 // only meaningful when IsPartition is true
2123
}
2224

2325
type LogBackupTableHistoryManager struct {
@@ -36,12 +38,26 @@ func NewTableHistoryManager() *LogBackupTableHistoryManager {
3638
// AddTableHistory adds or updates history for a regular table
3739
func (info *LogBackupTableHistoryManager) AddTableHistory(tableId int64, tableName string, dbID int64) {
3840
locationInfo := TableLocationInfo{
39-
DbID: dbID,
40-
TableName: tableName,
41+
DbID: dbID,
42+
TableName: tableName,
43+
IsPartition: false,
44+
ParentTableID: 0,
4145
}
4246
info.addHistory(tableId, locationInfo)
4347
}
4448

49+
// AddPartitionHistory adds or updates history for a partition
50+
func (info *LogBackupTableHistoryManager) AddPartitionHistory(partitionID int64, tableName string,
51+
dbID int64, parentTableID int64) {
52+
locationInfo := TableLocationInfo{
53+
DbID: dbID,
54+
TableName: tableName,
55+
IsPartition: true,
56+
ParentTableID: parentTableID,
57+
}
58+
info.addHistory(partitionID, locationInfo)
59+
}
60+
4561
// addHistory is a helper method to maintain the history
4662
func (info *LogBackupTableHistoryManager) addHistory(id int64, locationInfo TableLocationInfo) {
4763
existing, exists := info.tableNameHistory[id]

br/pkg/task/restore.go

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,11 @@ func handleTableRenames(
14391439
start := dbIDAndTableName[0]
14401440
end := dbIDAndTableName[1]
14411441

1442+
// skip if it contains partition
1443+
if start.IsPartition || end.IsPartition {
1444+
continue
1445+
}
1446+
14421447
startDBName, exists := getDBNameFromIDInBackup(start.DbID, snapshotDBMap, history)
14431448
if !exists {
14441449
continue
@@ -1488,6 +1493,84 @@ func handleTableRenames(
14881493
}
14891494
}
14901495

1496+
// shouldRestoreTable checks if a table or partition is being tracked for restore
1497+
func shouldRestoreTable(
1498+
dbID int64,
1499+
tableName string,
1500+
isPartition bool,
1501+
parentTableID int64,
1502+
snapshotDBMap map[int64]*metautil.Database,
1503+
history *stream.LogBackupTableHistoryManager,
1504+
cfg *RestoreConfig,
1505+
) bool {
1506+
if isPartition {
1507+
return cfg.PiTRTableTracker.ContainsTableId(dbID, parentTableID)
1508+
}
1509+
dbName, exists := getDBNameFromIDInBackup(dbID, snapshotDBMap, history)
1510+
if !exists {
1511+
return false
1512+
}
1513+
return utils.MatchTable(cfg.TableFilter, dbName, tableName, cfg.WithSysTable)
1514+
}
1515+
1516+
// handlePartitionExchanges checks for partition exchanges and returns an error if a partition
1517+
// was exchanged between tables where one is in the filter and one is not
1518+
func handlePartitionExchanges(
1519+
history *stream.LogBackupTableHistoryManager,
1520+
snapshotDBMap map[int64]*metautil.Database,
1521+
cfg *RestoreConfig,
1522+
) error {
1523+
for tableId, dbIDAndTableName := range history.GetTableHistory() {
1524+
start := dbIDAndTableName[0]
1525+
end := dbIDAndTableName[1]
1526+
1527+
// skip if both are not partition
1528+
if !start.IsPartition && !end.IsPartition {
1529+
continue
1530+
}
1531+
1532+
// skip if parent table id are the same (if it's a table, parent table id will be 0)
1533+
if start.ParentTableID == end.ParentTableID {
1534+
continue
1535+
}
1536+
1537+
restoreStart := shouldRestoreTable(start.DbID, start.TableName, start.IsPartition, start.ParentTableID,
1538+
snapshotDBMap, history, cfg)
1539+
restoreEnd := shouldRestoreTable(end.DbID, end.TableName, end.IsPartition, end.ParentTableID,
1540+
snapshotDBMap, history, cfg)
1541+
1542+
// error out if partition is exchanged between tables where one should restore and one shouldn't
1543+
if restoreStart != restoreEnd {
1544+
startDBName, exists := getDBNameFromIDInBackup(start.DbID, snapshotDBMap, history)
1545+
if !exists {
1546+
startDBName = fmt.Sprintf("(unknown db name %d)", start.DbID)
1547+
}
1548+
endDBName, exists := getDBNameFromIDInBackup(end.DbID, snapshotDBMap, history)
1549+
if !exists {
1550+
endDBName = fmt.Sprintf("(unknown db name %d)", end.DbID)
1551+
}
1552+
1553+
return errors.Annotatef(berrors.ErrRestoreModeMismatch,
1554+
"partition exchange detected: partition ID %d was exchanged from table '%s.%s' (ID: %d) "+
1555+
"eventually to table '%s.%s' (ID: %d), which is not supported in table filter",
1556+
tableId, startDBName, start.TableName, start.ParentTableID,
1557+
endDBName, end.TableName, end.ParentTableID)
1558+
}
1559+
1560+
// if we reach here, it will only be both are restore or not restore,
1561+
// if it's table, need to add to table tracker, this is for table created during log backup.
1562+
// if it's table and exist in snapshot, the actual table and files should already been added
1563+
// since matches filter.
1564+
if restoreStart && !start.IsPartition {
1565+
cfg.PiTRTableTracker.TrackTableId(start.DbID, tableId)
1566+
}
1567+
if restoreEnd && !end.IsPartition {
1568+
cfg.PiTRTableTracker.TrackTableId(end.DbID, tableId)
1569+
}
1570+
}
1571+
return nil
1572+
}
1573+
14911574
func AdjustTablesToRestoreAndCreateTableTracker(
14921575
logBackupTableHistory *stream.LogBackupTableHistoryManager,
14931576
cfg *RestoreConfig,
@@ -1498,6 +1581,7 @@ func AdjustTablesToRestoreAndCreateTableTracker(
14981581
) (err error) {
14991582
// build tracker for pitr restore to use later
15001583
piTRIdTracker := utils.NewPiTRIdTracker()
1584+
cfg.PiTRTableTracker = piTRIdTracker
15011585

15021586
// track newly created databases
15031587
newlyCreatedDBs := logBackupTableHistory.GetNewlyCreatedDBHistory()
@@ -1510,15 +1594,17 @@ func AdjustTablesToRestoreAndCreateTableTracker(
15101594
// first handle table renames to determine which tables we need
15111595
handleTableRenames(logBackupTableHistory, snapshotDBMap, cfg, tableMap, dbMap, fileMap, piTRIdTracker)
15121596

1513-
// handle partition exchange if needed in future
1597+
// handle partition exchange after all tables are tracked
1598+
if err := handlePartitionExchanges(logBackupTableHistory, snapshotDBMap, cfg); err != nil {
1599+
return err
1600+
}
15141601

15151602
// track all snapshot tables that's going to restore in PiTR tracker
15161603
for tableID, table := range tableMap {
15171604
piTRIdTracker.TrackTableId(table.DB.ID, tableID)
15181605
}
15191606

15201607
log.Info("pitr table tracker", zap.String("map", piTRIdTracker.String()))
1521-
cfg.PiTRTableTracker = piTRIdTracker
15221608
return nil
15231609
}
15241610

br/pkg/task/stream.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,7 +1981,10 @@ func buildPauseSafePointName(taskName string) string {
19811981
return fmt.Sprintf("%s_pause_safepoint", taskName)
19821982
}
19831983

1984-
func checkPiTRRequirements(mgr *conn.Mgr) error {
1984+
func checkPiTRRequirements(mgr *conn.Mgr, hasExplicitFilter bool) error {
1985+
if hasExplicitFilter {
1986+
return nil
1987+
}
19851988
return restore.AssertUserDBsEmpty(mgr.GetDomain())
19861989
}
19871990

@@ -2057,7 +2060,7 @@ func generatePiTRTaskInfo(
20572060
// Only when use checkpoint and not the first execution,
20582061
// skip checking requirements.
20592062
log.Info("check pitr requirements for the first execution")
2060-
if err := checkPiTRRequirements(mgr); err != nil {
2063+
if err := checkPiTRRequirements(mgr, cfg.ExplicitFilter); err != nil {
20612064
// delay cluster checks after we get the backupmeta.
20622065
// for the case that the restore inc + log backup,
20632066
// we can still restore them.

0 commit comments

Comments
 (0)