[ARVADOS] created: cf4baf70d54b20fd232f5eecaae435df295d8f46

Git user git at public.curoverse.com
Wed Sep 21 16:10:49 EDT 2016


        at  cf4baf70d54b20fd232f5eecaae435df295d8f46 (commit)


commit cf4baf70d54b20fd232f5eecaae435df295d8f46
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date:   Wed Sep 21 15:51:34 2016 -0400

    10117: OrderedDict preserves insertion order, so use inode as key instead of cache_priority.

diff --git a/apps/workbench/Gemfile.lock b/apps/workbench/Gemfile.lock
index a8431a7..9d04708 100644
--- a/apps/workbench/Gemfile.lock
+++ b/apps/workbench/Gemfile.lock
@@ -318,6 +318,3 @@ DEPENDENCIES
   therubyracer
   uglifier (>= 1.0.3)
   wiselinks
-
-BUNDLED WITH
-   1.12.1
diff --git a/services/api/db/structure.sql b/services/api/db/structure.sql
index 1c7363d..5f80fb2 100644
--- a/services/api/db/structure.sql
+++ b/services/api/db/structure.sql
@@ -2680,8 +2680,6 @@ INSERT INTO schema_migrations (version) VALUES ('20160506175108');
 
 INSERT INTO schema_migrations (version) VALUES ('20160509143250');
 
-INSERT INTO schema_migrations (version) VALUES ('20160808151459');
-
 INSERT INTO schema_migrations (version) VALUES ('20160808151559');
 
 INSERT INTO schema_migrations (version) VALUES ('20160819195557');
diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py
index ba70959..9ef6fa2 100644
--- a/services/fuse/arvados_fuse/__init__.py
+++ b/services/fuse/arvados_fuse/__init__.py
@@ -134,7 +134,6 @@ class InodeCache(object):
     def __init__(self, cap, min_entries=4):
         self._entries = collections.OrderedDict()
         self._by_uuid = {}
-        self._counter = itertools.count(0)
         self.cap = cap
         self._total = 0
         self.min_entries = min_entries
@@ -153,7 +152,7 @@ class InodeCache(object):
                 return False
             obj.clear()
         self._total -= obj.cache_size
-        del self._entries[obj.cache_priority]
+        del self._entries[obj.inode]
         if obj.cache_uuid:
             self._by_uuid[obj.cache_uuid].remove(obj)
             if not self._by_uuid[obj.cache_uuid]:
@@ -165,16 +164,15 @@ class InodeCache(object):
 
     def cap_cache(self):
         if self._total > self.cap:
-            for key in list(self._entries.keys()):
+            for ent in self._entries.values():
                 if self._total < self.cap or len(self._entries) < self.min_entries:
                     break
-                self._remove(self._entries[key], True)
+                self._remove(ent, True)
 
     def manage(self, obj):
         if obj.persisted():
-            obj.cache_priority = next(self._counter)
             obj.cache_size = obj.objsize()
-            self._entries[obj.cache_priority] = obj
+            self._entries[obj.inode] = obj
             obj.cache_uuid = obj.uuid()
             if obj.cache_uuid:
                 if obj.cache_uuid not in self._by_uuid:
@@ -185,17 +183,15 @@ class InodeCache(object):
             self._total += obj.objsize()
             _logger.debug("InodeCache touched inode %i (size %i) (uuid %s) total now %i", obj.inode, obj.objsize(), obj.cache_uuid, self._total)
             self.cap_cache()
-        else:
-            obj.cache_priority = None
 
     def touch(self, obj):
         if obj.persisted():
-            if obj.cache_priority in self._entries:
+            if obj.inode in self._entries:
                 self._remove(obj, False)
             self.manage(obj)
 
     def unmanage(self, obj):
-        if obj.persisted() and obj.cache_priority in self._entries:
+        if obj.persisted() and obj.inode in self._entries:
             self._remove(obj, True)
 
     def find_by_uuid(self, uuid):
