go.etcd.io/etcd@v3.3.27+incompatible/wal/repair.go (about) 1 // Copyright 2015 The etcd Authors 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 wal 16 17 import ( 18 "io" 19 "os" 20 "path/filepath" 21 "time" 22 23 "github.com/coreos/etcd/pkg/fileutil" 24 "github.com/coreos/etcd/wal/walpb" 25 ) 26 27 // Repair tries to repair ErrUnexpectedEOF in the 28 // last wal file by truncating. 29 func Repair(dirpath string) bool { 30 f, err := openLast(dirpath) 31 if err != nil { 32 return false 33 } 34 defer f.Close() 35 36 rec := &walpb.Record{} 37 decoder := newDecoder(f) 38 for { 39 lastOffset := decoder.lastOffset() 40 err := decoder.decode(rec) 41 switch err { 42 case nil: 43 // update crc of the decoder when necessary 44 switch rec.Type { 45 case crcType: 46 crc := decoder.crc.Sum32() 47 // current crc of decoder must match the crc of the record. 48 // do no need to match 0 crc, since the decoder is a new one at this case. 49 if crc != 0 && rec.Validate(crc) != nil { 50 return false 51 } 52 decoder.updateCRC(rec.Crc) 53 } 54 continue 55 case io.EOF: 56 return true 57 case io.ErrUnexpectedEOF: 58 plog.Noticef("repairing %v", f.Name()) 59 bf, bferr := os.Create(f.Name() + ".broken") 60 if bferr != nil { 61 plog.Errorf("could not repair %v, failed to create backup file", f.Name()) 62 return false 63 } 64 defer bf.Close() 65 66 if _, err = f.Seek(0, io.SeekStart); err != nil { 67 plog.Errorf("could not repair %v, failed to read file", f.Name()) 68 return false 69 } 70 71 if _, err = io.Copy(bf, f); err != nil { 72 plog.Errorf("could not repair %v, failed to copy file", f.Name()) 73 return false 74 } 75 76 if err = f.Truncate(int64(lastOffset)); err != nil { 77 plog.Errorf("could not repair %v, failed to truncate file", f.Name()) 78 return false 79 } 80 81 start := time.Now() 82 if err = fileutil.Fsync(f.File); err != nil { 83 plog.Errorf("could not repair %v, failed to sync file", f.Name()) 84 return false 85 } 86 syncDurations.Observe(time.Since(start).Seconds()) 87 88 return true 89 default: 90 plog.Errorf("could not repair error (%v)", err) 91 return false 92 } 93 } 94 } 95 96 // openLast opens the last wal file for read and write. 97 func openLast(dirpath string) (*fileutil.LockedFile, error) { 98 names, err := readWalNames(dirpath) 99 if err != nil { 100 return nil, err 101 } 102 last := filepath.Join(dirpath, names[len(names)-1]) 103 return fileutil.LockFile(last, os.O_RDWR, fileutil.PrivateFileMode) 104 }