github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logstore/driver/batchstoredriver/file.go (about) 1 // Copyright 2021 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 package batchstoredriver 15 16 import ( 17 "context" 18 "fmt" 19 "os" 20 "path" 21 "path/filepath" 22 "sort" 23 "strconv" 24 "strings" 25 "sync" 26 27 "github.com/matrixorigin/matrixone/pkg/common/moerr" 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/driver/entry" 30 ) 31 32 var suffix = ".rot" 33 34 func MakeVersionFile(dir, name string, version uint64) string { 35 return fmt.Sprintf("%s-%d%s", filepath.Join(dir, name), version, suffix) 36 } 37 38 func ParseVersion(name, prefix, suffix string) (n int, ok bool) { 39 woPrefix := strings.TrimPrefix(name, prefix+"-") 40 if len(woPrefix) == len(name) { 41 return 0, false 42 } 43 strVersion := strings.TrimSuffix(woPrefix, suffix) 44 if len(strVersion) == len(woPrefix) { 45 return 0, false 46 } 47 v, err := strconv.Atoi(strVersion) 48 if err != nil { 49 return 0, false 50 } 51 return v, true 52 } 53 54 type rotateFile struct { 55 *sync.RWMutex 56 dir, name string 57 checker RotateChecker 58 uncommitted []*vFile 59 history History 60 61 commitWg sync.WaitGroup 62 commitCtx context.Context 63 commitCancel context.CancelFunc 64 commitQueue chan *vFile 65 66 nextVer uint64 67 68 wg sync.WaitGroup 69 } 70 71 func OpenRotateFile(dir, name string, mu *sync.RWMutex, rotateChecker RotateChecker, 72 historyFactory HistoryFactory, observer ReplayObserver) (*rotateFile, error) { 73 var err error 74 if mu == nil { 75 mu = new(sync.RWMutex) 76 } 77 newDir := false 78 if _, err = os.Stat(dir); os.IsNotExist(err) { 79 err = os.MkdirAll(dir, 0755) 80 if err != nil { 81 return nil, err 82 } 83 newDir = true 84 } 85 86 if rotateChecker == nil { 87 rotateChecker = NewMaxSizeRotateChecker(DefaultRotateCheckerMaxSize) 88 } 89 if historyFactory == nil { 90 historyFactory = DefaultHistoryFactory 91 } 92 93 rf := &rotateFile{ 94 RWMutex: mu, 95 dir: dir, 96 name: name, 97 uncommitted: make([]*vFile, 0), 98 checker: rotateChecker, 99 commitQueue: make(chan *vFile, 10000), 100 history: historyFactory(), 101 } 102 if !newDir { 103 files, err := os.ReadDir(dir) 104 if err != nil { 105 return nil, err 106 } 107 vfiles := make([]VFile, 0) 108 for _, f := range files { 109 version, ok := ParseVersion(f.Name(), rf.name, suffix) 110 if !ok { 111 continue 112 } 113 file, err := os.OpenFile( 114 path.Join(dir, f.Name()), os.O_RDWR, os.ModePerm) 115 if err != nil { 116 return nil, err 117 } 118 info, err := f.Info() 119 if err != nil { 120 return nil, err 121 } 122 vf := &vFile{ 123 RWMutex: &sync.RWMutex{}, 124 File: file, 125 version: version, 126 commitCond: *sync.NewCond(new(sync.Mutex)), 127 history: rf.history, 128 size: int(info.Size()), 129 syncpos: int(info.Size()), 130 } 131 vf.vInfo = newVInfo(vf) 132 // vf.ReadMeta() 133 vfiles = append(vfiles, vf) 134 } 135 if len(vfiles) == 0 { 136 err = rf.scheduleNew() 137 if err != nil { 138 return nil, err 139 } 140 } else { 141 sort.Slice(vfiles, func(i, j int) bool { 142 return vfiles[i].(*vFile).version < vfiles[j].(*vFile).version 143 }) 144 observer.onTruncatedFile(vfiles[0].Id() - 1) 145 rf.history.Extend(vfiles[:len(vfiles)-1]...) 146 for _, vf := range vfiles[:len(vfiles)-1] { 147 vf.OnReplayCommitted() 148 } 149 rf.uncommitted = append( 150 rf.uncommitted, vfiles[len(vfiles)-1].(*vFile)) 151 rf.nextVer = uint64(vfiles[len(vfiles)-1].Id()) 152 } 153 } else { 154 err = rf.scheduleNew() 155 } 156 rf.commitCtx, rf.commitCancel = context.WithCancel(context.Background()) 157 rf.wg.Add(1) 158 go rf.commitLoop() 159 return rf, err 160 } 161 162 func (rf *rotateFile) getEntryFromUncommitted(id int) (e *vFile) { 163 for _, vf := range rf.uncommitted { 164 if vf.version == id { 165 return vf 166 } 167 } 168 return nil 169 } 170 func (rf *rotateFile) Replay(r *replayer) error { 171 entryIDs := rf.history.EntryIds() 172 for _, vf := range rf.uncommitted { 173 entryIDs = append(entryIDs, vf.Id()) 174 } 175 for _, id := range entryIDs { 176 entry := rf.history.GetEntry(id) 177 if entry == nil { 178 vf := rf.getEntryFromUncommitted(id) 179 if vf == nil { 180 panic("wrong id") 181 } 182 entry = vf 183 } 184 185 err := entry.Replay(r) 186 if err != nil { 187 panic(err) 188 } 189 } 190 return nil 191 } 192 193 func (rf *rotateFile) commitLoop() { 194 defer rf.wg.Done() 195 for { 196 select { 197 case <-rf.commitCtx.Done(): 198 return 199 case file := <-rf.commitQueue: 200 file.Commit() 201 rf.commitFile() 202 rf.commitWg.Done() 203 } 204 } 205 } 206 207 func (rf *rotateFile) scheduleCommit(file *vFile) { 208 rf.commitWg.Add(1) 209 rf.commitQueue <- file 210 } 211 212 func (rf *rotateFile) GetHistory() History { 213 return rf.history 214 } 215 216 func (rf *rotateFile) Close() error { 217 rf.commitWg.Wait() 218 rf.commitCancel() 219 rf.wg.Wait() 220 rf.history.Close() 221 for _, vf := range rf.uncommitted { 222 vf.Close() 223 return nil 224 } 225 return nil 226 } 227 228 func (rf *rotateFile) scheduleNew() error { 229 rf.nextVer++ 230 fname := MakeVersionFile(rf.dir, rf.name, rf.nextVer) 231 vf, err := newVFile(nil, fname, int(rf.nextVer), rf.history) 232 if err != nil { 233 return err 234 } 235 rf.uncommitted = append(rf.uncommitted, vf) 236 return nil 237 } 238 239 func (rf *rotateFile) getFileState() *vFileState { 240 l := len(rf.uncommitted) 241 if l == 0 { 242 return nil 243 } 244 return rf.uncommitted[l-1].GetState() 245 } 246 247 func (rf *rotateFile) makeSpace(size int) (rotated *vFile, curr *vFileState, err error) { 248 var ( 249 rotNeeded bool 250 ) 251 l := len(rf.uncommitted) 252 if l == 0 { 253 rotNeeded, err = rf.checker.PrepareAppend(nil, size) 254 } else { 255 rotNeeded, err = rf.checker.PrepareAppend(rf.uncommitted[l-1], size) 256 } 257 if err != nil { 258 return nil, nil, err 259 } 260 if l == 0 || rotNeeded { 261 if rotNeeded { 262 rotated = rf.uncommitted[l-1] 263 rf.scheduleCommit(rotated) 264 } 265 if err = rf.scheduleNew(); err != nil { 266 return nil, nil, err 267 } 268 } 269 curr = rf.getFileState() 270 // if size > curr.bufSize { 271 // return nil, nil, moerr.NewInternalErrorNoCtx("buff size is %v, but entry size is %v", rf.getFileState().file.bufSize, size) //TODO write without buf 272 // } 273 // if size+curr.bufPos > curr.bufSize { 274 // curr.file.Sync() 275 // logutil.Info("rf.250\n") 276 // curr.bufPos = 0 277 // } 278 curr.file.PrepareWrite(size) 279 return rotated, curr, nil 280 } 281 282 func (rf *rotateFile) GetAppender() FileAppender { 283 return newFileAppender(rf) 284 } 285 286 func (rf *rotateFile) commitFile() { 287 rf.Lock() 288 f := rf.uncommitted[0] 289 if !f.HasCommitted() { 290 panic("logic error") 291 } 292 rf.uncommitted = rf.uncommitted[1:] 293 err := f.Archive() 294 if err != nil { 295 panic(err) 296 } 297 rf.Unlock() 298 logutil.Debugf("Committed %s", f.Name()) 299 } 300 301 func (rf *rotateFile) Sync() error { 302 rf.RLock() 303 if len(rf.uncommitted) == 0 { 304 rf.RUnlock() 305 return nil 306 } 307 if len(rf.uncommitted) == 1 { 308 f := rf.uncommitted[0] 309 rf.RUnlock() 310 return f.Sync() 311 } 312 lastFile := rf.uncommitted[len(rf.uncommitted)-1] 313 waitFile := rf.uncommitted[len(rf.uncommitted)-2] 314 rf.RUnlock() 315 waitFile.WaitCommitted() 316 return lastFile.Sync() 317 } 318 319 func (rf *rotateFile) Load(ver int, groupId uint32, lsn uint64) (*entry.Entry, error) { 320 vf, err := rf.GetEntryByVersion(ver) 321 if err != nil { 322 return nil, err 323 } 324 return vf.Load(lsn) 325 } 326 327 func (rf *rotateFile) GetEntryByVersion(version int) (VFile, error) { 328 var vf VFile 329 rf.RLock() 330 defer rf.RUnlock() 331 for _, vf := range rf.uncommitted { 332 if vf.version == version { 333 return vf, nil 334 } 335 } 336 vf = rf.GetHistory().GetEntry(version) 337 if vf != nil { 338 return vf, nil 339 } 340 return nil, moerr.NewInternalErrorNoCtx("version not existed") 341 }