diff --git a/services/fuse/arvados_fuse/fresh.py b/services/fuse/arvados_fuse/fresh.py
index 532f110..43f53c9 100644
--- a/services/fuse/arvados_fuse/fresh.py
+++ b/services/fuse/arvados_fuse/fresh.py
@@ -64,7 +64,6 @@ class FreshBase(object):
         self.use_count = 0
         self.ref_count = 0
         self.dead = False
-        self.cache_priority = None
         self.cache_size = 0
         self.cache_uuid = None
         self.allow_attr_cache = True

commit 232cace27f60dbac0f46a9113a4b19a561689f4c
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date:   Wed Sep 21 14:55:53 2016 -0400

    10117: Restore original semantics of invalidate() and add code comment, add
    kernel_invalidate().

diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py
index e679a23..ba70959 100644
--- a/services/fuse/arvados_fuse/__init__.py
+++ b/services/fuse/arvados_fuse/__init__.py
@@ -148,8 +148,8 @@ class InodeCache(object):
                 _logger.debug("InodeCache cannot clear inode %i, in use", obj.inode)
                 return False
             if obj.has_ref(only_children=True):
-                obj.invalidate()
-                _logger.debug("InodeCache invalidate inode %i", obj.inode)
+                obj.kernel_invalidate()
+                _logger.debug("InodeCache sent kernel invalidate inode %i", obj.inode)
                 return False
             obj.clear()
         self._total -= obj.cache_size
diff --git a/services/fuse/arvados_fuse/fresh.py b/services/fuse/arvados_fuse/fresh.py
index 95dde2b..532f110 100644
--- a/services/fuse/arvados_fuse/fresh.py
+++ b/services/fuse/arvados_fuse/fresh.py
@@ -70,10 +70,14 @@ class FreshBase(object):
         self.allow_attr_cache = True
         self.allow_dirent_cache = True
 
-    # Mark the value as stale
     def invalidate(self):
+        """Indicate that object contents should be refreshed from source."""
         self._stale = True
 
+    def kernel_invalidate(self):
+        """Indicate that an invalidation for this object should be sent to the kernel."""
+        pass
+
     # Test if the entries dict is stale.
     def stale(self):
         if self._stale:
diff --git a/services/fuse/arvados_fuse/fusedir.py b/services/fuse/arvados_fuse/fusedir.py
index 38fe32c..15f7564 100644
--- a/services/fuse/arvados_fuse/fusedir.py
+++ b/services/fuse/arvados_fuse/fusedir.py
@@ -183,15 +183,11 @@ class Directory(FreshBase):
         self.inodes.invalidate_inode(self.inode)
         self.invalidate()
 
-    def invalidate(self):
-        try:
-            super(Directory, self).invalidate()
-            for n, e in self._entries.iteritems():
-                self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
-                e.invalidate()
-            self.inodes.invalidate_inode(self.inode)
-        except Exception:
-            _logger.exception()
+    def kernel_invalidate(self):
+        for n, e in self._entries.iteritems():
+            self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
+            e.kernel_invalidate()
+        self.inodes.invalidate_inode(self.inode)
 
     def mtime(self):
         return self._mtime
@@ -339,10 +335,8 @@ class CollectionDirectoryBase(Directory):
         src.flush()
 
     def clear(self):
-        r = super(CollectionDirectoryBase, self).clear()
+        super(CollectionDirectoryBase, self).clear()
         self.collection = None
-        self._manifest_size = 0
-        return r
 
 
 class CollectionDirectory(CollectionDirectoryBase):
@@ -512,6 +506,10 @@ class CollectionDirectory(CollectionDirectoryBase):
                 self.collection.save()
             self.collection.stop_threads()
 
