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