Merge branch '21639-keep-cache-dict' refs #21639
[arvados.git] / services / fuse / tests / test_inodes.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 import arvados_fuse
6 import mock
7 import unittest
8 import llfuse
9 import logging
10
11 class InodeTests(unittest.TestCase):
12
13     # The following tests call next(inodes._counter) because inode 1
14     # (the root directory) gets special treatment.
15
16     def test_inodes_basic(self):
17         cache = arvados_fuse.InodeCache(1000, 4)
18         inodes = arvados_fuse.Inodes(cache)
19         next(inodes._counter)
20
21         # Check that ent1 gets added to inodes
22         ent1 = mock.MagicMock()
23         ent1.in_use.return_value = False
24         ent1.has_ref.return_value = False
25         ent1.persisted.return_value = True
26         ent1.objsize.return_value = 500
27         inodes.add_entry(ent1)
28         self.assertIn(ent1.inode, inodes)
29         self.assertIs(inodes[ent1.inode], ent1)
30         self.assertEqual(500, cache.total())
31
32     def test_inodes_not_persisted(self):
33         cache = arvados_fuse.InodeCache(1000, 4)
34         inodes = arvados_fuse.Inodes(cache)
35         next(inodes._counter)
36
37         ent1 = mock.MagicMock()
38         ent1.in_use.return_value = False
39         ent1.has_ref.return_value = False
40         ent1.persisted.return_value = True
41         ent1.objsize.return_value = 500
42         inodes.add_entry(ent1)
43
44         # ent2 is not persisted, so it doesn't
45         # affect the cache total
46         ent2 = mock.MagicMock()
47         ent2.in_use.return_value = False
48         ent2.has_ref.return_value = False
49         ent2.persisted.return_value = False
50         ent2.objsize.return_value = 600
51         inodes.add_entry(ent2)
52         self.assertEqual(500, cache.total())
53
54     def test_inode_cleared(self):
55         cache = arvados_fuse.InodeCache(1000, 4)
56         inodes = arvados_fuse.Inodes(cache)
57         next(inodes._counter)
58
59         # Check that ent1 gets added to inodes
60         ent1 = mock.MagicMock()
61         ent1.in_use.return_value = False
62         ent1.has_ref.return_value = False
63         ent1.persisted.return_value = True
64         ent1.objsize.return_value = 500
65         inodes.add_entry(ent1)
66
67         # ent3 is persisted, adding it should cause ent1 to get cleared
68         ent3 = mock.MagicMock()
69         ent3.in_use.return_value = False
70         ent3.has_ref.return_value = False
71         ent3.persisted.return_value = True
72         ent3.objsize.return_value = 600
73
74         self.assertFalse(ent1.clear.called)
75         inodes.add_entry(ent3)
76
77         # Won't clear anything because min_entries = 4
78         self.assertEqual(2, len(cache._cache_entries))
79         self.assertFalse(ent1.clear.called)
80         self.assertEqual(1100, cache.total())
81
82         # Change min_entries
83         cache.min_entries = 1
84         ent1.parent_inode = None
85         inodes.cap_cache()
86         inodes.wait_remove_queue_empty()
87         self.assertEqual(600, cache.total())
88         self.assertTrue(ent1.clear.called)
89
90         # Touching ent1 should cause ent3 to get cleared
91         ent3.parent_inode = None
92         self.assertFalse(ent3.clear.called)
93         inodes.inode_cache.update_cache_size(ent1)
94         inodes.touch(ent1)
95         inodes.wait_remove_queue_empty()
96         self.assertTrue(ent3.clear.called)
97         self.assertEqual(500, cache.total())
98
99     def test_clear_in_use(self):
100         cache = arvados_fuse.InodeCache(1000, 4)
101         inodes = arvados_fuse.Inodes(cache)
102         next(inodes._counter)
103
104         ent1 = mock.MagicMock()
105         ent1.in_use.return_value = True
106         ent1.has_ref.return_value = False
107         ent1.persisted.return_value = True
108         ent1.objsize.return_value = 500
109         inodes.add_entry(ent1)
110
111         ent3 = mock.MagicMock()
112         ent3.in_use.return_value = False
113         ent3.has_ref.return_value = True
114         ent3.persisted.return_value = True
115         ent3.objsize.return_value = 600
116         inodes.add_entry(ent3)
117
118         cache.min_entries = 1
119
120         # ent1, ent3 in use, has ref, can't be cleared
121         ent1.clear.called = False
122         ent3.clear.called = False
123         self.assertFalse(ent1.clear.called)
124         self.assertFalse(ent3.clear.called)
125         inodes.touch(ent3)
126         inodes.wait_remove_queue_empty()
127         self.assertFalse(ent1.clear.called)
128         self.assertFalse(ent3.clear.called)
129         # kernel invalidate gets called anyway
130         self.assertTrue(ent3.kernel_invalidate.called)
131         self.assertEqual(1100, cache.total())
132
133         # ent1 still in use, ent3 doesn't have ref,
134         # so ent3 gets cleared
135         ent3.has_ref.return_value = False
136         ent1.clear.called = False
137         ent3.clear.called = False
138         ent3.parent_inode = None
139         inodes.touch(ent3)
140         inodes.wait_remove_queue_empty()
141         self.assertFalse(ent1.clear.called)
142         self.assertTrue(ent3.clear.called)
143         self.assertEqual(500, cache.total())
144
145     def test_delete(self):
146         cache = arvados_fuse.InodeCache(1000, 0)
147         inodes = arvados_fuse.Inodes(cache)
148         next(inodes._counter)
149
150         ent1 = mock.MagicMock()
151         ent1.in_use.return_value = False
152         ent1.has_ref.return_value = False
153         ent1.persisted.return_value = True
154         ent1.objsize.return_value = 500
155         inodes.add_entry(ent1)
156
157         ent3 = mock.MagicMock()
158         ent3.in_use.return_value = False
159         ent3.has_ref.return_value = False
160         ent3.persisted.return_value = True
161         ent3.objsize.return_value = 600
162
163         # Delete ent1
164         self.assertEqual(500, cache.total())
165         ent1.ref_count = 0
166         with llfuse.lock:
167             inodes.del_entry(ent1)
168         inodes.wait_remove_queue_empty()
169         self.assertEqual(0, cache.total())
170
171         inodes.add_entry(ent3)
172         inodes.wait_remove_queue_empty()
173         self.assertEqual(600, cache.total())