+    def clear(self):
+        super(CollectionDirectory, self).clear()
+        self._manifest_size = 0
+
 
 class TmpCollectionDirectory(CollectionDirectoryBase):
     """A directory backed by an Arvados collection that never gets saved.
diff --git a/services/fuse/tests/test_cache.py b/services/fuse/tests/test_cache.py
index 66b70c9..f90c5b0 100644
--- a/services/fuse/tests/test_cache.py
+++ b/services/fuse/tests/test_cache.py
@@ -11,9 +11,6 @@ import unittest
 from .integration_test import IntegrationTest
 from .mount_test_base import MountTestBase
 
-_logger = logging.getLogger('arvados.arvados_fuse')
-_logger.setLevel(logging.DEBUG)
-
 class TmpCollectionTest(IntegrationTest):
     mnt_args = ["--by-id", "--directory-cache=0"]
 

commit bb7f064243fcd052e17815129fb1dba2f86d493d
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date:   Wed Sep 21 14:17:51 2016 -0400

    10117: Additional refinement: a directory not in use but still referenced needs
    to be invalidated before it can be cleared so that the kernel releases its
    references.

diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py
index ac6e236..e679a23 100644
--- a/services/fuse/arvados_fuse/__init__.py
+++ b/services/fuse/arvados_fuse/__init__.py
@@ -143,9 +143,15 @@ class InodeCache(object):
         return self._total
 
     def _remove(self, obj, clear):
-        if clear and not obj.can_clear():
-            _logger.debug("InodeCache cannot clear %i", obj.inode)
-            return False
+        if clear:
+            if obj.in_use():
+                _logger.debug("InodeCache cannot clear inode %i, in use", obj.inode)
+                return False
+            if obj.has_ref(only_children=True):
+                obj.invalidate()
+                _logger.debug("InodeCache invalidate inode %i", obj.inode)
+                return False
+            obj.clear()
         self._total -= obj.cache_size
         del self._entries[obj.cache_priority]
         if obj.cache_uuid:
@@ -154,7 +160,7 @@ class InodeCache(object):
                 del self._by_uuid[obj.cache_uuid]
             obj.cache_uuid = None
         if clear:
-            _logger.debug("InodeCache cleared %i total now %i", obj.inode, self._total)
+            _logger.debug("InodeCache cleared inode %i total now %i", obj.inode, self._total)
         return True
 
     def cap_cache(self):
@@ -177,7 +183,7 @@ class InodeCache(object):
                     if obj not in self._by_uuid[obj.cache_uuid]:
                         self._by_uuid[obj.cache_uuid].append(obj)
             self._total += obj.objsize()
-            _logger.debug("InodeCache touched %i (size %i) (uuid %s) total now %i", obj.inode, obj.objsize(), obj.cache_uuid, self._total)
+            _logger.debug("InodeCache touched inode %i (size %i) (uuid %s) total now %i", obj.inode, obj.objsize(), obj.cache_uuid, self._total)
             self.cap_cache()
         else:
             obj.cache_priority = None
@@ -632,7 +638,7 @@ class Operations(llfuse.Operations):
 
     @catch_exceptions
     def create(self, inode_parent, name, mode, flags, ctx):
-        _logger.debug("arv-mount create: %i '%s' %o", inode_parent, name, mode)
+        _logger.debug("arv-mount create: parent_inode %i '%s' %o", inode_parent, name, mode)
 
         p = self._check_writable(inode_parent)
         p.create(name)
@@ -648,7 +654,7 @@ class Operations(llfuse.Operations):
 
     @catch_exceptions
     def mkdir(self, inode_parent, name, mode, ctx):
-        _logger.debug("arv-mount mkdir: %i '%s' %o", inode_parent, name, mode)
+        _logger.debug("arv-mount mkdir: parent_inode %i '%s' %o", inode_parent, name, mode)
 
         p = self._check_writable(inode_parent)
         p.mkdir(name)
@@ -661,19 +667,19 @@ class Operations(llfuse.Operations):
 
     @catch_exceptions
     def unlink(self, inode_parent, name):
-        _logger.debug("arv-mount unlink: %i '%s'", inode_parent, name)
+        _logger.debug("arv-mount unlink: parent_inode %i '%s'", inode_parent, name)
         p = self._check_writable(inode_parent)
         p.unlink(name)
 
     @catch_exceptions
     def rmdir(self, inode_parent, name):
-        _logger.debug("arv-mount rmdir: %i '%s'", inode_parent, name)
+        _logger.debug("arv-mount rmdir: parent_inode %i '%s'", inode_parent, name)
         p = self._check_writable(inode_parent)
         p.rmdir(name)
 
     @catch_exceptions
     def rename(self, inode_parent_old, name_old, inode_parent_new, name_new):
-        _logger.debug("arv-mount rename: %i '%s' %i '%s'", inode_parent_old, name_old, inode_parent_new, name_new)
+        _logger.debug("arv-mount rename: old_parent_inode %i '%s' new_parent_inode %i '%s'", inode_parent_old, name_old, inode_parent_new, name_new)
         src = self._check_writable(inode_parent_old)
         dest = self._check_writable(inode_parent_new)
         dest.rename(name_old, name_new, src)
diff --git a/services/fuse/arvados_fuse/fresh.py b/services/fuse/arvados_fuse/fresh.py
index 635acea..95dde2b 100644
--- a/services/fuse/arvados_fuse/fresh.py
+++ b/services/fuse/arvados_fuse/fresh.py
@@ -112,8 +112,8 @@ class FreshBase(object):
         self.ref_count -= n
         return self.ref_count
 
-    def can_clear(self):
-        return not (self.use_count > 0 or self.ref_count > 0)
+    def has_ref(self, only_children=False):
+        return self.ref_count > 0
 
     def objsize(self):
         return 0
diff --git a/services/fuse/arvados_fuse/fusedir.py b/services/fuse/arvados_fuse/fusedir.py
index aa9c4fd..38fe32c 100644
--- a/services/fuse/arvados_fuse/fusedir.py
+++ b/services/fuse/arvados_fuse/fusedir.py
@@ -156,13 +156,21 @@ class Directory(FreshBase):
 
         self.fresh()
 
-    def can_clear(self):
-        if not super(Directory, self).can_clear():
-            return False
+    def in_use(self):
+        if super(Directory, self).in_use():
+            return True
         for v in self._entries.itervalues():
-            if not v.can_clear():
-                return False
-        return True
+            if v.in_use():
+                return True
+        return False
+
+    def has_ref(self, only_children=False):
+        if not only_children and super(Directory, self).has_ref():
+            return True
+        for v in self._entries.itervalues():
+            if v.has_ref():
+                return True
+        return False
 
     def clear(self):
         """Delete all entries"""
@@ -170,12 +178,21 @@ class Directory(FreshBase):
         self._entries = {}
         for n in oldentries:
             oldentries[n].clear()
-        for n in oldentries:
             self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
             self.inodes.del_entry(oldentries[n])
         self.inodes.invalidate_inode(self.inode)
         self.invalidate()
 
+    def invalidate(self):
+        try:
+            super(Directory, self).invalidate()
+            for n, e in self._entries.iteritems():
+                self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
+                e.invalidate()
+            self.inodes.invalidate_inode(self.inode)
+        except Exception:
+            _logger.exception()
+
     def mtime(self):
         return self._mtime
 
@@ -324,6 +341,7 @@ class CollectionDirectoryBase(Directory):
     def clear(self):
         r = super(CollectionDirectoryBase, self).clear()
         self.collection = None
+        self._manifest_size = 0
         return r
 
 
@@ -648,17 +666,7 @@ will appear if it exists.
         return not self.pdh_only
 
 
-class RecursiveInvalidateDirectory(Directory):
-    def invalidate(self):
-        try:
-            super(RecursiveInvalidateDirectory, self).invalidate()
-            for a in self._entries:
-                self._entries[a].invalidate()
-        except Exception:
-            _logger.exception()
-
-
-class TagsDirectory(RecursiveInvalidateDirectory):
+class TagsDirectory(Directory):
     """A special directory that contains as subdirectories all tags visible to the user."""
 
     def __init__(self, parent_inode, inodes, api, num_retries, poll_time=60):
diff --git a/services/fuse/tests/test_cache.py b/services/fuse/tests/test_cache.py
index 7aa0009..66b70c9 100644
--- a/services/fuse/tests/test_cache.py
+++ b/services/fuse/tests/test_cache.py
@@ -11,8 +11,11 @@ import unittest
 from .integration_test import IntegrationTest
 from .mount_test_base import MountTestBase
 
+_logger = logging.getLogger('arvados.arvados_fuse')
+_logger.setLevel(logging.DEBUG)
+
 class TmpCollectionTest(IntegrationTest):
-    mnt_args = ["--directory-cache=0"]
+    mnt_args = ["--by-id", "--directory-cache=0"]
 
     @IntegrationTest.mount(argv=mnt_args)
     def test_cache_spill(self):
diff --git a/services/fuse/tests/test_inodes.py b/services/fuse/tests/test_inodes.py
index 61170d5..15bd8a9 100644
--- a/services/fuse/tests/test_inodes.py
+++ b/services/fuse/tests/test_inodes.py
@@ -2,6 +2,7 @@ import arvados_fuse
 import mock
 import unittest
 import llfuse
+import logging
 
 class InodeTests(unittest.TestCase):
     def test_inodes_basic(self):
@@ -11,8 +12,8 @@ class InodeTests(unittest.TestCase):
         # Check that ent1 gets added to inodes
         ent1 = mock.MagicMock()
         ent1.in_use.return_value = False
+        ent1.has_ref.return_value = False
         ent1.persisted.return_value = True
-        ent1.clear.return_value = True
         ent1.objsize.return_value = 500
         inodes.add_entry(ent1)
         self.assertIn(ent1.inode, inodes)
@@ -25,8 +26,8 @@ class InodeTests(unittest.TestCase):
 
         ent1 = mock.MagicMock()
         ent1.in_use.return_value = False
+        ent1.has_ref.return_value = False
         ent1.persisted.return_value = True
-        ent1.clear.return_value = True
         ent1.objsize.return_value = 500
         inodes.add_entry(ent1)
 
@@ -34,6 +35,7 @@ class InodeTests(unittest.TestCase):
         # affect the cache total
         ent2 = mock.MagicMock()
         ent2.in_use.return_value = False
+        ent2.has_ref.return_value = False
         ent2.persisted.return_value = False
         ent2.objsize.return_value = 600
         inodes.add_entry(ent2)
@@ -46,17 +48,17 @@ class InodeTests(unittest.TestCase):
         # Check that ent1 gets added to inodes
         ent1 = mock.MagicMock()
         ent1.in_use.return_value = False
+        ent1.has_ref.return_value = False
         ent1.persisted.return_value = True
-        ent1.clear.return_value = True
         ent1.objsize.return_value = 500
         inodes.add_entry(ent1)
 
         # ent3 is persisted, adding it should cause ent1 to get cleared
         ent3 = mock.MagicMock()
         ent3.in_use.return_value = False
+        ent3.has_ref.return_value = False
         ent3.persisted.return_value = True
         ent3.objsize.return_value = 600
-        ent3.clear.return_value = True
 
         self.assertFalse(ent1.clear.called)
         inodes.add_entry(ent3)
@@ -78,46 +80,43 @@ class InodeTests(unittest.TestCase):
         self.assertTrue(ent3.clear.called)
         self.assertEqual(500, cache.total())
 
-    def test_clear_false(self):
+    def test_clear_in_use(self):
         cache = arvados_fuse.InodeCache(1000, 4)
         inodes = arvados_fuse.Inodes(cache)
 
         ent1 = mock.MagicMock()
-        ent1.in_use.return_value = False
+        ent1.in_use.return_value = True
+        ent1.has_ref.return_value = False
         ent1.persisted.return_value = True
-        ent1.clear.return_value = True
         ent1.objsize.return_value = 500
         inodes.add_entry(ent1)
 
         ent3 = mock.MagicMock()
         ent3.in_use.return_value = False
+        ent3.has_ref.return_value = True
         ent3.persisted.return_value = True
         ent3.objsize.return_value = 600
-        ent3.clear.return_value = True
         inodes.add_entry(ent3)
 
         cache.min_entries = 1
 
-        # ent1, ent3 clear return false, can't be cleared
-        ent1.clear.return_value = False
-        ent3.clear.return_value = False
+        # ent1, ent3 in use, has ref, can't be cleared
         ent1.clear.called = False
         ent3.clear.called = False
         self.assertFalse(ent1.clear.called)
         self.assertFalse(ent3.clear.called)
         cache.touch(ent3)
-        self.assertTrue(ent1.clear.called)
-        self.assertTrue(ent3.clear.called)
+        self.assertFalse(ent1.clear.called)
+        self.assertFalse(ent3.clear.called)
         self.assertEqual(1100, cache.total())
 
-        # ent1 clear return false, so ent3
-        # gets cleared
-        ent1.clear.return_value = False
-        ent3.clear.return_value = True
+        # ent1 still in use, ent3 doesn't have ref,
+        # so ent3 gets cleared
+        ent3.has_ref.return_value = False
         ent1.clear.called = False
         ent3.clear.called = False
         cache.touch(ent3)
-        self.assertTrue(ent1.clear.called)
+        self.assertFalse(ent1.clear.called)
         self.assertTrue(ent3.clear.called)
         self.assertEqual(500, cache.total())
 
@@ -127,20 +126,19 @@ class InodeTests(unittest.TestCase):
 
         ent1 = mock.MagicMock()
         ent1.in_use.return_value = False
+        ent1.has_ref.return_value = False
         ent1.persisted.return_value = True
-        ent1.clear.return_value = True
         ent1.objsize.return_value = 500
         inodes.add_entry(ent1)
 
         ent3 = mock.MagicMock()
         ent3.in_use.return_value = False
+        ent3.has_ref.return_value = False
         ent3.persisted.return_value = True
         ent3.objsize.return_value = 600
-        ent3.clear.return_value = True
 
         # Delete ent1
         self.assertEqual(500, cache.total())
-        ent1.clear.return_value = True
         ent1.ref_count = 0
         with llfuse.lock:
             inodes.del_entry(ent1)

commit 3a391d6693d89c0cfe5862227fcc9a0568bc775d
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date:   Wed Sep 21 11:13:32 2016 -0400

    10117: Consider both use_count and ref_count and check subdirectories to determine if it is safe to evict directory contents from cache.

diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py
index 6ac51f4..ac6e236 100644
--- a/services/fuse/arvados_fuse/__init__.py
+++ b/services/fuse/arvados_fuse/__init__.py
@@ -143,8 +143,8 @@ class InodeCache(object):
         return self._total
 
     def _remove(self, obj, clear):
-        if clear and not obj.clear():
-            _logger.debug("InodeCache could not clear %i in_use %s", obj.inode, obj.in_use())
+        if clear and not obj.can_clear():
+            _logger.debug("InodeCache cannot clear %i", obj.inode)
             return False
         self._total -= obj.cache_size
         del self._entries[obj.cache_priority]
diff --git a/services/fuse/arvados_fuse/fresh.py b/services/fuse/arvados_fuse/fresh.py
index 2075741..635acea 100644
--- a/services/fuse/arvados_fuse/fresh.py
+++ b/services/fuse/arvados_fuse/fresh.py
@@ -92,7 +92,7 @@ class FreshBase(object):
     def persisted(self):
         return False
 
-    def clear(self, force=False):
+    def clear(self):
         pass
 
     def in_use(self):
@@ -112,6 +112,9 @@ class FreshBase(object):
         self.ref_count -= n
         return self.ref_count
 
+    def can_clear(self):
+        return not (self.use_count > 0 or self.ref_count > 0)
+
     def objsize(self):
         return 0
 
diff --git a/services/fuse/arvados_fuse/fusedir.py b/services/fuse/arvados_fuse/fusedir.py
index 3f2bcd5..aa9c4fd 100644
--- a/services/fuse/arvados_fuse/fusedir.py
+++ b/services/fuse/arvados_fuse/fusedir.py
@@ -156,24 +156,25 @@ class Directory(FreshBase):
 
         self.fresh()
 
-    def clear(self, force=False):
-        """Delete all entries"""
-
-        if not self.in_use() or force:
-            oldentries = self._entries
-            self._entries = {}
-            for n in oldentries:
-                if not oldentries[n].clear(force):
-                    self._entries = oldentries
-                    return False
-            for n in oldentries:
-                self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
-                self.inodes.del_entry(oldentries[n])
-            self.inodes.invalidate_inode(self.inode)
-            self.invalidate()
-            return True
-        else:
+    def can_clear(self):
+        if not super(Directory, self).can_clear():
             return False
+        for v in self._entries.itervalues():
+            if not v.can_clear():
+                return False
+        return True
+
+    def clear(self):
+        """Delete all entries"""
+        oldentries = self._entries
+        self._entries = {}
+        for n in oldentries:
+            oldentries[n].clear()
+        for n in oldentries:
+            self.inodes.invalidate_entry(self.inode, n.encode(self.inodes.encoding))
+            self.inodes.del_entry(oldentries[n])
+        self.inodes.invalidate_inode(self.inode)
+        self.invalidate()
 
     def mtime(self):
         return self._mtime
@@ -320,8 +321,8 @@ class CollectionDirectoryBase(Directory):
         self.flush()
         src.flush()
 
-    def clear(self, force=False):
-        r = super(CollectionDirectoryBase, self).clear(force)
+    def clear(self):
+        r = super(CollectionDirectoryBase, self).clear()
         self.collection = None
         return r
 
@@ -375,7 +376,7 @@ class CollectionDirectory(CollectionDirectoryBase):
 
     def new_collection(self, new_collection_record, coll_reader):
         if self.inode:
-            self.clear(force=True)
+            self.clear()
 
         self.collection_record = new_collection_record
 
@@ -407,7 +408,7 @@ class CollectionDirectory(CollectionDirectoryBase):
                     if not self.stale():
                         return
 
-                    _logger.debug("Updating %s", to_record_version)
+                    _logger.debug("Updating collection %s inode %s to record version %s", self.collection_locator, self.inode, to_record_version)
                     if self.collection is not None:
                         if self.collection.known_past_version(to_record_version):
                             _logger.debug("%s already processed %s", self.collection_locator, to_record_version)
@@ -640,7 +641,7 @@ will appear if it exists.
         else:
             raise KeyError("No collection with id " + item)
 
-    def clear(self, force=False):
+    def clear(self):
         pass
 
     def want_event_subscribe(self):
diff --git a/services/fuse/arvados_fuse/fusefile.py b/services/fuse/arvados_fuse/fusefile.py
index 3f0e493..81fcd40 100644
--- a/services/fuse/arvados_fuse/fusefile.py
+++ b/services/fuse/arvados_fuse/fusefile.py
@@ -29,8 +29,8 @@ class File(FreshBase):
     def mtime(self):
         return self._mtime
 
-    def clear(self, force=False):
-        return True
+    def clear(self):
+        pass
 
     def writable(self):
         return False

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list