github.com/matrixorigin/matrixone@v1.2.0/pkg/fileservice/local_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 "errors" 21 "io" 22 "io/fs" 23 "os" 24 pathpkg "path" 25 "path/filepath" 26 gotrace "runtime/trace" 27 "sort" 28 "strings" 29 "sync" 30 "sync/atomic" 31 "time" 32 33 "github.com/matrixorigin/matrixone/pkg/common/moerr" 34 "github.com/matrixorigin/matrixone/pkg/fileservice/memorycache" 35 "github.com/matrixorigin/matrixone/pkg/logutil" 36 "github.com/matrixorigin/matrixone/pkg/perfcounter" 37 metric "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 38 "github.com/matrixorigin/matrixone/pkg/util/trace" 39 "github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace/statistic" 40 "go.uber.org/zap" 41 ) 42 43 // LocalFS is a FileService implementation backed by local file system 44 type LocalFS struct { 45 name string 46 rootPath string 47 48 sync.RWMutex 49 dirFiles map[string]*os.File 50 51 allocator CacheDataAllocator 52 memCache *MemCache 53 diskCache *DiskCache 54 remoteCache *RemoteCache 55 asyncUpdate bool 56 57 perfCounterSets []*perfcounter.CounterSet 58 59 ioMerger *IOMerger 60 } 61 62 var _ FileService = new(LocalFS) 63 64 func NewLocalFS( 65 ctx context.Context, 66 name string, 67 rootPath string, 68 cacheConfig CacheConfig, 69 perfCounterSets []*perfcounter.CounterSet, 70 ) (*LocalFS, error) { 71 72 // get absolute path 73 if rootPath != "" { 74 var err error 75 rootPath, err = filepath.Abs(rootPath) 76 if err != nil { 77 return nil, err 78 } 79 80 // ensure dir 81 f, err := os.Open(rootPath) 82 if os.IsNotExist(err) { 83 // not exists, create 84 err := os.MkdirAll(rootPath, 0755) 85 if err != nil { 86 return nil, err 87 } 88 89 } else if err != nil { 90 // stat error 91 return nil, err 92 93 } else { 94 defer f.Close() 95 } 96 97 } 98 99 fs := &LocalFS{ 100 name: name, 101 rootPath: rootPath, 102 dirFiles: make(map[string]*os.File), 103 asyncUpdate: true, 104 perfCounterSets: perfCounterSets, 105 ioMerger: NewIOMerger(), 106 } 107 108 if err := fs.initCaches(ctx, cacheConfig); err != nil { 109 return nil, err 110 } 111 112 if fs.memCache != nil { 113 fs.allocator = fs.memCache 114 } else { 115 fs.allocator = DefaultCacheDataAllocator 116 } 117 118 return fs, nil 119 } 120 121 func (l *LocalFS) initCaches(ctx context.Context, config CacheConfig) error { 122 config.setDefaults() 123 124 if config.RemoteCacheEnabled { 125 if config.QueryClient == nil { 126 return moerr.NewInternalError(ctx, "query client is nil") 127 } 128 l.remoteCache = NewRemoteCache(config.QueryClient, config.KeyRouterFactory) 129 logutil.Info("fileservice: remote cache initialized", 130 zap.Any("fs-name", l.name), 131 ) 132 } 133 134 if *config.MemoryCapacity > DisableCacheCapacity { // 1 means disable 135 l.memCache = NewMemCache( 136 NewMemoryCache(int64(*config.MemoryCapacity), true, &config.CacheCallbacks), 137 l.perfCounterSets, 138 ) 139 logutil.Info("fileservice: memory cache initialized", 140 zap.Any("fs-name", l.name), 141 zap.Any("config", config), 142 ) 143 } 144 145 if config.enableDiskCacheForLocalFS { 146 if *config.DiskCapacity > DisableCacheCapacity && config.DiskPath != nil { 147 var err error 148 l.diskCache, err = NewDiskCache( 149 ctx, 150 *config.DiskPath, 151 int(*config.DiskCapacity), 152 l.perfCounterSets, 153 ) 154 if err != nil { 155 return err 156 } 157 logutil.Info("fileservice: disk cache initialized", 158 zap.Any("fs-name", l.name), 159 zap.Any("config", config), 160 ) 161 } 162 } 163 164 return nil 165 } 166 167 func (l *LocalFS) Name() string { 168 return l.name 169 } 170 171 func (l *LocalFS) Write(ctx context.Context, vector IOVector) error { 172 if err := ctx.Err(); err != nil { 173 return err 174 } 175 176 metric.FSWriteLocalCounter.Add(float64(len(vector.Entries))) 177 178 var err error 179 var bytesWritten int 180 start := time.Now() 181 ctx, span := trace.Start(ctx, "LocalFS.Write", trace.WithKind(trace.SpanKindLocalFSVis)) 182 defer func() { 183 // cover another func to catch the err when process Write 184 span.End(trace.WithFSReadWriteExtra(vector.FilePath, err, int64(bytesWritten))) 185 metric.LocalWriteIODurationHistogram.Observe(time.Since(start).Seconds()) 186 metric.LocalWriteIOBytesHistogram.Observe(float64(bytesWritten)) 187 }() 188 189 path, err := ParsePathAtService(vector.FilePath, l.name) 190 if err != nil { 191 return err 192 } 193 nativePath := l.toNativeFilePath(path.File) 194 195 // check existence 196 _, err = os.Stat(nativePath) 197 if err == nil { 198 // existed 199 err = moerr.NewFileAlreadyExistsNoCtx(path.File) 200 return err 201 } 202 203 bytesWritten, err = l.write(ctx, vector) 204 return err 205 } 206 207 func (l *LocalFS) write(ctx context.Context, vector IOVector) (bytesWritten int, err error) { 208 if err := ctx.Err(); err != nil { 209 return 0, err 210 } 211 212 path, err := ParsePathAtService(vector.FilePath, l.name) 213 if err != nil { 214 return 0, err 215 } 216 nativePath := l.toNativeFilePath(path.File) 217 218 // sort 219 sort.Slice(vector.Entries, func(i, j int) bool { 220 return vector.Entries[i].Offset < vector.Entries[j].Offset 221 }) 222 223 // size 224 var size int64 225 if len(vector.Entries) > 0 { 226 last := vector.Entries[len(vector.Entries)-1] 227 size = int64(last.Offset + last.Size) 228 } 229 230 // write 231 f, err := os.CreateTemp( 232 l.rootPath, 233 ".tmp.*", 234 ) 235 if err != nil { 236 return 0, err 237 } 238 fileWithChecksum, put := NewFileWithChecksumOSFile(ctx, f, _BlockContentSize, l.perfCounterSets) 239 defer put.Put() 240 241 r := newIOEntriesReader(ctx, vector.Entries) 242 243 var buf []byte 244 putBuf := ioBufferPool.Get(&buf) 245 defer putBuf.Put() 246 n, err := io.CopyBuffer(fileWithChecksum, r, buf) 247 if err != nil { 248 return 0, err 249 } 250 if n != size { 251 sizeUnknown := false 252 for _, entry := range vector.Entries { 253 if entry.Size < 0 { 254 sizeUnknown = true 255 break 256 } 257 } 258 if !sizeUnknown { 259 return 0, moerr.NewSizeNotMatchNoCtx(path.File) 260 } 261 } 262 bytesWritten = int(n) 263 if err := f.Sync(); err != nil { 264 return 0, err 265 } 266 if err := f.Close(); err != nil { 267 return 0, err 268 } 269 270 // ensure parent dir 271 parentDir, _ := filepath.Split(nativePath) 272 err = l.ensureDir(parentDir) 273 if err != nil { 274 return 0, err 275 } 276 277 // move 278 if err := os.Rename(f.Name(), nativePath); err != nil { 279 return 0, err 280 } 281 282 if err := l.syncDir(parentDir); err != nil { 283 return 0, err 284 } 285 286 return 287 } 288 289 func (l *LocalFS) Read(ctx context.Context, vector *IOVector) (err error) { 290 if err := ctx.Err(); err != nil { 291 return err 292 } 293 294 bytesCounter := new(atomic.Int64) 295 start := time.Now() 296 defer func() { 297 LocalReadIODuration := time.Since(start) 298 299 metric.LocalReadIODurationHistogram.Observe(LocalReadIODuration.Seconds()) 300 metric.LocalReadIOBytesHistogram.Observe(float64(bytesCounter.Load())) 301 }() 302 303 if len(vector.Entries) == 0 { 304 return moerr.NewEmptyVectorNoCtx() 305 } 306 307 stats := statistic.StatsInfoFromContext(ctx) 308 309 startLock := time.Now() 310 _, task := gotrace.NewTask(ctx, "LocalFS.Read: wait io") 311 done, wait := l.ioMerger.Merge(vector.ioMergeKey()) 312 if done != nil { 313 defer done() 314 } else { 315 wait() 316 } 317 task.End() 318 stats.AddLocalFSReadIOMergerTimeConsumption(time.Since(startLock)) 319 320 allocator := l.allocator 321 if vector.Policy.Any(SkipMemoryCache) { 322 allocator = DefaultCacheDataAllocator 323 } 324 for i := range vector.Entries { 325 vector.Entries[i].allocator = allocator 326 } 327 328 for _, cache := range vector.Caches { 329 cache := cache 330 if err := readCache(ctx, cache, vector); err != nil { 331 return err 332 } 333 defer func() { 334 if err != nil { 335 return 336 } 337 err = cache.Update(ctx, vector, false) 338 }() 339 } 340 341 if l.memCache != nil { 342 if err := readCache(ctx, l.memCache, vector); err != nil { 343 return err 344 } 345 defer func() { 346 if err != nil { 347 return 348 } 349 err = l.memCache.Update(ctx, vector, l.asyncUpdate) 350 }() 351 } 352 353 ioStart := time.Now() 354 defer func() { 355 stats.AddIOAccessTimeConsumption(time.Since(ioStart)) 356 }() 357 358 if l.diskCache != nil { 359 if err := readCache(ctx, l.diskCache, vector); err != nil { 360 return err 361 } 362 defer func() { 363 if err != nil { 364 return 365 } 366 err = l.diskCache.Update(ctx, vector, l.asyncUpdate) 367 }() 368 } 369 370 if l.remoteCache != nil { 371 if err := readCache(ctx, l.remoteCache, vector); err != nil { 372 return err 373 } 374 } 375 376 err = l.read(ctx, vector, bytesCounter) 377 if err != nil { 378 return err 379 } 380 381 return nil 382 } 383 384 func (l *LocalFS) ReadCache(ctx context.Context, vector *IOVector) (err error) { 385 if err := ctx.Err(); err != nil { 386 return err 387 } 388 389 ctx, span := trace.Start(ctx, "LocalFS.ReadCache") 390 defer span.End() 391 392 if len(vector.Entries) == 0 { 393 return moerr.NewEmptyVectorNoCtx() 394 } 395 396 startLock := time.Now() 397 done, wait := l.ioMerger.Merge(vector.ioMergeKey()) 398 if done != nil { 399 defer done() 400 } else { 401 wait() 402 } 403 statistic.StatsInfoFromContext(ctx).AddLocalFSReadCacheIOMergerTimeConsumption(time.Since(startLock)) 404 405 for _, cache := range vector.Caches { 406 cache := cache 407 if err := readCache(ctx, cache, vector); err != nil { 408 return err 409 } 410 defer func() { 411 if err != nil { 412 return 413 } 414 err = cache.Update(ctx, vector, false) 415 }() 416 } 417 418 if l.memCache != nil { 419 if err := readCache(ctx, l.memCache, vector); err != nil { 420 return err 421 } 422 } 423 424 return nil 425 } 426 427 func (l *LocalFS) read(ctx context.Context, vector *IOVector, bytesCounter *atomic.Int64) (err error) { 428 if vector.allDone() { 429 // all cache hit 430 return nil 431 } 432 433 path, err := ParsePathAtService(vector.FilePath, l.name) 434 if err != nil { 435 return err 436 } 437 nativePath := l.toNativeFilePath(path.File) 438 439 file, err := os.Open(nativePath) 440 if os.IsNotExist(err) { 441 return moerr.NewFileNotFoundNoCtx(path.File) 442 } 443 if err != nil { 444 return err 445 } 446 defer file.Close() 447 448 for i, entry := range vector.Entries { 449 if entry.Size == 0 { 450 return moerr.NewEmptyRangeNoCtx(path.File) 451 } 452 453 if entry.done { 454 continue 455 } 456 457 if entry.WriterForRead != nil { 458 fileWithChecksum, put := NewFileWithChecksumOSFile(ctx, file, _BlockContentSize, l.perfCounterSets) 459 defer put.Put() 460 461 if entry.Offset > 0 { 462 _, err = fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 463 if err != nil { 464 return err 465 } 466 } 467 r := (io.Reader)(fileWithChecksum) 468 if entry.Size > 0 { 469 r = io.LimitReader(r, int64(entry.Size)) 470 } 471 r = &countingReader{ 472 R: r, 473 C: bytesCounter, 474 } 475 476 if entry.ToCacheData != nil { 477 r = io.TeeReader(r, entry.WriterForRead) 478 counter := new(atomic.Int64) 479 cr := &countingReader{ 480 R: r, 481 C: counter, 482 } 483 var bs memorycache.CacheData 484 bs, err = entry.ToCacheData(cr, nil, DefaultCacheDataAllocator) 485 if err != nil { 486 return err 487 } 488 vector.Entries[i].CachedData = bs 489 if entry.Size > 0 && counter.Load() != entry.Size { 490 return moerr.NewUnexpectedEOFNoCtx(path.File) 491 } 492 493 } else { 494 var buf []byte 495 put := ioBufferPool.Get(&buf) 496 defer put.Put() 497 n, err := io.CopyBuffer(entry.WriterForRead, r, buf) 498 if err != nil { 499 return err 500 } 501 if entry.Size > 0 && n != int64(entry.Size) { 502 return moerr.NewUnexpectedEOFNoCtx(path.File) 503 } 504 } 505 506 } else if entry.ReadCloserForRead != nil { 507 file, err = os.Open(nativePath) 508 if os.IsNotExist(err) { 509 return moerr.NewFileNotFoundNoCtx(path.File) 510 } 511 if err != nil { 512 return err 513 } 514 fileWithChecksum := NewFileWithChecksum(ctx, file, _BlockContentSize, l.perfCounterSets) 515 516 if entry.Offset > 0 { 517 _, err = fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 518 if err != nil { 519 return err 520 } 521 } 522 r := (io.Reader)(fileWithChecksum) 523 if entry.Size > 0 { 524 r = io.LimitReader(r, int64(entry.Size)) 525 } 526 r = &countingReader{ 527 R: r, 528 C: bytesCounter, 529 } 530 531 if entry.ToCacheData == nil { 532 *entry.ReadCloserForRead = &readCloser{ 533 r: r, 534 closeFunc: file.Close, 535 } 536 537 } else { 538 buf := new(bytes.Buffer) 539 *entry.ReadCloserForRead = &readCloser{ 540 r: io.TeeReader(r, buf), 541 closeFunc: func() error { 542 defer file.Close() 543 var bs memorycache.CacheData 544 bs, err = entry.ToCacheData(buf, buf.Bytes(), DefaultCacheDataAllocator) 545 if err != nil { 546 return err 547 } 548 vector.Entries[i].CachedData = bs 549 return nil 550 }, 551 } 552 } 553 554 } else { 555 fileWithChecksum, put := NewFileWithChecksumOSFile(ctx, file, _BlockContentSize, l.perfCounterSets) 556 defer put.Put() 557 558 if entry.Offset > 0 { 559 _, err = fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 560 if err != nil { 561 return err 562 } 563 } 564 r := (io.Reader)(fileWithChecksum) 565 if entry.Size > 0 { 566 r = io.LimitReader(r, int64(entry.Size)) 567 } 568 r = &countingReader{ 569 R: r, 570 C: bytesCounter, 571 } 572 573 if entry.Size < 0 { 574 var data []byte 575 data, err = io.ReadAll(r) 576 if err != nil { 577 return err 578 } 579 entry.Data = data 580 entry.Size = int64(len(data)) 581 582 } else { 583 if int64(len(entry.Data)) < entry.Size { 584 entry.Data = make([]byte, entry.Size) 585 } 586 var n int 587 n, err = io.ReadFull(r, entry.Data) 588 if err != nil { 589 return err 590 } 591 if int64(n) != entry.Size { 592 return moerr.NewUnexpectedEOFNoCtx(path.File) 593 } 594 } 595 596 if err = entry.setCachedData(); err != nil { 597 return err 598 } 599 600 vector.Entries[i] = entry 601 602 } 603 604 } 605 606 return nil 607 608 } 609 610 func (l *LocalFS) List(ctx context.Context, dirPath string) (ret []DirEntry, err error) { 611 if err := ctx.Err(); err != nil { 612 return nil, err 613 } 614 615 ctx, span := trace.Start(ctx, "LocalFS.List", trace.WithKind(trace.SpanKindLocalFSVis)) 616 defer func() { 617 span.AddExtraFields([]zap.Field{zap.String("list", dirPath)}...) 618 span.End() 619 }() 620 621 _ = ctx 622 623 path, err := ParsePathAtService(dirPath, l.name) 624 if err != nil { 625 return nil, err 626 } 627 nativePath := l.toNativeFilePath(path.File) 628 629 f, err := os.Open(nativePath) 630 if os.IsNotExist(err) { 631 err = nil 632 return 633 } 634 if err != nil { 635 return nil, err 636 } 637 defer f.Close() 638 639 entries, err := f.ReadDir(-1) 640 for _, entry := range entries { 641 name := entry.Name() 642 if strings.HasPrefix(name, ".") { 643 continue 644 } 645 info, err := entry.Info() 646 if err != nil { 647 return nil, err 648 } 649 fileSize := info.Size() 650 nBlock := ceilingDiv(fileSize, _BlockSize) 651 contentSize := fileSize - _ChecksumSize*nBlock 652 653 isDir, err := entryIsDir(nativePath, name, info) 654 if err != nil { 655 return nil, err 656 } 657 ret = append(ret, DirEntry{ 658 Name: name, 659 IsDir: isDir, 660 Size: contentSize, 661 }) 662 } 663 664 sort.Slice(ret, func(i, j int) bool { 665 return ret[i].Name < ret[j].Name 666 }) 667 668 if err != nil { 669 return ret, err 670 } 671 672 return 673 } 674 675 func (l *LocalFS) StatFile(ctx context.Context, filePath string) (*DirEntry, error) { 676 if err := ctx.Err(); err != nil { 677 return nil, err 678 } 679 680 ctx, span := trace.Start(ctx, "LocalFS.StatFile", trace.WithKind(trace.SpanKindLocalFSVis)) 681 defer func() { 682 span.AddExtraFields([]zap.Field{zap.String("stat", filePath)}...) 683 span.End() 684 }() 685 686 path, err := ParsePathAtService(filePath, l.name) 687 if err != nil { 688 return nil, err 689 } 690 nativePath := l.toNativeFilePath(path.File) 691 692 stat, err := os.Stat(nativePath) 693 if os.IsNotExist(err) { 694 return nil, moerr.NewFileNotFound(ctx, filePath) 695 } 696 697 if stat.IsDir() { 698 return nil, moerr.NewFileNotFound(ctx, filePath) 699 } 700 701 fileSize := stat.Size() 702 nBlock := ceilingDiv(fileSize, _BlockSize) 703 contentSize := fileSize - _ChecksumSize*nBlock 704 705 return &DirEntry{ 706 Name: pathpkg.Base(filePath), 707 IsDir: false, 708 Size: contentSize, 709 }, nil 710 } 711 712 func (l *LocalFS) PrefetchFile(ctx context.Context, filePath string) error { 713 return nil 714 } 715 716 func (l *LocalFS) Delete(ctx context.Context, filePaths ...string) error { 717 if err := ctx.Err(); err != nil { 718 return err 719 } 720 721 ctx, span := trace.Start(ctx, "LocalFS.Delete", trace.WithKind(trace.SpanKindLocalFSVis)) 722 defer func() { 723 span.AddExtraFields([]zap.Field{zap.String("delete", strings.Join(filePaths, "|"))}...) 724 span.End() 725 }() 726 727 for _, filePath := range filePaths { 728 if err := l.deleteSingle(ctx, filePath); err != nil { 729 return err 730 } 731 } 732 733 return errors.Join( 734 func() error { 735 if l.memCache == nil { 736 return nil 737 } 738 return l.memCache.DeletePaths(ctx, filePaths) 739 }(), 740 func() error { 741 if l.diskCache == nil { 742 return nil 743 } 744 return l.diskCache.DeletePaths(ctx, filePaths) 745 }(), 746 func() error { 747 if l.remoteCache == nil { 748 return nil 749 } 750 return l.remoteCache.DeletePaths(ctx, filePaths) 751 }(), 752 ) 753 } 754 755 func (l *LocalFS) deleteSingle(_ context.Context, filePath string) error { 756 path, err := ParsePathAtService(filePath, l.name) 757 if err != nil { 758 return err 759 } 760 nativePath := l.toNativeFilePath(path.File) 761 762 _, err = os.Stat(nativePath) 763 if err != nil { 764 if os.IsNotExist(err) { 765 // ignore not found error 766 return nil 767 } 768 return err 769 } 770 771 err = os.Remove(nativePath) 772 if err != nil { 773 return err 774 } 775 776 parentDir, _ := filepath.Split(nativePath) 777 err = l.syncDir(parentDir) 778 if err != nil { 779 return err 780 } 781 782 return nil 783 } 784 785 func (l *LocalFS) ensureDir(nativePath string) error { 786 nativePath = filepath.Clean(nativePath) 787 if nativePath == "" { 788 return nil 789 } 790 791 // check existence by l.dirFiles 792 l.RLock() 793 _, ok := l.dirFiles[nativePath] 794 if ok { 795 // dir existed 796 l.RUnlock() 797 return nil 798 } 799 l.RUnlock() 800 801 // check existence by fstat 802 _, err := os.Stat(nativePath) 803 if err == nil { 804 // existed 805 return nil 806 } 807 808 // ensure parent 809 parent, _ := filepath.Split(nativePath) 810 if parent != nativePath { 811 if err := l.ensureDir(parent); err != nil { 812 return err 813 } 814 } 815 816 // create 817 if err := os.Mkdir(nativePath, 0755); err != nil { 818 if os.IsExist(err) { 819 // existed 820 return nil 821 } 822 return err 823 } 824 825 // sync parent dir 826 if err := l.syncDir(parent); err != nil { 827 return err 828 } 829 830 return nil 831 } 832 833 func (l *LocalFS) syncDir(nativePath string) error { 834 l.Lock() 835 f, ok := l.dirFiles[nativePath] 836 if !ok { 837 var err error 838 f, err = os.Open(nativePath) 839 if err != nil { 840 l.Unlock() 841 return err 842 } 843 l.dirFiles[nativePath] = f 844 } 845 l.Unlock() 846 if err := f.Sync(); err != nil { 847 return err 848 } 849 return nil 850 } 851 852 func (l *LocalFS) toNativeFilePath(filePath string) string { 853 return filepath.Join(l.rootPath, toOSPath(filePath)) 854 } 855 856 var _ MutableFileService = new(LocalFS) 857 858 func (l *LocalFS) NewMutator(ctx context.Context, filePath string) (Mutator, error) { 859 path, err := ParsePathAtService(filePath, l.name) 860 if err != nil { 861 return nil, err 862 } 863 nativePath := l.toNativeFilePath(path.File) 864 f, err := os.OpenFile(nativePath, os.O_RDWR, 0644) 865 if os.IsNotExist(err) { 866 return nil, moerr.NewFileNotFoundNoCtx(path.File) 867 } 868 return &LocalFSMutator{ 869 osFile: f, 870 fileWithChecksum: NewFileWithChecksum(ctx, f, _BlockContentSize, l.perfCounterSets), 871 }, nil 872 } 873 874 type LocalFSMutator struct { 875 osFile *os.File 876 fileWithChecksum *FileWithChecksum[*os.File] 877 } 878 879 func (l *LocalFSMutator) Mutate(ctx context.Context, entries ...IOEntry) error { 880 881 ctx, span := trace.Start(ctx, "LocalFS.Mutate") 882 defer span.End() 883 884 return l.mutate(ctx, 0, entries...) 885 } 886 887 func (l *LocalFSMutator) Append(ctx context.Context, entries ...IOEntry) error { 888 ctx, span := trace.Start(ctx, "LocalFS.Append") 889 defer span.End() 890 891 offset, err := l.fileWithChecksum.Seek(0, io.SeekEnd) 892 if err != nil { 893 return err 894 } 895 return l.mutate(ctx, offset, entries...) 896 } 897 898 func (l *LocalFSMutator) mutate(ctx context.Context, baseOffset int64, entries ...IOEntry) error { 899 if err := ctx.Err(); err != nil { 900 return err 901 } 902 903 // write 904 for _, entry := range entries { 905 906 if entry.ReaderForWrite != nil { 907 // seek and copy 908 _, err := l.fileWithChecksum.Seek(entry.Offset+baseOffset, 0) 909 if err != nil { 910 return err 911 } 912 var buf []byte 913 put := ioBufferPool.Get(&buf) 914 defer put.Put() 915 n, err := io.CopyBuffer(l.fileWithChecksum, entry.ReaderForWrite, buf) 916 if err != nil { 917 return err 918 } 919 if int64(n) != entry.Size { 920 return moerr.NewSizeNotMatchNoCtx("") 921 } 922 923 } else { 924 // WriteAt 925 n, err := l.fileWithChecksum.WriteAt(entry.Data, int64(entry.Offset+baseOffset)) 926 if err != nil { 927 return err 928 } 929 if int64(n) != entry.Size { 930 return moerr.NewSizeNotMatchNoCtx("") 931 } 932 } 933 } 934 935 return nil 936 } 937 938 func (l *LocalFSMutator) Close() error { 939 // sync 940 if err := l.osFile.Sync(); err != nil { 941 return err 942 } 943 944 // close 945 if err := l.osFile.Close(); err != nil { 946 return err 947 } 948 949 return nil 950 } 951 952 var _ ReplaceableFileService = new(LocalFS) 953 954 func (l *LocalFS) Replace(ctx context.Context, vector IOVector) error { 955 ctx, span := trace.Start(ctx, "LocalFS.Replace") 956 defer span.End() 957 _, err := l.write(ctx, vector) 958 if err != nil { 959 return err 960 } 961 return nil 962 } 963 964 var _ CachingFileService = new(LocalFS) 965 966 func (l *LocalFS) Close() { 967 l.FlushCache() 968 } 969 970 func (l *LocalFS) FlushCache() { 971 if l.memCache != nil { 972 l.memCache.Flush() 973 } 974 if l.diskCache != nil { 975 l.diskCache.Flush() 976 } 977 } 978 979 func (l *LocalFS) SetAsyncUpdate(b bool) { 980 l.asyncUpdate = b 981 } 982 983 func entryIsDir(path string, name string, entry fs.FileInfo) (bool, error) { 984 if entry.IsDir() { 985 return true, nil 986 } 987 if entry.Mode().Type()&fs.ModeSymlink > 0 { 988 stat, err := os.Stat(filepath.Join(path, name)) 989 if err != nil { 990 if os.IsNotExist(err) { 991 // invalid sym link 992 return false, nil 993 } 994 return false, err 995 } 996 return entryIsDir(path, name, stat) 997 } 998 return false, nil 999 }