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  }