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
zfs: Improve import handling #42269
zfs: Improve import handling #42269
Conversation
I've tested this manually, by restarts with a machine in various stages of (ocassionally non-)degradation. I haven't been able to test degradation of the root device, as I only have a single NVMe root disk, but I see no reason why that should fail. |
Probable output when no devices are broken:
|
@@ -58,6 +58,26 @@ let | |||
|
|||
snapshotNames = [ "frequent" "hourly" "daily" "weekly" "monthly" ]; | |||
|
|||
importLib = {pool, cfgZfs}: '' | |||
zpool_cmd="${packages.zfsUser}/sbin/zpool" | |||
awk_cmd="${pkgs.gawk}/bin/awk" |
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.
Maybe use the busybox awk instead
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 there's an idea.
Well, it worked, but it didn't affect the closure size. I'm starting to wonder if there's a spurious dependency from the scripting, somehow.
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.
...false. It seems I'm getting too sleepy for this, as I forgot to git commit
.
Swapping to busybox awk fixes the closure size... though it doesn't tell me why that was a problem to begin with.
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.
...false, again. I'll get back to this when I'm more awake, but it happens independently of the awk selection.
Got it. It's obvious in retrospect; I'd replaced the carefully copied Now it's good. |
...and this should be picked to stable as well, because the previous code is a data loss event in waiting. |
until msg="$(zpool import -d ${cfgZfs.devNodes} -N $ZFS_FORCE '${pool}' 2>&1)"; do | ||
sleep 0.25 | ||
# Loop across the import until it succeeds, because the devices needed may not be discovered yet. | ||
imported && exit |
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.
This is not an independent script. I don't think exit
is a good choice here it may interfere with other commands later in this file.
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.
Fixed.
zpoolCmd = "zpool"; | ||
awkCmd = "awk"; | ||
inherit pool cfgZfs; | ||
}) + '' |
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.
Here you overwrite existing function if multiple pools are defined. Can you only define each function once?
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.
If multiple root pools are defined?
Well, I suppose it'd be cleaner to not map across it. I'm not sure why this is a map in the first place, but let's do it right.
"${zpoolCmd}" list "${pool}" >/dev/null 2>/dev/null | ||
} | ||
import() { | ||
"${zpoolCmd}" import -d "${cfgZfs.devNodes}" -N $ZFS_FORCE "${pool}" |
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 would recommend to rename ready
to zpool-ready
, which takes the pool name as the first argument.
It would be actually more readable if the other functions would be inline, since the commands are not much more complex then there replacement.
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.
Fixed the names.
I think the functions are quite a bit more readable than their contents, but that's not the only reason they exist. It's also to avoid duplicating the code. We might need to change some of this in the future, and it's good not having to change two places.
Note that the import systemd unit and the import-root-pool scripts had already diverged, so that isn't an academic concern.
sleep 1 | ||
done | ||
imported || import # Try one last time, e.g. to import a degraded pool. | ||
if imported; then |
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.
What is the return value of zpool import
in case of an failure? Should the script not already exit before it reaches this line?
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.
Most likely. I'm going to call this defensive coding; no matter what happens, we won't claim the pool is successfully imported if it isn't.
ready && import && break | ||
sleep 1 | ||
done | ||
imported || import # Try one last time, e.g. to import a degraded pool. |
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 don't understand why this is tried a last time when it already failed 60 times before.
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.
It isn't tried in the same way. "ready && import" would only import the pool if every device is available, i.e. it's in state ONLINE and not state DEGRADED.
The unconditional import at the end will fail if it's in state FAULTED or MISSING, of course, but not if it's DEGRADED. That's a feature: The system will still boot if one or two devices are missing, just not as quickly. But we don't want it to import the pool in a degraded state if it can avoid that by waiting another few seconds.
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.
Alright, can you also mention this in the 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.
Also that it waits for devices to become visible, before the pool is imported the first time.
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.
Done.
4149a5c
to
1f50c87
Compare
Anything else that needs doing? |
@GrahamcOfBorg test installer.zfsroot |
@woffs can you test the import service? I only have zfs as a root pool. |
No attempt on aarch64-linux (full log) The following builds were skipped because they don't evaluate on aarch64-linux: tests.installer.zfsroot Partial log (click to expand)
|
Success on x86_64-linux (full log) Attempted: tests.installer.zfsroot Partial log (click to expand)
|
@Mic92 I applied 1f50c87c83d0c46a11d44958f59bd408db74bbc2 onto nixos-18.03-small and rebooted. It worked, the pool (consisting of 24 devices) was cleanly imported. |
@woffs you could open a new pull request on top of this one. |
I can try updating this one, but it's problematic rebasing it on top of unstable -- I don't run unstable on any of my computers, anymore. Perhaps I should. |
I rebased it. It might work. But at the moment |
I was able to test it. It seems to work... |
This seems to cause a 2min 25secs downtime for me when booting my notebooks from mirrored SSDs |
@Baughn Thanks for fixing this! I just spent a few hours yesterday trying to figure out why two of the devices in my pool were UNAVAIL ("label is missing or invalid"), even though the disks were clearly there in |
It looks like its functionality was (possibly accidentally) removed in NixOS#42269
Motivation for this change
Addresses #42178
This is a big sledgehammer, but it should be a reasonably safe sledgehammer: Use the ZFS tools to check if the pool is importable as ONLINE (as opposed to DEGRADED), and if not, sleep. It does cause serious performance degradation, but in practice that means adding another second or two to boot time, worst-case.
What I don't like, and which I can't find a cause for, is that this also increases the size of a ZFS-enabled initrd from 12 to 120 MB. By examination, this is because it ends up including all of ncurses, the glibc headers, and so on and so annoyingly forth.
I don't know why. The only added dependency is gawk, which only depends on glibc, which is already in the initrd.