mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 07:50:25 +08:00
!87 fix: 修复数据库备份与恢复问题
* feat: 修复数据库备份与恢复问题 * feat: 启用 BINLOG 支持全量备份和增量备份,未启用 BINLOG 仅支持全量备份 * feat: 数据库恢复后自动备份,避免数据丢失
This commit is contained in:
@@ -35,7 +35,13 @@
|
|||||||
clearable
|
clearable
|
||||||
class="w100"
|
class="w100"
|
||||||
>
|
>
|
||||||
<el-option v-for="item in state.histories" :key="item.id" :label="item.name" :value="item"> </el-option>
|
<el-option
|
||||||
|
v-for="item in state.histories"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name + (item.binlogFileName ? ' ' : ' 不') + '支持指定时间点恢复'"
|
||||||
|
:value="item"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="startTime" label="开始时间">
|
<el-form-item prop="startTime" label="开始时间">
|
||||||
@@ -83,20 +89,30 @@ const visible = defineModel<boolean>('visible', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const validatePointInTime = (rule: any, value: any, callback: any) => {
|
const validatePointInTime = (rule: any, value: any, callback: any) => {
|
||||||
if (!state.histories || state.histories.length == 0) {
|
|
||||||
callback(new Error('数据库没有备份记录'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const history = state.histories[state.histories.length - 1];
|
|
||||||
if (value < new Date(history.createTime)) {
|
|
||||||
callback(new Error('在此之前数据库没有备份记录'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value > new Date()) {
|
if (value > new Date()) {
|
||||||
callback(new Error('恢复时间点晚于当前时间'));
|
callback(new Error('恢复时间点晚于当前时间'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callback();
|
if (!state.histories || state.histories.length == 0) {
|
||||||
|
callback(new Error('数据库没有备份记录'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let last = null;
|
||||||
|
for (const history of state.histories) {
|
||||||
|
if (!history.binlogFileName || history.binlogFileName.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (new Date(history.createTime) < value) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last = history;
|
||||||
|
}
|
||||||
|
if (!last) {
|
||||||
|
callback(new Error('现有数据库备份不支持指定时间恢复'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(last.name + ' 之前的数据库备份不支持指定时间恢复');
|
||||||
};
|
};
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -110,7 +126,6 @@ const rules = {
|
|||||||
pointInTime: [
|
pointInTime: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
// message: '请选择恢复时间点',
|
|
||||||
validator: validatePointInTime,
|
validator: validatePointInTime,
|
||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,13 +46,3 @@ log:
|
|||||||
# file:
|
# file:
|
||||||
# path: ./
|
# path: ./
|
||||||
# name: mayfly-go.log
|
# name: mayfly-go.log
|
||||||
db:
|
|
||||||
backup-path: ./backup
|
|
||||||
mysqlutil-path:
|
|
||||||
mysql: ./mysqlutil/bin/mysql
|
|
||||||
mysqldump: ./mysqlutil/bin/mysqldump
|
|
||||||
mysqlbinlog: ./mysqlutil/bin/mysqlbinlog
|
|
||||||
mariadbutil-path:
|
|
||||||
mysql: ./mariadbutil/bin/mariadb
|
|
||||||
mysqldump: ./mariadbutil/bin/mariadb-dump
|
|
||||||
mysqlbinlog: ./mariadbutil/bin/mariadb-binlog
|
|
||||||
@@ -55,13 +55,13 @@ func (d *DbBackup) Create(rc *req.Ctx) {
|
|||||||
for _, dbName := range dbNames {
|
for _, dbName := range dbNames {
|
||||||
job := &entity.DbBackup{
|
job := &entity.DbBackup{
|
||||||
DbJobBaseImpl: entity.NewDbBJobBase(db.InstanceId, entity.DbJobTypeBackup),
|
DbJobBaseImpl: entity.NewDbBJobBase(db.InstanceId, entity.DbJobTypeBackup),
|
||||||
|
DbName: dbName,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Repeated: backupForm.Repeated,
|
Repeated: backupForm.Repeated,
|
||||||
StartTime: backupForm.StartTime,
|
StartTime: backupForm.StartTime,
|
||||||
Interval: backupForm.Interval,
|
Interval: backupForm.Interval,
|
||||||
Name: backupForm.Name,
|
Name: backupForm.Name,
|
||||||
}
|
}
|
||||||
job.DbName = dbName
|
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
}
|
}
|
||||||
biz.ErrIsNilAppendErr(d.DbBackupApp.Create(rc.MetaCtx, jobs), "添加数据库备份任务失败: %v")
|
biz.ErrIsNilAppendErr(d.DbBackupApp.Create(rc.MetaCtx, jobs), "添加数据库备份任务失败: %v")
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func (d *DbRestore) GetDbNamesWithoutRestore(rc *req.Ctx) {
|
|||||||
rc.ResData = dbNamesWithoutRestore
|
rc.ResData = dbNamesWithoutRestore
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库备份历史
|
// GetHistoryPageList 获取数据库备份历史
|
||||||
// @router /api/dbs/:dbId/restores/:restoreId/histories [GET]
|
// @router /api/dbs/:dbId/restores/:restoreId/histories [GET]
|
||||||
func (d *DbRestore) GetHistoryPageList(rc *req.Ctx) {
|
func (d *DbRestore) GetHistoryPageList(rc *req.Ctx) {
|
||||||
queryCond := &entity.DbRestoreHistoryQuery{
|
queryCond := &entity.DbRestoreHistoryQuery{
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ func (backup *DbBackup) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
// DbBackupHistory 数据库备份历史
|
// DbBackupHistory 数据库备份历史
|
||||||
type DbBackupHistory struct {
|
type DbBackupHistory struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
DbBackupId uint64 `json:"dbBackupId"`
|
DbBackupId uint64 `json:"dbBackupId"`
|
||||||
CreateTime time.Time `json:"createTime"`
|
CreateTime time.Time `json:"createTime"`
|
||||||
DbName string `json:"dbName"` // 数据库名称
|
DbName string `json:"dbName"` // 数据库名称
|
||||||
Name string `json:"name"` // 备份历史名称
|
Name string `json:"name"` // 备份历史名称
|
||||||
|
BinlogFileName string `json:"binlogFileName"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,6 +219,17 @@ func (s *dbScheduler) restoreMysql(ctx context.Context, job entity.DbJob) error
|
|||||||
}
|
}
|
||||||
dbProgram := conn.GetDialect().GetDbProgram()
|
dbProgram := conn.GetDialect().GetDbProgram()
|
||||||
if restore.PointInTime.Valid {
|
if restore.PointInTime.Valid {
|
||||||
|
if enabled, err := dbProgram.CheckBinlogEnabled(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !enabled {
|
||||||
|
return errors.New("数据库未启用 BINLOG")
|
||||||
|
}
|
||||||
|
if enabled, err := dbProgram.CheckBinlogRowFormat(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !enabled {
|
||||||
|
return errors.New("数据库未启用 BINLOG 行模式")
|
||||||
|
}
|
||||||
|
|
||||||
latestBinlogSequence, earliestBackupSequence := int64(-1), int64(-1)
|
latestBinlogSequence, earliestBackupSequence := int64(-1), int64(-1)
|
||||||
binlogHistory, ok, err := s.binlogHistoryRepo.GetLatestHistory(restore.DbInstanceId)
|
binlogHistory, ok, err := s.binlogHistoryRepo.GetLatestHistory(restore.DbInstanceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -363,7 +374,25 @@ func (s *dbScheduler) restorePointInTime(ctx context.Context, program dbi.DbProg
|
|||||||
if err := program.RestoreBackupHistory(ctx, backupHistory.DbName, backupHistory.DbBackupId, backupHistory.Uuid); err != nil {
|
if err := program.RestoreBackupHistory(ctx, backupHistory.DbName, backupHistory.DbBackupId, backupHistory.Uuid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return program.ReplayBinlog(ctx, job.DbName, job.DbName, restoreInfo)
|
if err := program.ReplayBinlog(ctx, job.DbName, job.DbName, restoreInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 由于 ReplayBinlog 未记录 BINLOG 事件,系统自动备份,避免数据丢失
|
||||||
|
backup := &entity.DbBackup{
|
||||||
|
DbJobBaseImpl: entity.NewDbBJobBase(backupHistory.DbInstanceId, entity.DbJobTypeBackup),
|
||||||
|
DbName: backupHistory.DbName,
|
||||||
|
Enabled: true,
|
||||||
|
Repeated: false,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
Interval: 0,
|
||||||
|
Name: fmt.Sprintf("%s-系统自动备份", backupHistory.DbName),
|
||||||
|
}
|
||||||
|
backup.Id = backupHistory.DbBackupId
|
||||||
|
if err := s.backupMysql(ctx, backup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dbScheduler) restoreBackupHistory(ctx context.Context, program dbi.DbProgram, job *entity.DbRestore) error {
|
func (s *dbScheduler) restoreBackupHistory(ctx context.Context, program dbi.DbProgram, job *entity.DbRestore) error {
|
||||||
@@ -399,8 +428,8 @@ func (s *dbScheduler) fetchBinlogMysql(ctx context.Context, backup entity.DbJob)
|
|||||||
}
|
}
|
||||||
dbProgram := conn.GetDialect().GetDbProgram()
|
dbProgram := conn.GetDialect().GetDbProgram()
|
||||||
binlogFiles, err := dbProgram.FetchBinlogs(ctx, false, earliestBackupSequence, latestBinlogSequence)
|
binlogFiles, err := dbProgram.FetchBinlogs(ctx, false, earliestBackupSequence, latestBinlogSequence)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
err = s.binlogHistoryRepo.InsertWithBinlogFiles(ctx, instanceId, binlogFiles)
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return s.binlogHistoryRepo.InsertWithBinlogFiles(ctx, instanceId, binlogFiles)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DbProgram interface {
|
type DbProgram interface {
|
||||||
|
CheckBinlogEnabled(ctx context.Context) (bool, error)
|
||||||
|
CheckBinlogRowFormat(ctx context.Context) (bool, error)
|
||||||
|
|
||||||
Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error)
|
Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error)
|
||||||
|
|
||||||
FetchBinlogs(ctx context.Context, downloadLatestBinlogFile bool, earliestBackupSequence, latestBinlogSequence int64) ([]*entity.BinlogFile, error)
|
FetchBinlogs(ctx context.Context, downloadLatestBinlogFile bool, earliestBackupSequence, latestBinlogSequence int64) ([]*entity.BinlogFile, error)
|
||||||
|
|||||||
@@ -77,6 +77,15 @@ func (svc *DbProgramMysql) GetBinlogFilePath(fileName string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (svc *DbProgramMysql) Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error) {
|
func (svc *DbProgramMysql) Backup(ctx context.Context, backupHistory *entity.DbBackupHistory) (*entity.BinlogInfo, error) {
|
||||||
|
binlogEnabled, err := svc.CheckBinlogEnabled(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rowFormatEnabled, err := svc.CheckBinlogRowFormat(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
dir := svc.getDbBackupDir(backupHistory.DbInstanceId, backupHistory.DbBackupId)
|
dir := svc.getDbBackupDir(backupHistory.DbInstanceId, backupHistory.DbBackupId)
|
||||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -95,10 +104,11 @@ func (svc *DbProgramMysql) Backup(ctx context.Context, backupHistory *entity.DbB
|
|||||||
"--add-drop-database",
|
"--add-drop-database",
|
||||||
"--result-file", tmpFile,
|
"--result-file", tmpFile,
|
||||||
"--single-transaction",
|
"--single-transaction",
|
||||||
"--master-data=2",
|
|
||||||
"--databases", backupHistory.DbName,
|
"--databases", backupHistory.DbName,
|
||||||
}
|
}
|
||||||
|
if binlogEnabled && rowFormatEnabled {
|
||||||
|
args = append(args, "--master-data=2")
|
||||||
|
}
|
||||||
cmd := exec.CommandContext(ctx, svc.getMysqlBin().MysqldumpPath, args...)
|
cmd := exec.CommandContext(ctx, svc.getMysqlBin().MysqldumpPath, args...)
|
||||||
logx.Debugf("backup database using mysqldump binary: %s", cmd.String())
|
logx.Debugf("backup database using mysqldump binary: %s", cmd.String())
|
||||||
if err := runCmd(cmd); err != nil {
|
if err := runCmd(cmd); err != nil {
|
||||||
@@ -115,7 +125,10 @@ func (svc *DbProgramMysql) Backup(ctx context.Context, backupHistory *entity.DbB
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
binlogInfo, err := readBinlogInfoFromBackup(reader)
|
binlogInfo := &entity.BinlogInfo{}
|
||||||
|
if binlogEnabled && rowFormatEnabled {
|
||||||
|
binlogInfo, err = readBinlogInfoFromBackup(reader)
|
||||||
|
}
|
||||||
_ = reader.Close()
|
_ = reader.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "从备份文件中读取 binlog 信息失败")
|
return nil, errors.Wrapf(err, "从备份文件中读取 binlog 信息失败")
|
||||||
@@ -568,18 +581,24 @@ func (svc *DbProgramMysql) ReplayBinlog(ctx context.Context, originalDatabase, t
|
|||||||
return errors.Wrap(err, "启动 mysqlbinlog 程序失败")
|
return errors.Wrap(err, "启动 mysqlbinlog 程序失败")
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
_ = mysqlbinlogCmd.Cancel()
|
||||||
if err := mysqlbinlogCmd.Wait(); err != nil {
|
if err := mysqlbinlogCmd.Wait(); err != nil {
|
||||||
if replayErr != nil {
|
if mysqlbinlogErr.Len() > 0 {
|
||||||
replayErr = errors.Wrap(replayErr, "运行 mysqlbinlog 程序失败")
|
logx.Errorf("运行 mysqlbinlog 程序失败", mysqlbinlogErr.String())
|
||||||
} else {
|
if replayErr != nil {
|
||||||
replayErr = errors.Errorf("运行 mysqlbinlog 程序失败: %s", mysqlbinlogErr.String())
|
replayErr = errors.Wrap(replayErr, "运行 mysqlbinlog 程序失败: "+mysqlbinlogErr.String())
|
||||||
|
} else {
|
||||||
|
replayErr = errors.Errorf("运行 mysqlbinlog 程序失败: %s", mysqlbinlogErr.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := mysqlCmd.Start(); err != nil {
|
if err := mysqlCmd.Start(); err != nil {
|
||||||
|
logx.Error("启动 mysql 程序失败")
|
||||||
return errors.Wrap(err, "启动 mysql 程序失败")
|
return errors.Wrap(err, "启动 mysql 程序失败")
|
||||||
}
|
}
|
||||||
if err := mysqlCmd.Wait(); err != nil {
|
if err := mysqlCmd.Wait(); err != nil {
|
||||||
|
logx.Errorf("运行 mysql 程序失败: %s", mysqlErr.String())
|
||||||
return errors.Errorf("运行 mysql 程序失败: %s", mysqlErr.String())
|
return errors.Errorf("运行 mysql 程序失败: %s", mysqlErr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,27 +625,30 @@ func (svc *DbProgramMysql) getServerVariable(_ context.Context, varName string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckBinlogEnabled checks whether binlog is enabled for the current instance.
|
// CheckBinlogEnabled checks whether binlog is enabled for the current instance.
|
||||||
func (svc *DbProgramMysql) CheckBinlogEnabled(ctx context.Context) error {
|
func (svc *DbProgramMysql) CheckBinlogEnabled(ctx context.Context) (bool, error) {
|
||||||
value, err := svc.getServerVariable(ctx, "log_bin")
|
value, err := svc.getServerVariable(ctx, "log_bin")
|
||||||
if err != nil {
|
switch {
|
||||||
return err
|
case err == nil:
|
||||||
|
return strings.ToUpper(value) == "ON", nil
|
||||||
|
case errors.Is(err, sql.ErrNoRows):
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
if strings.ToUpper(value) != "ON" {
|
|
||||||
return errors.Errorf("数据库未启用 binlog")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckBinlogRowFormat checks whether the binlog format is ROW.
|
// CheckBinlogRowFormat checks whether the binlog format is ROW or MIXED.
|
||||||
func (svc *DbProgramMysql) CheckBinlogRowFormat(ctx context.Context) error {
|
func (svc *DbProgramMysql) CheckBinlogRowFormat(ctx context.Context) (bool, error) {
|
||||||
value, err := svc.getServerVariable(ctx, "binlog_format")
|
value, err := svc.getServerVariable(ctx, "binlog_format")
|
||||||
if err != nil {
|
switch {
|
||||||
return err
|
case err == nil:
|
||||||
|
value = strings.ToUpper(value)
|
||||||
|
return value == "ROW" || value == "MIXED", nil
|
||||||
|
case errors.Is(err, sql.ErrNoRows):
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
if strings.ToUpper(value) != "ROW" {
|
|
||||||
return errors.Errorf("binlog 格式 %s 不是行模式", value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCmd(cmd *exec.Cmd) error {
|
func runCmd(cmd *exec.Cmd) error {
|
||||||
|
|||||||
@@ -153,8 +153,9 @@ func (s *DbInstanceSuite) TestRestorePontInTime() {
|
|||||||
s.createTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
s.createTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
targetTime := time.Now()
|
|
||||||
|
|
||||||
|
// 首次恢复数据库
|
||||||
|
firstTargetTime := time.Now()
|
||||||
s.dropTable(dbNameBackupTest, tableNameBackupTest, "")
|
s.dropTable(dbNameBackupTest, tableNameBackupTest, "")
|
||||||
s.selectTable(dbNameBackupTest, tableNameBackupTest, "运行 mysql 程序失败")
|
s.selectTable(dbNameBackupTest, tableNameBackupTest, "运行 mysql 程序失败")
|
||||||
s.createTable(dbNameBackupTest, tableNameNoBackupTest, "")
|
s.createTable(dbNameBackupTest, tableNameNoBackupTest, "")
|
||||||
@@ -165,7 +166,28 @@ func (s *DbInstanceSuite) TestRestorePontInTime() {
|
|||||||
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "运行 mysql 程序失败")
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "运行 mysql 程序失败")
|
||||||
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
||||||
|
|
||||||
s.testReplayBinlog(backupHistory, targetTime)
|
s.testReplayBinlog(backupHistory, firstTargetTime)
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameBackupTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
||||||
|
|
||||||
|
s.testBackup(backupHistory)
|
||||||
|
|
||||||
|
// 再次恢复数据库
|
||||||
|
secondTargetTime := time.Now()
|
||||||
|
s.dropTable(dbNameBackupTest, tableNameBackupTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameBackupTest, "运行 mysql 程序失败")
|
||||||
|
s.dropTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "运行 mysql 程序失败")
|
||||||
|
s.createTable(dbNameBackupTest, tableNameNoBackupTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "")
|
||||||
|
|
||||||
|
s.testRestore(backupHistory)
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameBackupTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
|
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
||||||
|
|
||||||
|
s.testReplayBinlog(backupHistory, secondTargetTime)
|
||||||
s.selectTable(dbNameBackupTest, tableNameBackupTest, "")
|
s.selectTable(dbNameBackupTest, tableNameBackupTest, "")
|
||||||
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
s.selectTable(dbNameBackupTest, tableNameRestorePITTest, "")
|
||||||
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
s.selectTable(dbNameBackupTest, tableNameNoBackupTest, "运行 mysql 程序失败")
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ const (
|
|||||||
DbJobFailed
|
DbJobFailed
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbJobType = string
|
type DbJobType string
|
||||||
|
|
||||||
|
func (typ DbJobType) String() string {
|
||||||
|
return string(typ)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DbJobTypeBackup DbJobType = "db-backup"
|
DbJobTypeBackup DbJobType = "db-backup"
|
||||||
@@ -121,10 +125,10 @@ func (d *DbJobBaseImpl) SetLastStatus(status DbJobStatus, err error) {
|
|||||||
jobName = DbJobNameBackup
|
jobName = DbJobNameBackup
|
||||||
case DbJobTypeRestore:
|
case DbJobTypeRestore:
|
||||||
jobName = DbJobNameRestore
|
jobName = DbJobNameRestore
|
||||||
case DbJobNameBinlog:
|
case DbJobTypeBinlog:
|
||||||
jobName = DbJobNameBinlog
|
jobName = DbJobNameBinlog
|
||||||
default:
|
default:
|
||||||
jobName = d.jobType
|
jobName = d.jobType.String()
|
||||||
}
|
}
|
||||||
d.LastStatus = status
|
d.LastStatus = status
|
||||||
var result = jobName + statusName
|
var result = jobName + statusName
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
type DbBackupHistory interface {
|
type DbBackupHistory interface {
|
||||||
base.Repo[*entity.DbBackupHistory]
|
base.Repo[*entity.DbBackupHistory]
|
||||||
|
|
||||||
// GetDbBackupHistories 分页获取数据备份历史
|
// GetHistories 分页获取数据备份历史
|
||||||
GetHistories(condition *entity.DbBackupHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
GetHistories(condition *entity.DbBackupHistoryQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||||
|
|
||||||
GetLatestHistory(instanceId uint64, dbName string, bi *entity.BinlogInfo) (*entity.DbBackupHistory, error)
|
GetLatestHistory(instanceId uint64, dbName string, bi *entity.BinlogInfo) (*entity.DbBackupHistory, error)
|
||||||
|
|||||||
Reference in New Issue
Block a user