Page MenuHomeFreeBSD

freebsd-update: efficiently handle no updates in fetch
Needs ReviewPublic

Authored by tux2bsd_protonmail.com on Oct 20 2021, 7:26 AM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Apr 21, 10:11 PM
Unknown Object (File)
Fri, Apr 19, 9:46 AM
Unknown Object (File)
Thu, Apr 18, 8:09 PM
Unknown Object (File)
Tue, Apr 9, 7:48 PM
Unknown Object (File)
Sun, Apr 7, 3:49 PM
Unknown Object (File)
Mar 19 2024, 3:35 AM
Unknown Object (File)
Mar 18 2024, 8:32 PM
Unknown Object (File)
Mar 15 2024, 10:06 PM

Details

Summary

Markedly improve "fetch" when there are no updates.

On IO bound hardware, in my case a Raspberry Pi 3, this improvement has reduced runtime duration of freebsd-update fetch from 1m20s down to 1.5s. Even on fast SSD hardware freebsd-update fetch currently takes ~15 seconds, this changes reduces the time to sub second.

(git commit message has more)

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=258863

Test Plan

Tested with run combinations:
freebsd-update fetch
freebsd-update fetch install
freebsd-update cron
On installs of FreeBSD 13 that are up to date and not up to date, behaves as expected

