You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							174 lines
						
					
					
						
							4.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							174 lines
						
					
					
						
							4.5 KiB
						
					
					
				| package mount | |
| 
 | |
| import ( | |
| 	"os" | |
| 	"sync" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/weed/filer" | |
| 	"github.com/seaweedfs/seaweedfs/weed/glog" | |
| 	"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" | |
| 	"github.com/seaweedfs/seaweedfs/weed/util" | |
| ) | |
| 
 | |
| type FileHandleId uint64 | |
| 
 | |
| var IsDebugFileReadWrite = false | |
| 
 | |
| type FileHandle struct { | |
| 	fh              FileHandleId | |
| 	counter         int64 | |
| 	entry           *LockedEntry | |
| 	entryLock       sync.RWMutex | |
| 	entryChunkGroup *filer.ChunkGroup | |
| 	inode           uint64 | |
| 	wfs             *WFS | |
| 
 | |
| 	// cache file has been written to | |
| 	dirtyMetadata bool | |
| 	dirtyPages    *PageWriter | |
| 	reader        *filer.ChunkReadAt | |
| 	contentType   string | |
| 
 | |
| 	isDeleted bool | |
| 
 | |
| 	// RDMA chunk offset cache for performance optimization | |
| 	chunkOffsetCache []int64 | |
| 	chunkCacheValid  bool | |
| 	chunkCacheLock   sync.RWMutex | |
| 
 | |
| 	// for debugging | |
| 	mirrorFile *os.File | |
| } | |
| 
 | |
| func newFileHandle(wfs *WFS, handleId FileHandleId, inode uint64, entry *filer_pb.Entry) *FileHandle { | |
| 	fh := &FileHandle{ | |
| 		fh:      handleId, | |
| 		counter: 1, | |
| 		inode:   inode, | |
| 		wfs:     wfs, | |
| 	} | |
| 	// dirtyPages: newContinuousDirtyPages(file, writeOnly), | |
| 	fh.dirtyPages = newPageWriter(fh, wfs.option.ChunkSizeLimit) | |
| 	fh.entry = &LockedEntry{ | |
| 		Entry: entry, | |
| 	} | |
| 	if entry != nil { | |
| 		fh.SetEntry(entry) | |
| 	} | |
| 
 | |
| 	if IsDebugFileReadWrite { | |
| 		var err error | |
| 		fh.mirrorFile, err = os.OpenFile("/tmp/sw/"+entry.Name, os.O_RDWR|os.O_CREATE, 0600) | |
| 		if err != nil { | |
| 			println("failed to create mirror:", err.Error()) | |
| 		} | |
| 	} | |
| 
 | |
| 	return fh | |
| } | |
| 
 | |
| func (fh *FileHandle) FullPath() util.FullPath { | |
| 	fp, _ := fh.wfs.inodeToPath.GetPath(fh.inode) | |
| 	return fp | |
| } | |
| 
 | |
| func (fh *FileHandle) GetEntry() *LockedEntry { | |
| 	return fh.entry | |
| } | |
| 
 | |
| func (fh *FileHandle) SetEntry(entry *filer_pb.Entry) { | |
| 	if entry != nil { | |
| 		fileSize := filer.FileSize(entry) | |
| 		entry.Attributes.FileSize = fileSize | |
| 		var resolveManifestErr error | |
| 		fh.entryChunkGroup, resolveManifestErr = filer.NewChunkGroup(fh.wfs.LookupFn(), fh.wfs.chunkCache, entry.Chunks) | |
| 		if resolveManifestErr != nil { | |
| 			glog.Warningf("failed to resolve manifest chunks in %+v", entry) | |
| 		} | |
| 	} else { | |
| 		glog.Fatalf("setting file handle entry to nil") | |
| 	} | |
| 	fh.entry.SetEntry(entry) | |
| 
 | |
| 	// Invalidate chunk offset cache since chunks may have changed | |
| 	fh.invalidateChunkCache() | |
| } | |
| 
 | |
