diff --git a/weed/filer/filer_inode_index.go b/weed/filer/filer_inode_index.go index 04e487ffbd..83dd6f9bdb 100644 --- a/weed/filer/filer_inode_index.go +++ b/weed/filer/filer_inode_index.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/json" "sort" + "sync" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/util" @@ -13,6 +14,14 @@ import ( const inodeIndexKeyPrefix = "filer.inode.path." const InodeIndexInitialGeneration uint64 = 1 +// inodeRowLocks serializes the read-modify-write of one inode's index row so +// concurrent writers (hard links share an inode) cannot overwrite each other. +var inodeRowLocks [256]sync.Mutex + +func inodeRowLock(inode uint64) *sync.Mutex { + return &inodeRowLocks[inode%uint64(len(inodeRowLocks))] +} + type inodeIndexEntry struct { path util.FullPath inode uint64 @@ -190,6 +199,10 @@ func (fsw *FilerStoreWrapper) storeInodeIndex(ctx context.Context, path util.Ful return nil } + lock := inodeRowLock(inode) + lock.Lock() + defer lock.Unlock() + record, err := fsw.lookupInodeIndex(ctx, inode) if err != nil { if err != ErrKvNotFound { @@ -237,6 +250,10 @@ func (fsw *FilerStoreWrapper) removePathFromInodeIndex(ctx context.Context, path return nil } + lock := inodeRowLock(inode) + lock.Lock() + defer lock.Unlock() + record, err := fsw.lookupInodeIndex(ctx, inode) if err != nil { if err == ErrKvNotFound {