19428: Avoid locking entire filesystem for MemorySize.
authorTom Clegg <tom@curii.com>
Mon, 29 Aug 2022 21:01:08 +0000 (17:01 -0400)
committerTom Clegg <tom@curii.com>
Tue, 30 Aug 2022 14:19:54 +0000 (10:19 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

sdk/go/arvados/fs_base.go
sdk/go/arvados/fs_collection.go

index 2ad4d1f859f1141035c04cb4180c5ef623d1fa04..6da639edaf73c3731501246de556f1c7c1932e16 100644 (file)
@@ -420,10 +420,20 @@ func (n *treenode) Sync() error {
 }
 
 func (n *treenode) MemorySize() (size int64) {
+       // To avoid making other callers wait while we count the
+       // entire filesystem size, we lock the node only long enough
+       // to copy the list of children. We accept that the resulting
+       // size will sometimes be misleading (e.g., we will
+       // double-count an item that moves from A to B after we check
+       // A's size but before we check B's size).
        n.RLock()
-       defer n.RUnlock()
        debugPanicIfNotLocked(n, false)
+       todo := make([]inode, 0, len(n.inodes))
        for _, inode := range n.inodes {
+               todo = append(todo, inode)
+       }
+       n.RUnlock()
+       for _, inode := range todo {
                size += inode.MemorySize()
        }
        return 64 + size
index 26012e240603d0be43a1019346c4e946e2821790..759a0497e2f937c0e6178609103b58a18237000d 100644 (file)
@@ -443,8 +443,8 @@ func (fs *collectionFileSystem) Flush(path string, shortBlocks bool) error {
 }
 
 func (fs *collectionFileSystem) MemorySize() int64 {
-       fs.fileSystem.root.Lock()
-       defer fs.fileSystem.root.Unlock()
+       fs.fileSystem.root.RLock()
+       defer fs.fileSystem.root.RUnlock()
        return fs.fileSystem.root.(*dirnode).MemorySize()
 }
 
@@ -1154,8 +1154,7 @@ func (dn *dirnode) flush(ctx context.Context, names []string, opts flushOpts) er
 func (dn *dirnode) MemorySize() (size int64) {
        for _, name := range dn.sortedNames() {
                node := dn.inodes[name]
-               node.Lock()
-               defer node.Unlock()
+               node.RLock()
                switch node := node.(type) {
                case *dirnode:
                        size += node.MemorySize()
@@ -1169,6 +1168,7 @@ func (dn *dirnode) MemorySize() (size int64) {
                                size += 64
                        }
                }
+               node.RUnlock()
        }
        return 64 + size
 }