-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
br: migrate pitr id map to the system table mysql.tidb_pitr_id_map
#55871
Changes from 10 commits
ea63cf4
3764be3
69fba50
70c2341
5557409
260cad6
e0ae93c
2f35f29
9697e48
b115f48
5bdca09
5e80aa8
f4d5dda
b9d858e
d605dd8
b095fdb
c6f6d7f
2cf1bd5
c82be88
fedd8e4
8f39023
f78334d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ import ( | |
"time" | ||
|
||
"github.com/fatih/color" | ||
"github.com/gogo/protobuf/proto" | ||
"github.com/opentracing/opentracing-go" | ||
"github.com/pingcap/errors" | ||
"github.com/pingcap/failpoint" | ||
|
@@ -95,6 +96,8 @@ type LogClient struct { | |
// Can not use `restoreTS` directly, because schema created in `full backup` maybe is new than `restoreTS`. | ||
currentTS uint64 | ||
|
||
upstreamClusterID uint64 | ||
|
||
*LogFileManager | ||
|
||
workerPool *tidbutil.WorkerPool | ||
|
@@ -167,6 +170,11 @@ func (rc *LogClient) SetConcurrency(c uint) { | |
rc.workerPool = tidbutil.NewWorkerPool(c, "file") | ||
} | ||
|
||
func (rc *LogClient) SetUpstreamClusterID(upstreamClusterID uint64) { | ||
log.Info("upstream cluster id", zap.Uint64("cluster id", upstreamClusterID)) | ||
rc.upstreamClusterID = upstreamClusterID | ||
} | ||
|
||
func (rc *LogClient) SetStorage(ctx context.Context, backend *backuppb.StorageBackend, opts *storage.ExternalStorageOptions) error { | ||
var err error | ||
rc.storage, err = storage.New(ctx, backend, opts) | ||
|
@@ -558,24 +566,38 @@ func (rc *LogClient) RestoreKVFiles( | |
|
||
func (rc *LogClient) initSchemasMap( | ||
ctx context.Context, | ||
clusterID uint64, | ||
restoreTS uint64, | ||
) ([]*backuppb.PitrDBMap, error) { | ||
filename := metautil.PitrIDMapsFilename(clusterID, restoreTS) | ||
exist, err := rc.storage.FileExists(ctx, filename) | ||
if err != nil { | ||
return nil, errors.Annotatef(err, "failed to check filename:%s ", filename) | ||
} else if !exist { | ||
log.Info("pitr id maps isn't existed", zap.String("file", filename)) | ||
getPitrIDMapSQL := "SELECT segment_id, id_map FROM mysql.tidb_pitr_id_map WHERE restored_ts = %? and upstream_cluster_id = %? ORDER BY segment_id;" | ||
execCtx := rc.se.GetSessionCtx().GetRestrictedSQLExecutor() | ||
rows, _, errSQL := execCtx.ExecRestrictedSQL( | ||
kv.WithInternalSourceType(ctx, kv.InternalTxnBR), | ||
nil, | ||
getPitrIDMapSQL, | ||
restoreTS, | ||
rc.upstreamClusterID, | ||
) | ||
if errSQL != nil { | ||
return nil, errors.Annotatef(errSQL, "failed to get pitr id map from mysql.tidb_pitr_id_map") | ||
} | ||
if len(rows) == 0 { | ||
log.Info("pitr id map does not exist", zap.Uint64("restored ts", restoreTS)) | ||
return nil, nil | ||
} | ||
|
||
metaData, err := rc.storage.ReadFile(ctx, filename) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
metaData := make([]byte, 0, len(rows)*PITRIdMapBlockSize) | ||
for i, row := range rows { | ||
elementID := row.GetUint64(0) | ||
if uint64(i) != elementID { | ||
return nil, errors.Errorf("the part(segment_id = %d) of pitr id map is lost", i) | ||
} | ||
d := row.GetBytes(1) | ||
if len(d) == 0 { | ||
return nil, errors.Errorf("get the empty part(segment_id = %d) of pitr id map", i) | ||
} | ||
metaData = append(metaData, d...) | ||
} | ||
backupMeta := &backuppb.BackupMeta{} | ||
if err = backupMeta.Unmarshal(metaData); err != nil { | ||
if err := backupMeta.Unmarshal(metaData); err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
|
||
|
@@ -722,7 +744,7 @@ func (rc *LogClient) InitSchemasReplaceForDDL( | |
if !cfg.IsNewTask { | ||
log.Info("try to load pitr id maps") | ||
needConstructIdMap = false | ||
dbMaps, err = rc.initSchemasMap(ctx, rc.GetClusterID(ctx), rc.restoreTS) | ||
dbMaps, err = rc.initSchemasMap(ctx, rc.restoreTS) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
|
@@ -733,7 +755,7 @@ func (rc *LogClient) InitSchemasReplaceForDDL( | |
if len(dbMaps) <= 0 && cfg.FullBackupStorage == nil { | ||
log.Info("try to load pitr id maps of the previous task", zap.Uint64("start-ts", rc.startTS)) | ||
needConstructIdMap = true | ||
dbMaps, err = rc.initSchemasMap(ctx, rc.GetClusterID(ctx), rc.startTS) | ||
dbMaps, err = rc.initSchemasMap(ctx, rc.startTS) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
|
@@ -887,7 +909,7 @@ func (rc *LogClient) PreConstructAndSaveIDMap( | |
return errors.Trace(err) | ||
} | ||
|
||
if err := rc.SaveIDMap(ctx, sr); err != nil { | ||
if err := rc.saveIDMap(ctx, sr); err != nil { | ||
return errors.Trace(err) | ||
} | ||
return nil | ||
|
@@ -1491,24 +1513,36 @@ func (rc *LogClient) GetGCRows() []*stream.PreDelRangeQuery { | |
return rc.deleteRangeQuery | ||
} | ||
|
||
// SaveIDMap saves the id mapping information. | ||
func (rc *LogClient) SaveIDMap( | ||
const PITRIdMapBlockSize int = 524288 | ||
|
||
// saveIDMap saves the id mapping information. | ||
func (rc *LogClient) saveIDMap( | ||
ctx context.Context, | ||
sr *stream.SchemasReplace, | ||
) error { | ||
idMaps := sr.TidySchemaMaps() | ||
clusterID := rc.GetClusterID(ctx) | ||
metaFileName := metautil.PitrIDMapsFilename(clusterID, rc.restoreTS) | ||
metaWriter := metautil.NewMetaWriter(rc.storage, metautil.MetaFileSize, false, metaFileName, nil) | ||
metaWriter.Update(func(m *backuppb.BackupMeta) { | ||
// save log startTS to backupmeta file | ||
m.ClusterId = clusterID | ||
m.DbMaps = idMaps | ||
}) | ||
|
||
if err := metaWriter.FlushBackupMeta(ctx); err != nil { | ||
backupmeta := &backuppb.BackupMeta{DbMaps: sr.TidySchemaMaps()} | ||
data, err := proto.Marshal(backupmeta) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
// clean the dirty id map at first | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it safe? if process exits abnormally after deleting finished. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have the processes to persist id map:
In the external storage, step 2.a and 2.b is replaced with uploading files with override mode. Actually, the atomic is not necessary. That's because there must be failed at the step 2.b or 2.c, which means the pitr id map is incomplete. But just do the step 2.a in the next execution to delete the incomplete pitr id map. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on id map at |
||
err = rc.se.ExecuteInternal(ctx, "DELETE FROM mysql.tidb_pitr_id_map WHERE restored_ts = %? and upstream_cluster_id = %?;", rc.restoreTS, rc.upstreamClusterID) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
replacePitrIDMapSQL := "REPLACE INTO mysql.tidb_pitr_id_map (restored_ts, upstream_cluster_id, segment_id, id_map) VALUES (%?, %?, %?, %?);" | ||
for startIdx, segmentId := 0, 0; startIdx < len(data); segmentId += 1 { | ||
endIdx := startIdx + PITRIdMapBlockSize | ||
if endIdx > len(data) { | ||
endIdx = len(data) | ||
} | ||
err := rc.se.ExecuteInternal(ctx, replacePitrIDMapSQL, rc.restoreTS, rc.upstreamClusterID, segmentId, data[startIdx:endIdx]) | ||
if err != nil { | ||
return errors.Trace(err) | ||
} | ||
startIdx = endIdx | ||
} | ||
|
||
if rc.useCheckpoint { | ||
var items map[int64]model.TiFlashReplicaInfo | ||
if sr.TiflashRecorder != nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
restoreTS
maybe confusing, because sometimes we usestart-ts
to build pitr map. esspecially in L584.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current log restore's
start-ts
is the last log restore'srestored-ts
, so BR gets the pitr id map atstart-ts
of the current log restore is actually to get the pitr id map atrestored-ts
of the last log restore.