github.com/TeaOSLab/EdgeNode@v1.3.8/internal/utils/bfs/meta_file.go (about)

     1  // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
     2  
     3  package bfs
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
     9  	"github.com/TeaOSLab/EdgeNode/internal/zero"
    10  	"io"
    11  	"os"
    12  	"sync"
    13  )
    14  
    15  const MFileExt = ".m"
    16  const Version1 = 1
    17  
    18  type MetaFile struct {
    19  	fp        *os.File
    20  	filename  string
    21  	headerMap map[string]*LazyFileHeader // hash => *LazyFileHeader
    22  	mu        *sync.RWMutex              // TODO 考虑单独一个,不要和bFile共享?
    23  
    24  	isModified      bool
    25  	modifiedHashMap map[string]zero.Zero // hash => Zero
    26  }
    27  
    28  func OpenMetaFile(filename string, mu *sync.RWMutex) (*MetaFile, error) {
    29  	fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	var mFile = &MetaFile{
    35  		filename:        filename,
    36  		fp:              fp,
    37  		headerMap:       map[string]*LazyFileHeader{},
    38  		mu:              mu,
    39  		modifiedHashMap: map[string]zero.Zero{},
    40  	}
    41  
    42  	// 从文件中加载已有的文件头信息
    43  	err = mFile.load()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	return mFile, nil
    49  }
    50  
    51  func (this *MetaFile) load() error {
    52  	AckReadThread()
    53  	_, err := this.fp.Seek(0, io.SeekStart)
    54  	ReleaseReadThread()
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	// TODO 检查文件是否完整
    60  
    61  	var buf = make([]byte, 4<<10)
    62  	var blockBytes []byte
    63  	for {
    64  		AckReadThread()
    65  		n, readErr := this.fp.Read(buf)
    66  		ReleaseReadThread()
    67  		if n > 0 {
    68  			blockBytes = append(blockBytes, buf[:n]...)
    69  			for len(blockBytes) > 4 {
    70  				var l = int(binary.BigEndian.Uint32(blockBytes[:4])) + 4 /* Len **/
    71  				if len(blockBytes) < l {
    72  					break
    73  				}
    74  
    75  				action, hash, data, decodeErr := DecodeMetaBlock(blockBytes[:l])
    76  				if decodeErr != nil {
    77  					return decodeErr
    78  				}
    79  
    80  				switch action {
    81  				case MetaActionNew:
    82  					this.headerMap[hash] = NewLazyFileHeaderFromData(data)
    83  				case MetaActionRemove:
    84  					delete(this.headerMap, hash)
    85  				}
    86  
    87  				blockBytes = blockBytes[l:]
    88  			}
    89  		}
    90  		if readErr != nil {
    91  			if readErr == io.EOF {
    92  				break
    93  			}
    94  			return readErr
    95  		}
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (this *MetaFile) WriteMeta(hash string, status int, expiresAt int64, expectedFileSize int64) error {
   102  
   103  	this.mu.Lock()
   104  	defer this.mu.Unlock()
   105  
   106  	this.headerMap[hash] = NewLazyFileHeader(&FileHeader{
   107  		Version:         Version1,
   108  		ExpiresAt:       expiresAt,
   109  		Status:          status,
   110  		ExpiredBodySize: expectedFileSize,
   111  		IsWriting:       true,
   112  	})
   113  
   114  	this.modifiedHashMap[hash] = zero.Zero{}
   115  
   116  	return nil
   117  }
   118  
   119  func (this *MetaFile) WriteHeaderBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64) error {
   120  	lazyHeader, ok := this.headerMap[hash]
   121  	if !ok {
   122  		return nil
   123  	}
   124  
   125  	header, err := lazyHeader.FileHeaderUnsafe()
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	// TODO 合并相邻block
   131  	header.HeaderBlocks = append(header.HeaderBlocks, BlockInfo{
   132  		BFileOffsetFrom: bOffsetFrom,
   133  		BFileOffsetTo:   bOffsetTo,
   134  	})
   135  
   136  	this.modifiedHashMap[hash] = zero.Zero{}
   137  
   138  	return nil
   139  }
   140  
   141  func (this *MetaFile) WriteBodyBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64, originOffsetFrom int64, originOffsetTo int64) error {
   142  	lazyHeader, ok := this.headerMap[hash]
   143  	if !ok {
   144  		return nil
   145  	}
   146  
   147  	header, err := lazyHeader.FileHeaderUnsafe()
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// TODO 合并相邻block
   153  	header.BodyBlocks = append(header.BodyBlocks, BlockInfo{
   154  		OriginOffsetFrom: originOffsetFrom,
   155  		OriginOffsetTo:   originOffsetTo,
   156  		BFileOffsetFrom:  bOffsetFrom,
   157  		BFileOffsetTo:    bOffsetTo,
   158  	})
   159  
   160  	this.modifiedHashMap[hash] = zero.Zero{}
   161  
   162  	return nil
   163  }
   164  
   165  func (this *MetaFile) WriteClose(hash string, headerSize int64, bodySize int64) error {
   166  	// TODO 考虑单个hash多次重复调用的情况
   167  
   168  	this.mu.Lock()
   169  	lazyHeader, ok := this.headerMap[hash]
   170  	if !ok {
   171  		this.mu.Unlock()
   172  		return nil
   173  	}
   174  
   175  	header, err := lazyHeader.FileHeaderUnsafe()
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	this.mu.Unlock()
   181  
   182  	// TODO 检查bodySize和expectedBodySize是否一致,如果不一致则从headerMap中删除
   183  
   184  	header.ModifiedAt = fasttime.Now().Unix()
   185  	header.HeaderSize = headerSize
   186  	header.BodySize = bodySize
   187  	header.Compact()
   188  
   189  	blockBytes, err := header.Encode(hash)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	this.mu.Lock()
   195  	defer this.mu.Unlock()
   196  
   197  	AckReadThread()
   198  	_, err = this.fp.Seek(0, io.SeekEnd)
   199  	ReleaseReadThread()
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	AckWriteThread()
   205  	_, err = this.fp.Write(blockBytes)
   206  	ReleaseWriteThread()
   207  
   208  	this.isModified = true
   209  	return err
   210  }
   211  
   212  func (this *MetaFile) RemoveFile(hash string) error {
   213  	this.mu.Lock()
   214  	defer this.mu.Unlock()
   215  
   216  	_, ok := this.headerMap[hash]
   217  	if ok {
   218  		delete(this.headerMap, hash)
   219  	}
   220  
   221  	if ok {
   222  		blockBytes, err := EncodeMetaBlock(MetaActionRemove, hash, nil)
   223  		if err != nil {
   224  			return err
   225  		}
   226  
   227  		AckWriteThread()
   228  		_, err = this.fp.Write(blockBytes)
   229  		ReleaseWriteThread()
   230  		if err != nil {
   231  			return err
   232  		}
   233  		this.isModified = true
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (this *MetaFile) FileHeader(hash string) (header *FileHeader, ok bool) {
   240  	this.mu.RLock()
   241  	defer this.mu.RUnlock()
   242  
   243  	lazyHeader, ok := this.headerMap[hash]
   244  
   245  	if ok {
   246  		var err error
   247  		header, err = lazyHeader.FileHeaderUnsafe()
   248  		if err != nil {
   249  			ok = false
   250  		}
   251  	}
   252  	return
   253  }
   254  
   255  func (this *MetaFile) FileHeaderUnsafe(hash string) (header *FileHeader, ok bool) {
   256  	lazyHeader, ok := this.headerMap[hash]
   257  
   258  	if ok {
   259  		var err error
   260  		header, err = lazyHeader.FileHeaderUnsafe()
   261  		if err != nil {
   262  			ok = false
   263  		}
   264  	}
   265  
   266  	return
   267  }
   268  
   269  func (this *MetaFile) CloneFileHeader(hash string) (header *FileHeader, ok bool) {
   270  	this.mu.RLock()
   271  	defer this.mu.RUnlock()
   272  	lazyHeader, ok := this.headerMap[hash]
   273  	if !ok {
   274  		return
   275  	}
   276  
   277  	var err error
   278  	header, err = lazyHeader.FileHeaderUnsafe()
   279  	if err != nil {
   280  		ok = false
   281  		return
   282  	}
   283  
   284  	header = header.Clone()
   285  	return
   286  }
   287  
   288  func (this *MetaFile) FileHeaders() map[string]*LazyFileHeader {
   289  	this.mu.RLock()
   290  	defer this.mu.RUnlock()
   291  	return this.headerMap
   292  }
   293  
   294  func (this *MetaFile) ExistFile(hash string) bool {
   295  	this.mu.RLock()
   296  	defer this.mu.RUnlock()
   297  
   298  	_, ok := this.headerMap[hash]
   299  	return ok
   300  }
   301  
   302  // Compact the meta file
   303  // TODO 考虑自动Compact的时机(脏数据比例?)
   304  func (this *MetaFile) Compact() error {
   305  	this.mu.Lock()
   306  	defer this.mu.Unlock()
   307  
   308  	var buf = bytes.NewBuffer(nil)
   309  	for hash, lazyHeader := range this.headerMap {
   310  		header, err := lazyHeader.FileHeaderUnsafe()
   311  		if err != nil {
   312  			return err
   313  		}
   314  
   315  		blockBytes, err := header.Encode(hash)
   316  		if err != nil {
   317  			return err
   318  		}
   319  		buf.Write(blockBytes)
   320  	}
   321  
   322  	AckWriteThread()
   323  	err := this.fp.Truncate(int64(buf.Len()))
   324  	ReleaseWriteThread()
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	AckReadThread()
   330  	_, err = this.fp.Seek(0, io.SeekStart)
   331  	ReleaseReadThread()
   332  	if err != nil {
   333  		return err
   334  	}
   335  
   336  	AckWriteThread()
   337  	_, err = this.fp.Write(buf.Bytes())
   338  	ReleaseWriteThread()
   339  	this.isModified = true
   340  	return err
   341  }
   342  
   343  func (this *MetaFile) SyncUnsafe() error {
   344  	if !this.isModified {
   345  		return nil
   346  	}
   347  
   348  	AckWriteThread()
   349  	err := this.fp.Sync()
   350  	ReleaseWriteThread()
   351  	if err != nil {
   352  		return err
   353  	}
   354  
   355  	for hash := range this.modifiedHashMap {
   356  		lazyHeader, ok := this.headerMap[hash]
   357  		if ok {
   358  			header, decodeErr := lazyHeader.FileHeaderUnsafe()
   359  			if decodeErr != nil {
   360  				return decodeErr
   361  			}
   362  			header.IsWriting = false
   363  		}
   364  	}
   365  
   366  	this.isModified = false
   367  	this.modifiedHashMap = map[string]zero.Zero{}
   368  	return nil
   369  }
   370  
   371  // Close 关闭当前文件
   372  func (this *MetaFile) Close() error {
   373  	return this.fp.Close()
   374  }
   375  
   376  // RemoveAll 删除所有数据
   377  func (this *MetaFile) RemoveAll() error {
   378  	_ = this.fp.Close()
   379  	return os.Remove(this.fp.Name())
   380  }