From 6d86e355c251a68dd870e22ea1cdd43eea10c390 Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Tue, 24 Nov 2015 13:45:02 -0500 Subject: [PATCH] 7751: Fix race by telling fuse not to cache the .arvados#collection dirent. --- services/fuse/arvados_fuse/__init__.py | 4 ++-- services/fuse/arvados_fuse/fresh.py | 2 ++ services/fuse/arvados_fuse/fusefile.py | 7 +++++++ services/fuse/tests/test_tmp_collection.py | 19 +++++++++++-------- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/services/fuse/arvados_fuse/__init__.py b/services/fuse/arvados_fuse/__init__.py index b7c0091a61..65f117b55a 100644 --- a/services/fuse/arvados_fuse/__init__.py +++ b/services/fuse/arvados_fuse/__init__.py @@ -407,8 +407,8 @@ class Operations(llfuse.Operations): entry = llfuse.EntryAttributes() entry.st_ino = inode entry.generation = 0 - entry.entry_timeout = 60 - entry.attr_timeout = 60 + entry.entry_timeout = 60 if e.allow_dirent_cache else 0 + entry.attr_timeout = 60 if e.allow_attr_cache else 0 entry.st_mode = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH if isinstance(e, Directory): diff --git a/services/fuse/arvados_fuse/fresh.py b/services/fuse/arvados_fuse/fresh.py index ec2d47a269..2075741dbd 100644 --- a/services/fuse/arvados_fuse/fresh.py +++ b/services/fuse/arvados_fuse/fresh.py @@ -67,6 +67,8 @@ class FreshBase(object): self.cache_priority = None self.cache_size = 0 self.cache_uuid = None + self.allow_attr_cache = True + self.allow_dirent_cache = True # Mark the value as stale def invalidate(self): diff --git a/services/fuse/arvados_fuse/fusefile.py b/services/fuse/arvados_fuse/fusefile.py index d4c075a40d..e731327dec 100644 --- a/services/fuse/arvados_fuse/fusefile.py +++ b/services/fuse/arvados_fuse/fusefile.py @@ -112,6 +112,13 @@ class FuncToJSONFile(StringFile): super(FuncToJSONFile, self).__init__(parent_inode, "", 0) self.func = func + # invalidate_inode() and invalidate_entry() are asynchronous + # with no callback to wait for. In order to guarantee + # userspace programs don't get stale data that was generated + # before the last invalidate(), we must disallow dirent + # caching entirely. + self.allow_dirent_cache = False + def size(self): self._update() return super(FuncToJSONFile, self).size() diff --git a/services/fuse/tests/test_tmp_collection.py b/services/fuse/tests/test_tmp_collection.py index e403a2c4ea..60eba1bf85 100644 --- a/services/fuse/tests/test_tmp_collection.py +++ b/services/fuse/tests/test_tmp_collection.py @@ -110,12 +110,15 @@ class TmpCollectionTest(IntegrationTest): r'^\. 37b51d194a7513e45b56f6524f2d51f2\+3(\+\S+)? acbd18db4cc2f85cedef654fccc4a4d8\+3(\+\S+)? 0:3:bar 3:3:foo\n$'), ('foo', None, r'^\. 37b51d194a7513e45b56f6524f2d51f2\+3(\+\S+)? 0:3:bar\n$'), + ('bar', None, + r'^$'), ] - for fn, content, expect in ops: - path = os.path.join(tmpdir, fn) - if content is None: - os.unlink(path) - else: - with open(path, 'w') as f: - f.write(content) - self.assertRegexpMatches(current_manifest(tmpdir), expect) + for _ in range(10): + for fn, content, expect in ops: + path = os.path.join(tmpdir, fn) + if content is None: + os.unlink(path) + else: + with open(path, 'w') as f: + f.write(content) + self.assertRegexpMatches(current_manifest(tmpdir), expect) -- 2.30.2