github.com/matrixorigin/matrixone@v1.2.0/pkg/logservice/store_metadata.go (about) 1 // Copyright 2021 - 2022 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 logservice 16 17 import ( 18 "bytes" 19 "crypto/md5" 20 "fmt" 21 "io" 22 "path/filepath" 23 "runtime" 24 25 "github.com/cockroachdb/errors/oserror" 26 "github.com/lni/vfs" 27 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 28 "go.uber.org/zap" 29 ) 30 31 const ( 32 logMetadataFilename = "mo-logservice.metadata" 33 defaultDirFileMode = 0750 34 ) 35 36 func ws(err error) error { 37 return err 38 } 39 40 func dirExist(name string, fs vfs.FS) (result bool, err error) { 41 if name == "." || name == "/" { 42 return true, nil 43 } 44 f, err := fs.OpenDir(name) 45 if err != nil && oserror.IsNotExist(err) { 46 return false, nil 47 } 48 if err != nil { 49 return false, err 50 } 51 defer func() { 52 err = firstError(err, ws(f.Close())) 53 }() 54 s, err := f.Stat() 55 if err != nil { 56 return false, ws(err) 57 } 58 if !s.IsDir() { 59 panic("not a dir") 60 } 61 return true, nil 62 } 63 64 func mkdirAll(dir string, fs vfs.FS) error { 65 exist, err := dirExist(dir, fs) 66 if err != nil { 67 return err 68 } 69 if exist { 70 return nil 71 } 72 parent := fs.PathDir(dir) 73 exist, err = dirExist(parent, fs) 74 if err != nil { 75 return err 76 } 77 if !exist { 78 if err := mkdirAll(parent, fs); err != nil { 79 return err 80 } 81 } 82 return mkdir(dir, fs) 83 } 84 85 func mkdir(dir string, fs vfs.FS) error { 86 parent := fs.PathDir(dir) 87 exist, err := dirExist(parent, fs) 88 if err != nil { 89 return err 90 } 91 if !exist { 92 panic(fmt.Sprintf("%s doesn't exist when creating %s", parent, dir)) 93 } 94 if err := fs.MkdirAll(dir, defaultDirFileMode); err != nil { 95 return err 96 } 97 return syncDir(parent, fs) 98 } 99 100 func syncDir(dir string, fs vfs.FS) (err error) { 101 if runtime.GOOS == "windows" { 102 return nil 103 } 104 if dir == "." { 105 return nil 106 } 107 f, err := fs.OpenDir(dir) 108 if err != nil { 109 return err 110 } 111 defer func() { 112 err = firstError(err, ws(f.Close())) 113 }() 114 fileInfo, err := f.Stat() 115 if err != nil { 116 return ws(err) 117 } 118 if !fileInfo.IsDir() { 119 panic("not a dir") 120 } 121 df, err := fs.OpenDir(filepath.Clean(dir)) 122 if err != nil { 123 return err 124 } 125 defer func() { 126 err = firstError(err, ws(df.Close())) 127 }() 128 return ws(df.Sync()) 129 } 130 131 func getHash(data []byte) []byte { 132 h := md5.New() 133 if _, err := h.Write(data); err != nil { 134 panic(err) 135 } 136 s := h.Sum(nil) 137 return s[8:] 138 } 139 140 func exist(name string, fs vfs.FS) (bool, error) { 141 if name == "." || name == "/" { 142 return true, nil 143 } 144 _, err := fs.Stat(name) 145 if err != nil && oserror.IsNotExist(err) { 146 return false, nil 147 } 148 if err != nil { 149 return false, err 150 } 151 return true, nil 152 } 153 154 func createMetadataFile(dir string, 155 filename string, obj Marshaller, fs vfs.FS) (err error) { 156 de, err := dirExist(dir, fs) 157 if err != nil { 158 return err 159 } 160 if !de { 161 if err := mkdirAll(dir, fs); err != nil { 162 return err 163 } 164 } 165 tmp := fs.PathJoin(dir, fmt.Sprintf("%s.tmp", filename)) 166 fp := fs.PathJoin(dir, filename) 167 f, err := fs.Create(tmp) 168 if err != nil { 169 return err 170 } 171 defer func() { 172 err = firstError(err, f.Close()) 173 err = firstError(err, syncDir(dir, fs)) 174 }() 175 data := MustMarshal(obj) 176 h := getHash(data) 177 n, err := f.Write(h) 178 if err != nil { 179 return ws(err) 180 } 181 if n != len(h) { 182 return ws(io.ErrShortWrite) 183 } 184 n, err = f.Write(data) 185 if err != nil { 186 return ws(err) 187 } 188 if n != len(data) { 189 return ws(io.ErrShortWrite) 190 } 191 if err := ws(f.Sync()); err != nil { 192 return err 193 } 194 return fs.Rename(tmp, fp) 195 } 196 197 func readMetadataFile(dir string, 198 filename string, obj Unmarshaler, fs vfs.FS) (err error) { 199 fp := fs.PathJoin(dir, filename) 200 f, err := fs.Open(filepath.Clean(fp)) 201 if err != nil { 202 return err 203 } 204 defer func() { 205 err = firstError(err, ws(f.Close())) 206 }() 207 data, err := io.ReadAll(f) 208 if err != nil { 209 return ws(err) 210 } 211 if len(data) < 8 { 212 panic("corrupted flag file") 213 } 214 h := data[:8] 215 buf := data[8:] 216 expectedHash := getHash(buf) 217 if !bytes.Equal(h, expectedHash) { 218 panic("corrupted flag file content") 219 } 220 MustUnmarshal(obj, buf) 221 return nil 222 } 223 224 func hasMetadataRec(dir string, 225 filename string, shardID uint64, replicaID uint64, fs vfs.FS) (has bool, err error) { 226 var md metadata.LogStore 227 if err := readMetadataFile(dir, filename, &md, fs); err != nil { 228 return false, err 229 } 230 for _, rec := range md.Shards { 231 if rec.ShardID == shardID && rec.ReplicaID == replicaID { 232 return true, nil 233 } 234 } 235 return false, nil 236 } 237 238 func (l *store) loadMetadata() error { 239 fs := l.cfg.FS 240 dir := l.cfg.DataDir 241 fp := fs.PathJoin(dir, logMetadataFilename) 242 found, err := exist(fp, fs) 243 if err != nil { 244 return err 245 } 246 if !found { 247 return nil 248 } 249 expectedUUID := l.mu.metadata.UUID 250 if err := readMetadataFile(dir, logMetadataFilename, &l.mu.metadata, fs); err != nil { 251 return err 252 } 253 if expectedUUID != l.mu.metadata.UUID { 254 l.runtime.Logger().Panic("unexpected UUID", 255 zap.String("on disk UUID", l.mu.metadata.UUID), 256 zap.String("expect", expectedUUID)) 257 } 258 return nil 259 } 260 261 func (l *store) mustSaveMetadata() { 262 fs := l.cfg.FS 263 dir := l.cfg.DataDir 264 if err := createMetadataFile(dir, logMetadataFilename, &l.mu.metadata, fs); err != nil { 265 l.runtime.Logger().Panic("failed to save metadata file", zap.Error(err)) 266 } 267 } 268 269 func (l *store) addMetadata(shardID uint64, replicaID uint64) { 270 rec := metadata.LogShard{} 271 rec.ShardID = shardID 272 rec.ReplicaID = replicaID 273 l.mu.Lock() 274 defer l.mu.Unlock() 275 276 for _, rec := range l.mu.metadata.Shards { 277 if rec.ShardID == shardID && rec.ReplicaID == replicaID { 278 l.runtime.Logger().Info(fmt.Sprintf("addMetadata for shardID %d skipped, dupl shard", shardID)) 279 return 280 } 281 } 282 283 l.mu.metadata.Shards = append(l.mu.metadata.Shards, rec) 284 l.mustSaveMetadata() 285 } 286 287 func (l *store) removeMetadata(shardID uint64, replicaID uint64) { 288 l.mu.Lock() 289 defer l.mu.Unlock() 290 291 shards := make([]metadata.LogShard, 0) 292 for _, rec := range l.mu.metadata.Shards { 293 if rec.ShardID != shardID || rec.ReplicaID != replicaID { 294 shards = append(shards, rec) 295 } 296 } 297 l.mu.metadata.Shards = shards 298 l.mustSaveMetadata() 299 } 300 301 func (l *store) getReplicaID(shardID uint64) int64 { 302 l.mu.Lock() 303 defer l.mu.Unlock() 304 for _, rec := range l.mu.metadata.Shards { 305 if rec.ShardID == shardID { 306 return int64(rec.ReplicaID) 307 } 308 } 309 return -1 310 } 311 312 func (l *store) getShards() []metadata.LogShard { 313 l.mu.Lock() 314 defer l.mu.Unlock() 315 shards := make([]metadata.LogShard, 0, len(l.mu.metadata.Shards)) 316 shards = append(shards, l.mu.metadata.Shards...) 317 return shards 318 }