(edit: some historic output available: https://github.com/freebsd/freebsd-src/pull/543)

Diff Detail

Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

usr.sbin/freebsd-update/freebsd-update.sh
1220

This whole function could be written as

[ -f tag.new -a -f tag ] && cmp -s tag.new tag

without the if and returns.

3426

why an exact match of the line in one case, but not the other?
^(foo|bar)$ would catch both

LGTM with the minor tweaks @imp suggested.

Needs an update to freebsd-update.8 since -F is being repurposed.

This will break very badly if freebsd-update fetch gets interrupted. The file tag is created at the end of fetch_metadata so the fact that it exists does not imply that the fetch completed.

We need to be very very careful to avoid the situation where an out-of-date system emits No updates are available to fetch erroneously.

This revision now requires changes to proceed.Oct 20 2021, 4:45 PM

Needs an update to freebsd-update.8 since -F is being repurposed.

It is NOT being repurposed, a force fetch will run as it did prior. The entire point of checking for that was to allow it to run as before.

(edit: at the time I didn't think I was repurposing -F)

This will break very badly if freebsd-update fetch gets interrupted. The file tag is created at the end of fetch_metadata so the fact that it exists does not imply that the fetch completed.

We need to be very very careful to avoid the situation where an out-of-date system emits No updates are available to fetch erroneously.

If tag and tag.new are different in any way or either tag or tag.new do not exist fetch will run as normal.

An out of date system cannot emit No updates are available to fetch

This patch was built up locally by me then further on GitHub with some great input there over the last few weeks. It hasn't simply landed here untested / unvetted.

usr.sbin/freebsd-update/freebsd-update.sh
1220

Yes, you're right. I had echo statements in various if/then blocks while I was getting this all to work how I wanted. Personally I prefer code where I can see the mechanics and descriptive names.

3426

"No updates need" was the prior test, it needs to be remain and I'm not altering that behaviour. I don't like that test, at it isn't specific enough and matches on a longer string. (not my code)

Style change for @imp and @emaste (re-upload, diff against the right commit ID this time...)

btw, once we get this right, if you push the git changes to github, and I'll land it from there.

This will break very badly if freebsd-update fetch gets interrupted. The file tag is created at the end of fetch_metadata so the fact that it exists does not imply that the fetch completed.

Ah, I see it now.

So in this patch (currently), "No updates are available to fetch":
fetch_run doesn't finish (interrupted) but fetch_metadata creates the tag. That tag file being present is enough to have a subsequent tag = tag.new because that preceeding entire fetch_run didn't finish.

How can it be detected that fetch_run ran to completion AND allows this patch to bypass unnecessary runs...

something like

rm -f fetch.run.completed (at the beginning of fetch_metadata)
test -f fetch.run.completed (act appropriately within this patch)
touch fetch.run.completed (at the end of fetch_run)

(edit: that was what I thought out loud, I went with a similar approach)

I'm glad to see @cperciva is a fan of shaving time off things. The reason I was sharing this patch to begin with was the drastic reduction of run times.

introduces a way to test that fetch_run ran to completion in a way that allows the new tag match test

usr.sbin/freebsd-update/freebsd-update.sh
2205

not so great aesthetically, @imp comments please?

2210

should probably add

|| return 1

Some more explanation in the comments.

Removed the function, it was one line so it seemed a bit silly.

Some more explanation in the comments.

Removed the function, it was one line so it seemed a bit silly.

(typo, sorry)

Put in what I hope is a better commented explanation.

Sorry if there is too much activity on this, I'm just trying to get it right. Let me know if I'm too noisy.

usr.sbin/freebsd-update/freebsd-update.sh
2208

"be" needs removing

Take out the bit about "Force", simply focus on tag/tag.new and fetch_run prior completion.

Needs an update to freebsd-update.8 since -F is being repurposed.

I thought more about what you said here and have taken out the '-F' check, my patch doesn't need to concern itself with an unfinished upgrade.

-F             Force freebsd-update fetch to proceed in the case of an
               unfinished upgrade.

The goal of my patch has been to make fetch efficient (in the case of no upstream updates).

Thanks, I think this solves the 'fetch dies partway through' issue.

I'm not entirely sure that it handles the case of 'a previous fetch downloaded updates which have not yet been installed' though -- it looks like it now exits with "No updates are available to fetch", which is a change from the previous behaviour.

In particular I'd like to make sure that

  1. If freebsd-update cron runs every night, it will continue to send emails until the updates it has downloaded are installed, and
  1. freebsd-update fetch install will result in an up-to-date system.

since those are both very common usage patterns.

Regarding #2, This use case was something I tested with along the way (this VM was deliberately out of date for this). It looks like this:

root@pull543:~ # freebsd-update fetch install
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 2 mirrors found.
Fetching metadata signature for 13.0-RELEASE from update2.freebsd.org... done.
No updates are available to fetch.
Creating snapshot of existing boot environment... done.
Installing updates...

Regarding #1, "a previous fetch downloaded updates which have not yet been installed"
The recipient would get the message that updates were ready, when they were ready (but not each "day" just initially). If the updates have not been installed then "install" is not being run, fairly normal - lots of people like to manually install.

My thought around #1 would be if "No updates are available to fetch." is emited in "cron" then call "updatesready" for the email unless it has "No updates are available to install."

If that sounds good for the "cron" mode I can work toward that, please let me know?

root@pull543:~ # freebsd-update updatesready
src component not installed, skipped
No updates are available to install.

(pull543 was the github pull request number)

Regarding #2, This use case was something I tested with along the way (this VM was deliberately out of date for this). It looks like this:

root@pull543:~ # freebsd-update fetch install
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 2 mirrors found.
Fetching metadata signature for 13.0-RELEASE from update2.freebsd.org... done.
No updates are available to fetch.
Creating snapshot of existing boot environment... done.
Installing updates...

Hmm. Maybe it would be clearer if "No updates are available to fetch" continued with " but previously fetched updates are available to install"? I can imagine users being very confused as to why their system is installing updates after saying that none are available.

Regarding #1, "a previous fetch downloaded updates which have not yet been installed"
The recipient would get the message that updates were ready, when they were ready (but not each "day" just initially). If the updates have not been installed then "install" is not being run, fairly normal - lots of people like to manually install.

My thought around #1 would be if "No updates are available to fetch." is emited in "cron" then call "updatesready" for the email unless it has "No updates are available to install."

If that sounds good for the "cron" mode I can work toward that, please let me know?

I think it's useful for the email sent by freebsd-update cron to list the updates -- this may help users to decide how urgent it is to install said updates, and simply sending an email saying "No updates are available to fetch. There are updates available to install." loses that information.

I'm leaning towards saying that the best solution here is to split fetch_create_manifest into fetch_create_manifest and fetch_print_summary and then call fetch_print_summary if we determine that nothing new needs to be fetched but previously fetched updates are waiting to be installed.

Colin suggested splitting the manifest function in two but I'm not seeing how to do that, I'm sure it's possible but at this stage that has me stumped.

What I have done instead is capture the manifest output(s) into a file to present at the right times (immediately when called, and when the tags match). The output is removed in a few places to avoid cross contamination, fetch vs upgrade for example.

This output of fetch is passed through to cron , the cron subroutine is reverted prior to my attempts with D32570.

Much cleaner. Only thing I'm not sure about is how we handle upgrade.

usr.sbin/freebsd-update/freebsd-update.sh
2767

Is there any point doing this here? The only time we use fetch_create_manifest.out is when we're short-circuiting a fetch run and we won't do that if the previous command run was an upgrade... right?

added PAGER for outputting fetch_create_manifest.out

Will this work if the -b basedir flag is used? Jails can be updated this way, and we don't want to fail to fetch updates for a jail simply because we've already fetched updates for the host.

Sorry, another thought: What happens in the following sequence of events?

  1. freebsd-update fetch
  2. freebsd-update install
  3. freebsd-update fetch
  4. freebsd-update rollback
  5. freebsd-update fetch
Sorry, another thought: What happens in the following sequence of events?

1. `freebsd-update fetch`
2. `freebsd-update install`
3. `freebsd-update fetch`
4. `freebsd-update rollback`
5. `freebsd-update fetch`

I've made two simple changes that will force an "old fashioned fetch" after either install or rollback.

As for the '-b' option, I'll have to think about that as I'm not a jail user.

don't need to induce old fashioned fetch after install

usr.sbin/freebsd-update/freebsd-update.sh
2208

This will still break if you run

# freebsd-update -b /path/to/jail fetch install
# freebsd-update -b /path/to/jail fetch
# freebsd-update fetch

How about:

  1. Create a file fetch_run_cachedtag at the end of a successful fetch which has BASEDIR = "/",
  2. Delete that file on any "install", "upgrade", or "rollback" operation,
  3. Compare tag.new to that file instead of tag, and
  4. Get rid of fetch_run.pending.
2279

This also has a risk of basedir confusion -- we could overwrite fetch_create_manifest.out with the fetch manifest for a jail. Let's make this conditional, on the same conditions as creating a fetch_run_cachedtag file.

Please elaborate on "This will still break"? That lacks a reasonable explanation.

I don't have an environment with jails and will have to spend some time setting that up at some point when I get some more time to look at this.

I'm no longer spending my time on D32570.

https://wiki.freebsd.org/2021FoundationCFI search for "freebsd-update improvements", I did ask for help - hopefully someone can take up where I left off.

Just to note, I haven't forgotten about this; I've just been very busy the past two weeks due to re:Invent. I promise I'll be coming back to this before 13.1-RELEASE.