refactor: atomic installation#201
Conversation
5cdcf71 to
66cd69c
Compare
d4a962e to
f9967ef
Compare
d1f6aad to
1d53ddf
Compare
f9967ef to
d1f6aad
Compare
|
@bitwalker like mentioned on #194, this PR is also ready for review. The main set of changes come from Thanks in advance ^_^ |
bitwalker
left a comment
There was a problem hiding this comment.
Looks good overall! I found a couple of issues that we should address, but this is pretty close
| // We now rename tmp_link to toolchain_dir. When renamed, it will still be | ||
| // pointing to relative_install_target. If the channel direcotry existed, it | ||
| // will overwrite the file. This is what marks the install as completed. | ||
| std::fs::rename(&temp_symlink, &toolchain_dir).with_context(|| { |
There was a problem hiding this comment.
It isn't clear to me whether there will be an issue here with existing installations. Those may already have real directories at toolchains<version>, so this will fail if toolchain_dir already exists.
There was a problem hiding this comment.
Great point, addressed it here: https://github.com/0xmiden/midenup/compare/35b49ac..970b91d
I added a migration check and associated function. Were more migrations to be needed in the future, we could just add more checks and associated fuctions on the corresponding mod.rs file
|
|
||
| local_manifest.remove_channel(channel.name.clone()); | ||
| // We remove the symlink, thus making the channel unaccesible. | ||
| if toolchain_symlink.exists() { |
There was a problem hiding this comment.
Similar to my comment in install.rs, if there is an existing install, then remove_file here may be getting called on a real directory, not a symlink. Existing installs will need a one-time migration or compatibility path.
| .unwrap_or(false); | ||
| let installed_toolchains_dir = config.midenup_home.join("installed_toolchains"); | ||
| let install_dir_name = format!("{}-{}", &channel.name, channel.content_hash()); | ||
| let install_dir = installed_toolchains_dir.join(&install_dir_name); |
There was a problem hiding this comment.
It appears updates that have the same hash can be marked complete without reinstalling. Same-hash updates can be marked complete without reinstalling. install_dir is based on channel.content_hash() here, and changed branch/path components can reuse the same upstream channel hash. If the hashed install directory already exists, component removal is skipped entirely (see line 44), the generated install script skips existing binaries, and the manifest is refreshed as if the update happened. This affects branch-tip and local-path updates.
There was a problem hiding this comment.
Great catch, I've since adressed this issue here: https://github.com/0xmiden/midenup/compare/2402558..725ac8f
Now, when performing an update, the channels might mutate (for instance, channels that are tracking branches will get the fetched latest commit added onto their latest_commit field. Same with Path tracked components.
There was a problem hiding this comment.
Update: This is no longer the case, the is_up_to_date function no longer changes the component. Now, when the UpstreamChannel is created, we sync the channel to get the latest changes.
| Authority::Cargo { .. } | Authority::Git { .. } => true, | ||
| // Since uninstalling a component from the filesystem is potentially | ||
| // irreversible, we take special precautions before uninstalling them. | ||
| Authority::Path { .. } => match options.path_update { |
There was a problem hiding this comment.
--path-update=off no longer preserves skipped components. Update excludes path components here, but installation still saves the upstream channel and refreshes path metadata for all path components (see line 223 of install.rs), so a skipped rebuild can then look current.
There was a problem hiding this comment.
Noted. This has seen been fixed here: f29ac1e
This same issue also caused a bug where partial channels would install all the remaining components.
| // We now rename tmp_link to toolchain_dir. When renamed, it will still be | ||
| // pointing to relative_install_target. If the channel direcotry existed, it | ||
| // will overwrite the file. This is what marks the install as completed. | ||
| std::fs::rename(&temp_symlink, &toolchain_dir).with_context(|| { |
There was a problem hiding this comment.
It looks like installs can be published before metadata is committed. The runnable toolchains/<version> symlink is published before stable and manifest.json are written here and on line 176. A failure in between will leave the filesystem and local manifest state inconsistent.
There was a problem hiding this comment.
This has since been fixed here: d58c9dc
Updating the manifest should be the last operation we do IIUC
|
|
||
| local_manifest.remove_channel(channel.name.clone()); | ||
| // We remove the symlink, thus making the channel unaccesible. | ||
| if toolchain_symlink.exists() { |
There was a problem hiding this comment.
It appears uninstallation is not retry-safe after cleanup failure. This removes the symlink and manifest entry before removing components/install directory. If cleanup fails, rerunning uninstall bails because the manifest entry is gone.
There was a problem hiding this comment.
Good point, changed here: f1a3b5d
Now the symlink is removed after removing the components.
a826083 to
d288af2
Compare
fa817e0 to
d58c9dc
Compare
…on' into fabrizioorsi/atomic-installation
This reverts commit 2bfa932.
03ca629 to
452b5ee
Compare
0e97928 to
100f6a4
Compare
100f6a4 to
6c1f491
Compare
c51597a to
8cc8e8f
Compare
|
The latest commits updated the way updates were computed. The main change is the following:
The entire change set can be seen on this diff: https://github.com/0xmiden/midenup/compare/fd96e357560fc4a3f33b94db1de4df69c2dee28b..aab07d5c0c32eb76c113d61f39960f943dacc840 |
Closes #198
Closes #195
Blocked-by: #209
This PR changes the way installs are performed in midenup. After this PR, installs in midenup are made atomically, meaning that the entire toolchain is either fully installed onto the filesystem or not.
Installs are performed on a separate directory that's stored inside the
installed_toolchains/<channel-version>-<hash>. This directory is then atomically symlinked into the correspondingtoolchains/<channel-version>directory. If a toolchain gets re-installed with newer components (such as in the case=This structure roughly looks like:
Notes:
Hash,PartialEqandEqforComponent. These custom definition were only used inupdate.rswhen performing set operations. Removed in 5597a1e.Components by names, the wrapper structComponentByNamecan be used.Channelnow deriveHash, which is then used to generate the directory name ininstalled_toolchains.installation-successfuland.installation-in-progressinstall-logs have been removed. Since toolchains are atomically installed, if the toolchain is symlinked, then it was definitely installed.channel.jsonsnapshot is no longer saved during installs. Now,uninstallrelies on the information saved on the local manifest to uninstall the component. This got changed on 2c39912midenupfails to uninstall components that were installed from pre-compiled artifacts #195.midenup updatewould finish the process accordingly.