-
Notifications
You must be signed in to change notification settings - Fork 14
cfsctl: Fix garbage collection #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
I think a lot of this will change once #185 lands. We'd probably want to wait until then, I think |
|
I briefly skimmed the PR, I think as long as the split stream changes exposes the same That being said, it's totally fair to delay this PR ahead of a potentially dependent change as large as that. I could work on a rebase once that is landed. |
cgwalters
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not adding any tests here, but we definitely want that. Basic ones would be ensuring adding content and removing all the streams also GC's all objects, and two streams with shared objects but removing just one keeps the right objects etc.
|
Also sorry to be clear the above review was using the newly landed https://github.com/bootc-dev/agent-skills/blob/main/perform-forge-review/SKILL.md flow...and it somehow seems to have lost my header comment? I'll look at that. |
|
I rebased the branch onto main after the splitstream changes. It seems that in the new format manifests referring to other streams in a separate table |
a6cefcf to
56c2597
Compare
Signed-off-by: Chaser Huang <huangkangjing@gmail.com>
ec9f7e5 to
604907a
Compare
Add support for user-specified GC Roots Add cleanup of broken links after GC Use log crate to properly log in GC Signed-off-by: Chaser Huang <huangkangjing@gmail.com>
07e0787 to
87a7a1a
Compare
Fixes not marking named reference stream objects themselves as live Signed-off-by: Chaser Huang <huangkangjing@gmail.com>
|
Done, I have included some improvements to the GC code as well. Now GC process can take caller-specified GC roots, and remove broken links in the repo. PTAL @cgwalters |
|
Hold on, I found another bug with actual bootc repository testing. |
Also includes regression test for these cases Signed-off-by: Chaser Huang <huangkangjing@gmail.com>
|
Should be good now, I also included a regression test for the bug mentioned above |
cgwalters
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this!! Sorry about the delay. I only have a relatively superficial review so far
| GC { | ||
| // digest of root images for gc operations | ||
| #[clap(long, short = 'i')] | ||
| root_images: Vec<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait why would these be provided externally? Are they additional to the images present?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, these are added to the list of GC roots that would be used for GC analysis. The existing internal references (in streams/refs/ and in images/refs) will always be considered and added to the GC root list.
The main reasoning here to allow additional external roots is mainly because we don't have a concrete standard to partition */refs directory into different namespaces for different users of composefs. Even bootc does not call composefs in a way that would create these references. So it is better to just assume the */refs references are not enough and it is up to the user to supplement all sensible roots. This concrete standard/scheme is particularly more relevant in pursuing the unified containers storage approach and we probably need to come to it later.
Besides it's really handy to be able to specify the additional roots when doing tests, management and diagnosis.
| } | ||
|
|
||
| fn walk_symlinkdir(fd: OwnedFd, objects: &mut HashSet<ObjectID>) -> Result<()> { | ||
| fn walk_symlinkdir(fd: OwnedFd, entry_digests: &mut HashSet<CString>) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should prefer Rust-native representations in memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now uses OsString
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if we want String here or OsString. Since the hash set stores digest strings, they should always be UTF8 and not really giving any UTF8 parsing errors. But the UTF8 conversion feels redundant anyways. Let me know your preferences.
| )?; | ||
| } else { | ||
| // stream is in table but not in repo, the repo is potentially broken, issue a warning | ||
| warn!("broken repo: named reference stream {stream_name_in_table} not found as stream in repo"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with this but in general I would say we should stick to debug! and trace! - warn! is likely to be routed to stderr which might not be expected in all places.
It's complicated but we could have APIs like this return something like a Vec<Warning> or so. There may be prior art for this.
| &self, | ||
| root_image_names: Vec<String>, | ||
| root_stream_names: Vec<String>, | ||
| force: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think force could use a comment
| pub fn gc(&self) -> Result<()> { | ||
| pub fn gc( | ||
| &self, | ||
| root_image_names: Vec<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass slices not Vec to functions
This PR fixes the broken garbage collection implementation provided by
cfsctl. To my knowledge this GC implementation is not used anywhere else, but this fixed implementation could be helpful for downstream project like bootc as well, see bootc-dev/bootc#1808Previously, the GC implementation was completely broken as it didn't add any object IDs to the live set and would thus always mark every object for deletion. The code in
gc_categoryseems to be assuming an old composefs structure where non-first-level entries instreams/andimages/directory (e.g.streams/refs/some_distro/some_version) could directly link to object stores. But currently these links always link to first-level entries first and first-level entries would then link to objects in store.This PR fixes this by doing a proper walk to add all objects referred from named references to the live set, such that all unlinked objects would be marked for deletion.
Furthermore, the old implementation was doing a naive shallow walk for the streams, this is problematic for pulled OCI images in composefs repo, since they have two layers of links, a config split stream linking all layers, and each layers linking to their layer contents. This PR adds a full walk algorithm to walk down and prune the entire stream tree to mark unlinked objects for deletion.
Currently this is still dry-run only, but I have changed the output format to add a "#" before all non-delete lines and the output could now just be piped to a shell to perform the deletion.
Note that bootc has its own GC implementations here. But bootc uses bootloader entries as part of the GC root, which I believe should be considered out of composefs scope. Bootc GC also does not prune streams. I think ideally we should have a complete GC implementation in composefs and bootc should just forward the call.