github.com/matrixorigin/matrixone@v0.7.0/pkg/fileservice/memory_fs.go (about)

     1  // Copyright 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 fileservice
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"io"
    21  	pathpkg "path"
    22  	"sort"
    23  	"strings"
    24  	"sync"
    25  
    26  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    27  	"github.com/tidwall/btree"
    28  )
    29  
    30  // MemoryFS is an in-memory FileService implementation
    31  type MemoryFS struct {
    32  	name string
    33  	sync.RWMutex
    34  	tree *btree.Generic[*_MemFSEntry]
    35  }
    36  
    37  var _ FileService = new(MemoryFS)
    38  
    39  func NewMemoryFS(name string) (*MemoryFS, error) {
    40  	return &MemoryFS{
    41  		name: name,
    42  		tree: btree.NewGeneric(func(a, b *_MemFSEntry) bool {
    43  			return a.FilePath < b.FilePath
    44  		}),
    45  	}, nil
    46  }
    47  
    48  func (m *MemoryFS) Name() string {
    49  	return m.name
    50  }
    51  
    52  func (m *MemoryFS) List(ctx context.Context, dirPath string) (entries []DirEntry, err error) {
    53  	select {
    54  	case <-ctx.Done():
    55  		return nil, ctx.Err()
    56  	default:
    57  	}
    58  
    59  	m.RLock()
    60  	defer m.RUnlock()
    61  
    62  	path, err := ParsePathAtService(dirPath, m.name)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	iter := m.tree.Iter()
    68  	defer iter.Release()
    69  
    70  	pivot := &_MemFSEntry{
    71  		FilePath: path.File,
    72  	}
    73  	for ok := iter.Seek(pivot); ok; ok = iter.Next() {
    74  		item := iter.Item()
    75  		if !strings.HasPrefix(item.FilePath, path.File) {
    76  			break
    77  		}
    78  
    79  		relPath := strings.TrimPrefix(item.FilePath, path.File)
    80  		relPath = strings.Trim(relPath, "/")
    81  		parts := strings.Split(relPath, "/")
    82  		isDir := len(parts) > 1
    83  		name := parts[0]
    84  
    85  		if len(entries) == 0 || entries[len(entries)-1].Name != name {
    86  			entries = append(entries, DirEntry{
    87  				IsDir: isDir,
    88  				Name:  name,
    89  				Size:  int64(len(item.Data)),
    90  			})
    91  		}
    92  	}
    93  
    94  	return
    95  }
    96  
    97  func (m *MemoryFS) Write(ctx context.Context, vector IOVector) error {
    98  	select {
    99  	case <-ctx.Done():
   100  		return ctx.Err()
   101  	default:
   102  	}
   103  
   104  	m.Lock()
   105  	defer m.Unlock()
   106  
   107  	path, err := ParsePathAtService(vector.FilePath, m.name)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	pivot := &_MemFSEntry{
   113  		FilePath: path.File,
   114  	}
   115  	_, ok := m.tree.Get(pivot)
   116  	if ok {
   117  		return moerr.NewFileAlreadyExistsNoCtx(path.File)
   118  	}
   119  
   120  	return m.write(ctx, vector)
   121  }
   122  
   123  func (m *MemoryFS) write(ctx context.Context, vector IOVector) error {
   124  	select {
   125  	case <-ctx.Done():
   126  		return ctx.Err()
   127  	default:
   128  	}
   129  
   130  	path, err := ParsePathAtService(vector.FilePath, m.name)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	if len(vector.Entries) == 0 {
   136  		vector.Entries = []IOEntry{
   137  			{
   138  				Offset: 0,
   139  				Size:   0,
   140  				Data:   nil,
   141  			},
   142  		}
   143  	}
   144  
   145  	sort.Slice(vector.Entries, func(i, j int) bool {
   146  		return vector.Entries[i].Offset < vector.Entries[j].Offset
   147  	})
   148  
   149  	r := newIOEntriesReader(ctx, vector.Entries)
   150  	data, err := io.ReadAll(r)
   151  	if err != nil {
   152  		return err
   153  	}
   154  	entry := &_MemFSEntry{
   155  		FilePath: path.File,
   156  		Data:     data,
   157  	}
   158  	m.tree.Set(entry)
   159  
   160  	return nil
   161  }
   162  
   163  func (m *MemoryFS) Read(ctx context.Context, vector *IOVector) error {
   164  	select {
   165  	case <-ctx.Done():
   166  		return ctx.Err()
   167  	default:
   168  	}
   169  
   170  	path, err := ParsePathAtService(vector.FilePath, m.name)
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	if len(vector.Entries) == 0 {
   176  		return moerr.NewEmptyVectorNoCtx()
   177  	}
   178  
   179  	m.RLock()
   180  	defer m.RUnlock()
   181  
   182  	pivot := &_MemFSEntry{
   183  		FilePath: path.File,
   184  	}
   185  
   186  	fsEntry, ok := m.tree.Get(pivot)
   187  	if !ok {
   188  		return moerr.NewFileNotFoundNoCtx(path.File)
   189  	}
   190  
   191  	for i, entry := range vector.Entries {
   192  		if entry.done {
   193  			continue
   194  		}
   195  
   196  		if entry.Size == 0 {
   197  			return moerr.NewEmptyRangeNoCtx(path.File)
   198  		}
   199  		if entry.Size < 0 {
   200  			entry.Size = int64(len(fsEntry.Data)) - entry.Offset
   201  		}
   202  		if entry.Size > int64(len(fsEntry.Data)) {
   203  			return moerr.NewUnexpectedEOFNoCtx(path.File)
   204  		}
   205  		data := fsEntry.Data[entry.Offset : entry.Offset+entry.Size]
   206  
   207  		setData := true
   208  
   209  		if w := vector.Entries[i].WriterForRead; w != nil {
   210  			setData = false
   211  			_, err := w.Write(data)
   212  			if err != nil {
   213  				return err
   214  			}
   215  		}
   216  
   217  		if ptr := vector.Entries[i].ReadCloserForRead; ptr != nil {
   218  			setData = false
   219  			*ptr = io.NopCloser(bytes.NewReader(data))
   220  		}
   221  
   222  		if setData {
   223  			if int64(len(entry.Data)) < entry.Size || entry.Size < 0 {
   224  				entry.Data = data
   225  				if entry.Size < 0 {
   226  					entry.Size = int64(len(data))
   227  				}
   228  			} else {
   229  				copy(entry.Data, data)
   230  			}
   231  		}
   232  
   233  		if err := entry.setObjectFromData(); err != nil {
   234  			return err
   235  		}
   236  
   237  		vector.Entries[i] = entry
   238  	}
   239  
   240  	return nil
   241  }
   242  
   243  func (m *MemoryFS) StatFile(ctx context.Context, filePath string) (*DirEntry, error) {
   244  	select {
   245  	case <-ctx.Done():
   246  		return nil, ctx.Err()
   247  	default:
   248  	}
   249  
   250  	path, err := ParsePathAtService(filePath, m.name)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	m.RLock()
   256  	defer m.RUnlock()
   257  
   258  	pivot := &_MemFSEntry{
   259  		FilePath: path.File,
   260  	}
   261  
   262  	fsEntry, ok := m.tree.Get(pivot)
   263  	if !ok {
   264  		return nil, moerr.NewFileNotFoundNoCtx(path.File)
   265  	}
   266  
   267  	return &DirEntry{
   268  		Name:  pathpkg.Base(filePath),
   269  		IsDir: false,
   270  		Size:  int64(len(fsEntry.Data)),
   271  	}, nil
   272  }
   273  
   274  func (m *MemoryFS) Delete(ctx context.Context, filePaths ...string) error {
   275  	select {
   276  	case <-ctx.Done():
   277  		return ctx.Err()
   278  	default:
   279  	}
   280  
   281  	m.Lock()
   282  	defer m.Unlock()
   283  	for _, filePath := range filePaths {
   284  		if err := m.deleteSingle(ctx, filePath); err != nil {
   285  			return err
   286  		}
   287  	}
   288  	return nil
   289  }
   290  
   291  func (m *MemoryFS) deleteSingle(ctx context.Context, filePath string) error {
   292  
   293  	path, err := ParsePathAtService(filePath, m.name)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	pivot := &_MemFSEntry{
   299  		FilePath: path.File,
   300  	}
   301  	m.tree.Delete(pivot)
   302  
   303  	return nil
   304  }
   305  
   306  type _MemFSEntry struct {
   307  	FilePath string
   308  	Data     []byte
   309  }
   310  
   311  var _ ReplaceableFileService = new(MemoryFS)
   312  
   313  func (m *MemoryFS) Replace(ctx context.Context, vector IOVector) error {
   314  	m.Lock()
   315  	defer m.Unlock()
   316  	return m.write(ctx, vector)
   317  }
   318  
   319  // mark MemoryFS as ETL-compatible to use it as ETL fs in testing
   320  var _ ETLFileService = new(MemoryFS)
   321  
   322  func (m *MemoryFS) ETLCompatible() {}