|
- package cache
-
- import (
- "context"
- "io"
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "gitlink.org.cn/cloudream/common/pkgs/future"
- cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
- "gitlink.org.cn/cloudream/common/utils/lo2"
- "gitlink.org.cn/cloudream/common/utils/math2"
- "gitlink.org.cn/cloudream/common/utils/serder"
- "gitlink.org.cn/cloudream/storage/client2/internal/mount/fuse"
- "gitlink.org.cn/cloudream/storage/common/pkgs/downloader"
- )
-
- type FileInfo struct {
- // 文件总大小。可能会超过对应的远端文件的大小。
- // 此大小可能与本地缓存文件大小也不用,需要定时将本地缓存文件大小修正到与这个值相同。
- Size int64
- // 本文件是否有未提交的修改
- Dirty bool
- // 数据段列表,按照段开始位置从小到大排列
- Segments []Range
- // 文件对应的对象ID,仅在文件是一个缓存文件时才有值
- ObjectID cdssdk.ObjectID
- // 文件对应的对象大小,仅在文件是一个缓存文件时才有值。
- // 此值代表有多少数据应该从远端加载,所以可能会小于远端实际大小
- ObjectSize int64
- // 如果本文件完全是一个缓存文件,那么这个字段记录了其内容的哈希值,用于在下载缓存数据时,检查远端文件是否被修改过
- Hash string
- // 文件的最后修改时间
- ModTime time.Time
- // 文件的权限
- Perm os.FileMode
- }
-
- // 返回值代表文件大小是否发生了变化
- func (f *FileInfo) EnlargeTo(size int64) bool {
- if size > f.Size {
- f.Size = size
- return true
- }
- return false
- }
-
- type Range struct {
- Position int64
- Length int64
- }
-
- func (r *Range) GetPosition() int64 {
- return r.Position
- }
-
- func (r *Range) GetLength() int64 {
- return r.Length
- }
-
- type ReadRequest struct {
- Position int64
- Length int64
- Callback *future.SetVoidFuture
- Zeros int64 // 如果>0,那么说明读到了一块全0的数据,值的大小代表这块数据的长度,此时Segment字段为nil
- Segment *FileSegment
- }
-
- func (r *ReadRequest) GetPosition() int64 {
- return r.Position
- }
-
- func (r *ReadRequest) GetLength() int64 {
- return r.Length
- }
-
- type WriteReqeust struct {
- Position int64
- Length int64
- Callback *future.SetVoidFuture
- Segment *FileSegment
- }
-
- // 所有读写过程共用同一个CacheFile对象。
- // 不应该将此结构体保存到对象中
- type CacheFile struct {
- objDownloader *downloader.Downloader
- pathComps []string
- name string
- info FileInfo
- lock sync.Mutex
- segBuffer SegmentBuffer
- readers int
- writers int
-
- localLoaders []*LocalLoader
- remoteLoaders []*RemoteLoader
- pendingReadings []*ReadRequest
- pendingWritings []*WriteReqeust
- managerChan chan any
- }
-
- func loadCacheFile(pathComps []string, infoPath string) (*CacheFile, error) {
- f, err := os.Open(infoPath)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- info := &FileInfo{}
- err = serder.JSONToObjectStream(f, info)
- if err != nil {
- return nil, err
- }
-
- return &CacheFile{
- pathComps: pathComps,
- name: filepath.Base(infoPath),
- info: *info,
- }, nil
- }
-
- func (f *CacheFile) PathComps() []string {
- return f.pathComps
- }
-
- func (f *CacheFile) Name() string {
- return f.name
- }
-
- func (f *CacheFile) Size() int64 {
- return f.info.Size
- }
-
- func (f *CacheFile) Mode() os.FileMode {
- return f.info.Perm
- }
-
- func (f *CacheFile) ModTime() time.Time {
- return f.info.ModTime
- }
-
- func (f *CacheFile) IsDir() bool {
- return false
- }
-
- // 打开一个写入句柄,同时支持读取
- func (f *CacheFile) Open(readOnly bool) *CacheFileReadWriter {
- f.lock.Lock()
- defer f.lock.Unlock()
-
- if readOnly {
- f.readers++
- } else {
- f.writers++
- }
-
- return &CacheFileReadWriter{
- file: f,
- readOnly: readOnly,
- }
- }
-
- func (f *CacheFile) SetModTime(modTime time.Time) error {
- }
-
- func (f *CacheFile) Truncate(size int64) error {
- }
-
- // 不再使用缓存文件
- func (f *CacheFile) Release() {
-
- }
-
- // 一个文件段的数据被写入到本地了。err为nil表示成功,否则表示写入失败。
- func (f *CacheFile) OnSaved(seg *FileSegment, err error) {
-
- }
-
- // 一个文件段的数据被加载到内存了。err为nil表示成功,否则表示加载失败。
- func (f *CacheFile) OnLoaded(seg *FileSegment, err error) {
-
- }
-
- func (f *CacheFile) notifyManager() {
- select {
- case f.managerChan <- nil:
- break
- default:
- }
- }
-
- func (f *CacheFile) managing() {
- for {
- <-f.managerChan
-
- }
- }
-
- type CacheFileReadWriter struct {
- file *CacheFile
- readOnly bool
- }
-
- func (f *CacheFileReadWriter) ReadAt(buf []byte, off int64) (int, error) {
- f.file.lock.Lock()
-
- if off >= f.file.info.Size {
- return 0, io.EOF
- }
-
- segIdx := f.file.segBuffer.FirstContains(off)
-
- if segIdx >= 0 {
- seg := f.file.segBuffer.Segment(segIdx)
-
- // 读取的数据在当前段内
- if off >= seg.Position && off < seg.Position+seg.Length {
- readLen := math2.Min(int64(len(buf)), seg.Position+seg.Length-off)
- seg.RefCount++
- f.file.lock.Unlock()
-
- copy(buf[:readLen], seg.SubSliceAbs(off, readLen))
-
- f.file.lock.Lock()
- seg.RefCount--
- seg.Type = SegmentDirty
- f.file.lock.Unlock()
- return int(readLen), nil
- }
-
- // if off >= f.file.info.ObjectSize {
- // readLen := int64(len(buf))
-
- // if segIdx < f.file.segBuffer.BuzyCount()-1 {
- // nextSeg := f.file.segBuffer.Segment(segIdx + 1)
- // readLen = math2.Min(readLen, nextSeg.Position-off)
- // } else {
- // readLen = math2.Min(readLen, f.file.info.Size-off)
- // }
- // f.file.lock.Unlock()
-
- // clear(buf[:readLen])
- // return int(readLen), nil
- // }
- }
-
- // 没有被缓存的数据,则需要通知加载器去加载
-
- fut := future.NewSetVoid()
- req := &ReadRequest{
- Position: off,
- Callback: fut,
- }
- insertIdx := FirstContains(f.file.pendingReadings, off)
- f.file.pendingReadings = lo2.Insert(f.file.pendingReadings, insertIdx+1, req)
- f.file.lock.Unlock()
-
- f.file.notifyManager()
-
- err := fut.Wait(context.Background())
- if err != nil {
- return 0, err
- }
-
- if req.Segment != nil {
- // 这里不加锁,因为在引用计数大于0期间,Position不变,而Length虽然可能发生变化,但并发读写是未定义行为,所以不管读到多少数据都可以
- readLen := math2.Min(int64(len(buf)), req.Segment.Position+req.Segment.Length-off)
- copy(buf[:readLen], req.Segment.SubSliceAbs(off, readLen))
-
- // bufferManager线程会在调用回调之前,给引用计数+1
- // TODO 这种做法容易粗心产生遗漏,且不利于实现超时机制
- f.file.lock.Lock()
- req.Segment.RefCount--
- f.file.lock.Unlock()
- return int(readLen), nil
- }
-
- if req.Zeros > 0 {
- clear(buf[:req.Zeros])
- return int(req.Zeros), nil
- }
-
- return 0, io.EOF
- }
-
- func (f *CacheFileReadWriter) WriteAt(buf []byte, off int64) (int, error) {
- if f.readOnly {
- return 0, fuse.ErrPermission
- }
-
- f.file.lock.Lock()
- // 如果找到一个包含off位置的段,那么就先写满这个段
- segIdx := f.file.segBuffer.FirstContains(off)
- if segIdx >= 0 {
- seg := f.file.segBuffer.Segment(segIdx)
-
- if off >= seg.Position && off < seg.Position+seg.Length {
- writeLen := math2.Min(int64(len(buf)), seg.BufferEnd()-off)
- seg.RefCount++
- f.file.lock.Unlock()
-
- // 不管此时是不是有其他线程在读取数据,因为我们约定并发读写就是未定义行为。
- copy(seg.SubSliceAbs(off, writeLen), buf[:writeLen])
-
- f.file.lock.Lock()
- seg.RefCount--
- seg.EnlargeTo(off + writeLen)
- seg.Type = SegmentDirty
- f.file.info.EnlargeTo(seg.AvailableEnd())
- f.file.lock.Unlock()
- f.file.notifyManager()
- return int(writeLen), nil
- }
- }
-
- fut := future.NewSetVoid()
- req := &WriteReqeust{
- Position: off,
- Callback: fut,
- }
- f.file.pendingWritings = append(f.file.pendingWritings, req)
- f.file.lock.Unlock()
-
- err := fut.Wait(context.Background())
- if err != nil {
- return 0, err
- }
-
- // 一些说明参考ReadAt函数
- // 由managing线程保证文件的同一个位置只会有一个段,因此这里不考虑在copy期间又有其他线程在写同一个段导致的产生新的重复段
- writeLen := math2.Min(int64(len(buf)), req.Segment.BufferEnd()-off)
- copy(req.Segment.SubSliceAbs(off, writeLen), buf[:writeLen])
-
- f.file.lock.Lock()
- req.Segment.RefCount--
- req.Segment.EnlargeTo(off + writeLen)
- req.Segment.Type = SegmentDirty
- f.file.info.EnlargeTo(req.Segment.AvailableEnd())
- f.file.lock.Unlock()
- f.file.notifyManager()
- return int(writeLen), nil
- }
-
- func (f *CacheFileReadWriter) Sync() error {
- }
-
- func (f *CacheFileReadWriter) Close() error {
-
- }
|