| func (fh *FileHandle) UpdateEntry(fn func(entry *filer_pb.Entry)) *filer_pb.Entry { | |
| 	result := fh.entry.UpdateEntry(fn) | |
| 
 | |
| 	// Invalidate chunk offset cache since entry may have been modified | |
| 	fh.invalidateChunkCache() | |
| 
 | |
| 	return result | |
| } | |
| 
 | |
| func (fh *FileHandle) AddChunks(chunks []*filer_pb.FileChunk) { | |
| 	fh.entry.AppendChunks(chunks) | |
| 
 | |
| 	// Invalidate chunk offset cache since new chunks were added | |
| 	fh.invalidateChunkCache() | |
| } | |
| 
 | |
| func (fh *FileHandle) ReleaseHandle() { | |
| 
 | |
| 	fhActiveLock := fh.wfs.fhLockTable.AcquireLock("ReleaseHandle", fh.fh, util.ExclusiveLock) | |
| 	defer fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock) | |
| 
 | |
| 	fh.dirtyPages.Destroy() | |
| 	if IsDebugFileReadWrite { | |
| 		fh.mirrorFile.Close() | |
| 	} | |
| } | |
| 
 | |
| func lessThan(a, b *filer_pb.FileChunk) bool { | |
| 	if a.ModifiedTsNs == b.ModifiedTsNs { | |
| 		return a.Fid.FileKey < b.Fid.FileKey | |
| 	} | |
| 	return a.ModifiedTsNs < b.ModifiedTsNs | |
| } | |
| 
 | |
| // getCumulativeOffsets returns cached cumulative offsets for chunks, computing them if necessary | |
| func (fh *FileHandle) getCumulativeOffsets(chunks []*filer_pb.FileChunk) []int64 { | |
| 	fh.chunkCacheLock.RLock() | |
| 	if fh.chunkCacheValid && len(fh.chunkOffsetCache) == len(chunks)+1 { | |
| 		// Cache is valid and matches current chunk count | |
| 		result := make([]int64, len(fh.chunkOffsetCache)) | |
| 		copy(result, fh.chunkOffsetCache) | |
| 		fh.chunkCacheLock.RUnlock() | |
| 		return result | |
| 	} | |
| 	fh.chunkCacheLock.RUnlock() | |
| 
 | |
| 	// Need to compute/recompute cache | |
| 	fh.chunkCacheLock.Lock() | |
| 	defer fh.chunkCacheLock.Unlock() | |
| 
 | |
| 	// Double-check in case another goroutine computed it while we waited for the lock | |
| 	if fh.chunkCacheValid && len(fh.chunkOffsetCache) == len(chunks)+1 { | |
| 		result := make([]int64, len(fh.chunkOffsetCache)) | |
| 		copy(result, fh.chunkOffsetCache) | |
| 		return result | |
| 	} | |
| 
 | |
| 	// Compute cumulative offsets | |
| 	cumulativeOffsets := make([]int64, len(chunks)+1) | |
| 	for i, chunk := range chunks { | |
| 		cumulativeOffsets[i+1] = cumulativeOffsets[i] + int64(chunk.Size) | |
| 	} | |
| 
 | |
| 	// Cache the result | |
| 	fh.chunkOffsetCache = make([]int64, len(cumulativeOffsets)) | |
| 	copy(fh.chunkOffsetCache, cumulativeOffsets) | |
| 	fh.chunkCacheValid = true | |
| 
 | |
| 	return cumulativeOffsets | |
| } | |
| 
 | |
| // invalidateChunkCache invalidates the chunk offset cache when chunks are modified | |
| func (fh *FileHandle) invalidateChunkCache() { | |
| 	fh.chunkCacheLock.Lock() | |
| 	fh.chunkCacheValid = false | |
| 	fh.chunkOffsetCache = nil | |
| 	fh.chunkCacheLock.Unlock() | |
| }
 |