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  }