Skip to content

Commit 3ea3e92

Browse files
committedFeb 1, 2024
osbuild: update LRU cache eviction PR with latest from upstream
osbuild/osbuild#1503
1 parent 758179e commit 3ea3e92

4 files changed

+65
-69
lines changed
 

‎src/0001-fscache-add-new-FsCache._last_used_objs-helper.patch

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From 0fd9104f51b8d8a58bfa7e9a32817a6f907fe166 Mon Sep 17 00:00:00 2001
1+
From 4a9831fa889b073ccb14568c4571f57d28dfe84f Mon Sep 17 00:00:00 2001
22
From: Michael Vogt <michael.vogt@gmail.com>
33
Date: Tue, 12 Dec 2023 21:20:42 +0100
44
Subject: [PATCH 1/4] fscache: add new `FsCache._last_used_objs()' helper

‎src/0002-fscache-add-FsCache._remove_lru-to-remove-entries.patch

+34-14
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1-
From 056d2fd5a419ab5f5974864b513230d3d133f7cf Mon Sep 17 00:00:00 2001
1+
From fff7dd5e1ab967165c16edec66b989adc70e39b7 Mon Sep 17 00:00:00 2001
22
From: Michael Vogt <michael.vogt@gmail.com>
33
Date: Wed, 13 Dec 2023 10:07:32 +0100
44
Subject: [PATCH 2/4] fscache: add FsCache._remove_lru() to remove entries
55

66
The FsCache._remove_lru() removes the least recently used entry
77
from the cache.
88
---
9-
osbuild/util/fscache.py | 44 +++++++++++++++++++++++++++++++++++++++++
10-
1 file changed, 44 insertions(+)
9+
osbuild/util/fscache.py | 64 +++++++++++++++++++++++++++++++++++++++++
10+
1 file changed, 64 insertions(+)
1111

1212
diff --git a/osbuild/util/fscache.py b/osbuild/util/fscache.py
13-
index 58c9a310..21e03f95 100644
13+
index 58c9a310..32056a35 100644
1414
--- a/osbuild/util/fscache.py
1515
+++ b/osbuild/util/fscache.py
16-
@@ -1086,6 +1086,50 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
16+
@@ -1086,6 +1086,70 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
1717
objs.append(FsCacheObjectInfo(name=name, last_used=last_used))
1818
return sorted(objs, key=lambda obj: obj.last_used)
1919

20-
+ def _remove_lru(self):
21-
+ """"Remove the least recently used entry from the cache."""
20+
+ def _remove_lru(self, required_size):
21+
+ """"
22+
+ Make room in the cache for "required_size" by remove the least
23+
+ recently used entry from the cache. Note that the cache may
24+
+ clear more than required_size.
25+
+ """
2226
+ # To avoid having to take a global cache lock the strategy is:
2327
+ # 1. Get list of (object, last_used) sorted from oldest to newest.
2428
+ # This is racy so we need to take care of that in step(2).
@@ -32,6 +36,11 @@ index 58c9a310..21e03f95 100644
3236
+ # Note that there is a risk to get out-of-sync in (3). If the
3337
+ # process dies while removing and before updating the cache
3438
+ # size the cache will be over reported.
39+
+
40+
+ # Try to clean at least twice the requested size to avoid having
41+
+ # to do this all over again
42+
+ try_to_free = required_size * 2
43+
+ freed_so_far = 0
3544
+ for name, last_used in self._last_used_objs():
3645
+ # take write lock for the indivdual object
3746
+ rpath = os.path.join(self._dirname_objects, name)
@@ -48,18 +57,29 @@ index 58c9a310..21e03f95 100644
4857
+ ):
4958
+ if last_used != self._last_used(name):
5059
+ continue
51-
+ # This is racy right now. To fix it we need to (atomic)
52-
+ # rename the "object.info" file in _rm_r_object() to
53-
+ # something like "object.removing". Then when opening
54-
+ # the cache scan for leftover "object.removing" files
55-
+ # and finish the cleanup and update the cache size
56-
+ # based on the size recorded inside "object.removing".
60+
+ # This is racy right now if the process is killed
61+
+ # during "_rm_r_object(rpath)" because then the
62+
+ # cache size is never reduced by the amount that
63+
+ # was about to be deleted.
64+
+ #
65+
+ # To fix it we need to (atomic) rename the
66+
+ # "object.info" file in _rm_r_object() to
67+
+ # something like "object.removing". Then when
68+
+ # opening the cache scan for leftover
69+
+ # "object.removing" files and finish the cleanup
70+
+ # and update the cache size based on the size
71+
+ # recorded inside "object.removing".
5772
+ size = self._calculate_space(self._path(rpath))
5873
+ self._rm_r_object(rpath)
5974
+ self._update_cache_size(-size)
60-
+ return
75+
+ freed_so_far += size
76+
+ if freed_so_far >= try_to_free:
77+
+ break
6178
+ except BlockingIOError:
6279
+ continue
80+
+
81+
+ # return True if at least the required size got freed
82+
+ return freed_so_far > required_size
6383
+
6484
@property
6585
def info(self) -> FsCacheInfo:
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
1-
From d33447368180a414f17a9a08346857129caf2556 Mon Sep 17 00:00:00 2001
1+
From 6ac1d20c8c52f3cbc6d890ed8a008f8f2d4c147b Mon Sep 17 00:00:00 2001
22
From: Michael Vogt <michael.vogt@gmail.com>
33
Date: Wed, 13 Dec 2023 17:39:55 +0100
44
Subject: [PATCH 3/4] fscache: use remove_lru() to reclaim space when the cache
55
is full
66

77
This commit adds code that will remove the least recently used
88
entries when a store() operation does not succeeds because the
9-
cache is full.
9+
cache is full. To be more efficient it will try to free
10+
twice the requested size (this can be configured in the code).
1011
---
11-
osbuild/util/fscache.py | 48 ++++++++++++++++++++++++++++++-----------
12-
1 file changed, 36 insertions(+), 12 deletions(-)
12+
osbuild/util/fscache.py | 32 +++++++++++++++++++++-----------
13+
1 file changed, 21 insertions(+), 11 deletions(-)
1314

1415
diff --git a/osbuild/util/fscache.py b/osbuild/util/fscache.py
15-
index 21e03f95..9057d53c 100644
16+
index 32056a35..59039522 100644
1617
--- a/osbuild/util/fscache.py
1718
+++ b/osbuild/util/fscache.py
18-
@@ -941,20 +941,31 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
19-
info: Dict[str, Any] = {}
19+
@@ -683,8 +683,11 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
20+
self._info_maximum_size = -1
21+
elif isinstance(info.maximum_size, int):
22+
self._info_maximum_size = info.maximum_size
23+
- else:
24+
+ elif info.maximum_size is None:
25+
self._info_maximum_size = 0
26+
+ else:
27+
+ raise ValueError(
28+
+ f"maximum-size can only be set to 'unlimited' or an integer value, got {type(info.maximum_size)}")
29+
30+
def _is_active(self):
31+
# Internal helper to verify we are in an active context-manager.
32+
@@ -942,19 +945,27 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
2033
info["creation-boot-id"] = self._bootid
2134
info["size"] = self._calculate_space(path_data)
22-
+ # account for the extra block of the object-information file
23-
+ # (find a better way to account for the block size)
24-
+ info["size"] += 4096
2535

2636
- # Update the total cache-size. If it exceeds the limits, bail out
2737
- # but do not trigger an error. It behaves as if the entry was
@@ -56,48 +66,14 @@ index 21e03f95..9057d53c 100644
5666

5767
try:
5868
# Commit the object-information, thus marking it as fully
59-
@@ -1086,8 +1097,12 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
60-
objs.append(FsCacheObjectInfo(name=name, last_used=last_used))
61-
return sorted(objs, key=lambda obj: obj.last_used)
62-
63-
- def _remove_lru(self):
64-
- """"Remove the least recently used entry from the cache."""
65-
+ def _remove_lru(self, required_size):
66-
+ """"
67-
+ Make room in the cache for "required_size" by remove the least
68-
+ recently used entry from the cache. Note that the cache may
69-
+ clear more than required_size.
70-
+ """
71-
# To avoid having to take a global cache lock the strategy is:
72-
# 1. Get list of (object, last_used) sorted from oldest to newest.
73-
# This is racy so we need to take care of that in step(2).
74-
@@ -1101,6 +1116,11 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
75-
# Note that there is a risk to get out-of-sync in (3). If the
76-
# process dies while removing and before updating the cache
77-
# size the cache will be over reported.
78-
+ #
79-
+ # try to clean at least twice the requested size to avoid having
80-
+ # to do this all over again
81-
+ try_to_free = required_size * 2
82-
+ freed_so_far = 0
83-
for name, last_used in self._last_used_objs():
84-
# take write lock for the indivdual object
85-
rpath = os.path.join(self._dirname_objects, name)
86-
@@ -1126,9 +1146,13 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
87-
size = self._calculate_space(self._path(rpath))
88-
self._rm_r_object(rpath)
89-
self._update_cache_size(-size)
90-
- return
91-
+ freed_so_far += size
92-
+ if freed_so_far >= try_to_free:
93-
+ break
69+
@@ -1146,7 +1157,6 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
70+
break
9471
except BlockingIOError:
9572
continue
96-
+ # return if at least the required size got freed
97-
+ return freed_so_far > required_size
73+
-
74+
# return True if at least the required size got freed
75+
return freed_so_far > required_size
9876

99-
@property
100-
def info(self) -> FsCacheInfo:
10177
--
10278
2.43.0
10379

‎src/0004-fscache-add-eviction-log-statement.patch

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From adb3016947277a272bbc586053569c92448f2fa8 Mon Sep 17 00:00:00 2001
1+
From 3e63810210760998f197437e6c3fb82df87483b8 Mon Sep 17 00:00:00 2001
22
From: Dusty Mabe <dusty@dustymabe.com>
33
Date: Mon, 15 Jan 2024 16:14:50 -0500
44
Subject: [PATCH 4/4] fscache: add eviction log statement
@@ -9,12 +9,12 @@ This will help us know when the cache LRU policy is working.
99
1 file changed, 1 insertion(+)
1010

1111
diff --git a/osbuild/util/fscache.py b/osbuild/util/fscache.py
12-
index 9057d53c..a2e6d374 100644
12+
index 59039522..3aa6a1a4 100644
1313
--- a/osbuild/util/fscache.py
1414
+++ b/osbuild/util/fscache.py
15-
@@ -1144,6 +1144,7 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
16-
# and finish the cleanup and update the cache size
17-
# based on the size recorded inside "object.removing".
15+
@@ -1150,6 +1150,7 @@ class FsCache(contextlib.AbstractContextManager, os.PathLike):
16+
# and update the cache size based on the size
17+
# recorded inside "object.removing".
1818
size = self._calculate_space(self._path(rpath))
1919
+ print(f"CACHE: evicting {rpath}")
2020
self._rm_r_object(rpath)

0 commit comments

Comments
 (0)
Please sign in to comment.