github.com/matrixorigin/matrixone@v1.2.0/pkg/backup/backup.go (about) 1 // Copyright 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package backup 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/csv" 21 "github.com/matrixorigin/matrixone/pkg/container/types" 22 "os" 23 "path" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/google/uuid" 29 "github.com/matrixorigin/matrixone/pkg/common/moerr" 30 "github.com/matrixorigin/matrixone/pkg/fileservice" 31 "github.com/matrixorigin/matrixone/pkg/logutil" 32 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 33 ) 34 35 // Backup 36 // Note: ctx needs to support cancel. The user can cancel the backup task by canceling the ctx. 37 func Backup(ctx context.Context, bs *tree.BackupStart, cfg *Config) error { 38 var err error 39 var s3Conf *s3Config 40 if !cfg.metasMustBeSet() { 41 return moerr.NewInternalError(ctx, "invalid config or metas or fileservice") 42 } 43 if bs == nil { 44 return moerr.NewInternalError(ctx, "invalid backup start") 45 } 46 // step 1 : setup fileservice 47 //1.1 setup ETL fileservice for general usage 48 if !bs.IsS3 { 49 cfg.GeneralDir, _, err = setupFilesystem(ctx, bs.Dir, true) 50 if err != nil { 51 return err 52 } 53 // for tae hakeeper 54 cfg.TaeDir, _, err = setupFilesystem(ctx, bs.Dir, false) 55 if err != nil { 56 return err 57 } 58 // for parallel backup 59 parallel, err := strconv.ParseUint(bs.Parallelism, 10, 16) 60 if err != nil { 61 return err 62 } 63 cfg.Parallelism = uint16(parallel) 64 } else { 65 s3Conf, err = getS3Config(ctx, bs.Option) 66 if err != nil { 67 return err 68 } 69 cfg.GeneralDir, _, err = setupS3(ctx, s3Conf, true) 70 if err != nil { 71 return err 72 } 73 cfg.TaeDir, _, err = setupS3(ctx, s3Conf, false) 74 if err != nil { 75 return err 76 } 77 cfg.Parallelism = s3Conf.parallelism 78 } 79 80 if bs.BackupTs == "" { 81 cfg.BackupTs = types.TS{} 82 } else { 83 cfg.BackupTs = types.StringToTS(bs.BackupTs) 84 } 85 cfg.BackupType = bs.BackupType 86 87 // step 2 : backup mo 88 if err = backupBuildInfo(ctx, cfg); err != nil { 89 return err 90 } 91 92 if err = backupConfigs(ctx, cfg); err != nil { 93 return err 94 } 95 96 if err = backupTae(ctx, cfg); err != nil { 97 return err 98 } 99 100 if err = backupHakeeper(ctx, cfg); err != nil { 101 return err 102 } 103 104 if err = saveMetas(ctx, cfg); err != nil { 105 return err 106 } 107 108 return err 109 } 110 111 // saveBuildInfo saves backupVersion, build info. 112 func backupBuildInfo(ctx context.Context, cfg *Config) error { 113 cfg.Metas.AppendVersion(Version) 114 cfg.Metas.AppendBuildinfo(buildInfo()) 115 return nil 116 } 117 118 // saveConfigs saves cluster config or service config 119 func backupConfigs(ctx context.Context, cfg *Config) error { 120 var err error 121 // save cluster config files 122 for typ, files := range launchConfigPaths { 123 for _, f := range files { 124 err = backupConfigFile(ctx, typ, f, cfg) 125 if err != nil { 126 return err 127 } 128 } 129 } 130 131 return err 132 } 133 134 var backupTae = func(ctx context.Context, config *Config) error { 135 fs := fileservice.SubPath(config.TaeDir, taeDir) 136 return BackupData(ctx, config.SharedFs, fs, "", config) 137 } 138 139 func backupHakeeper(ctx context.Context, config *Config) error { 140 var ( 141 err error 142 haData []byte 143 ) 144 if !config.metasGeneralFsMustBeSet() { 145 return moerr.NewInternalError(ctx, "invalid config or metas or fileservice") 146 } 147 if config.HAkeeper == nil { 148 return moerr.NewInternalError(ctx, "hakeeper client is nil") 149 } 150 fs := fileservice.SubPath(config.TaeDir, hakeeperDir) 151 // get hakeeper data 152 haData, err = config.HAkeeper.GetBackupData(ctx) 153 if err != nil { 154 return err 155 } 156 return writeFile(ctx, fs, HakeeperFile, haData) 157 } 158 159 func backupConfigFile(ctx context.Context, typ, configPath string, cfg *Config) error { 160 if !cfg.metasGeneralFsMustBeSet() { 161 return moerr.NewInternalError(ctx, "invalid config or metas or fileservice") 162 } 163 data, err := os.ReadFile(configPath) 164 if err != nil { 165 logutil.Errorf("read file %s failed, err: %v", configPath, err) 166 //!!!neglect the error 167 return nil 168 } 169 uid, _ := uuid.NewV7() 170 _, file := path.Split(configPath) 171 newfile := file + "_" + uid.String() 172 cfg.Metas.AppendLaunchconfig(typ, newfile) 173 filename := configDir + "/" + newfile 174 return writeFile(ctx, cfg.GeneralDir, filename, data) 175 } 176 177 func saveMetas(ctx context.Context, cfg *Config) error { 178 if !cfg.metasGeneralFsMustBeSet() { 179 return moerr.NewInternalError(ctx, "invalid config or metas or fileservice") 180 } 181 lines := cfg.Metas.CsvString() 182 metas, err := ToCsvLine2(lines) 183 if err != nil { 184 return err 185 } 186 return writeFile(ctx, cfg.GeneralDir, moMeta, []byte(metas)) 187 } 188 189 func ToCsvLine2(s [][]string) (string, error) { 190 ss := strings.Builder{} 191 writer := csv.NewWriter(&ss) 192 for _, t := range s { 193 err := writer.Write(t) 194 if err != nil { 195 return "", err 196 } 197 } 198 199 writer.Flush() 200 return ss.String(), nil 201 } 202 203 func saveTaeFilesList(ctx context.Context, Fs fileservice.FileService, taeFiles []*taeFile, backupTime, backupTS, typ string) error { 204 var err error 205 if Fs == nil { 206 return moerr.NewInternalError(ctx, "fileservice is nil") 207 } 208 _, err = time.Parse(time.DateTime, backupTime) 209 if err != nil { 210 return err 211 } 212 lines, size := taeFileListToCsv(taeFiles) 213 metas, err := ToCsvLine2(lines) 214 if err != nil { 215 return err 216 } 217 //save tae files list 218 err = writeFile(ctx, Fs, taeList, []byte(metas)) 219 if err != nil { 220 return err 221 } 222 223 //save tae files size 224 lines = [][]string{taeBackupTimeAndSizeToCsv(backupTime, backupTS, typ, size)} 225 metas, err = ToCsvLine2(lines) 226 if err != nil { 227 return err 228 } 229 230 return writeFile(ctx, Fs, taeSum, []byte(metas)) 231 } 232 233 // fromCsvBytes converts the csv bytes to the array of string 234 func fromCsvBytes(data []byte) ([][]string, error) { 235 r := csv.NewReader(bytes.NewReader(data)) 236 return r.ReadAll() 237 }