Skip to content
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

[Updates] Create 'volta-migrate' binary to handle layout migrations #590

Merged
merged 12 commits into from
Dec 3, 2019

Conversation

charlespierce
Copy link
Contributor

Closes #566

Info

  • To support background updates (either automatic or managed) when the layout of the Volta directory may change between versions, we need a way to migrate from one version of a layout to the most recent.
  • This migration should be checked on every execution, to make sure that the Volta system is up-to-date with the executables, however that check should be fast so as not to adversely affect the hot-path of shims.
  • The migration itself should be handled by a separate executable, to prevent the primary executables (volta and volta-shim) from growing unbounded in size as we add more migrations.

Changes

  • Added a new executable volta-migrate that is responsible for performing the actual migration. This executable is called by volta and the shims if a discrepancy is detected between the binaries and the current layout.
  • Added a new crate volta-migrate that contains the migration logic, including detecting the specific current layout and executing any migrations that are needed.
  • Updated the installers to include volta-migrate along side the other binaries.
  • Added acceptance tests for the different migration cases (currently they are very similar because the migration is primarily only creating the layout file).

Notes

@charlespierce charlespierce changed the title Volta migrate [Updates] Create 'volta-migrate' binary to handle layout migrations Nov 5, 2019
@rwjblue
Copy link
Contributor

rwjblue commented Nov 5, 2019

This migration should be checked on every execution, to make sure that the Volta system is up-to-date with the executables, however that check should be fast so as not to adversely affect the hot-path of shims.

This particular point is super important to me, can you explain (at a high level) how the check can be done without doing a bunch of extra I/O during the "hot path" of a specific shim/tool execution?

@charlespierce
Copy link
Contributor Author

@rwjblue Absolutely! We're using a marker file that indicates the current layout (at the moment, it's v1, so the file is layout.v1) in the root of ~/.volta. The hot paths check if that file exists. If it does, we're on the latest layout and we continue as normal. If it doesn't, then we launch volta-migrate, which does more involved logic to determine whether this is an empty layout or a previous version, and then does the appropriate migration.

@rwjblue
Copy link
Contributor

rwjblue commented Nov 5, 2019

We're using a marker file that indicates the current layout (at the moment, it's v1, so the file is layout.v1) in the root of ~/.volta. The hot paths check if that file exists.

Gotcha, thank you! So I'm still confused on the exact I/O operations that we expect in various cases. Instead of guessing, I thought maybe we could chat through various scenarios, do you mind?

Assuming that is fine, here are a few scenarios that I had in mind:

  • The current ~/.volta installation is on v1, but the the installed binaries are from v2. What I/O will be done? (from your reply it seems like a single fs.stat to see if layout.v1 exists, and update?)
  • The current ~/.volta installation is on v2, and the binaries are on v2; what I/O operations do we do? fs.stat('layout.v1') and fs.stat('layout.v2')?
  • The current ~/.volta installation is on v1, and the binaries are on v5; what I/O operations do we do?
  • The current ~/.volta is "empty" (or missing?), and the binaries are on v2; what does it do?

@rwjblue
Copy link
Contributor

rwjblue commented Nov 5, 2019

Hmm, as I re-read what you said in your reply:

We're using a marker file that indicates the current layout (at the moment, it's v1, so the file is layout.v1) in the root of ~/.volta. The hot paths check if that file exists. If it does, we're on the latest layout and we continue as normal.

It seems that I misunderstood/misread. Is it correct to say that in the hot path (e.g. node or other tool invocation) that we would only check for the current layout version file (e.g. in v2 binary, we only check for ~/.volta/layout.v2) and if not found we launch the more expensive tool (presumably only one time)?

@charlespierce
Copy link
Contributor Author

@rwjblue That’s it exactly. To ensure we’re on the correct layout version, we check for the current layout file. If that isn’t there, then we launch volta-migrate, which has more complicated and expensive logic to determine what migrations are needed and carry them out.

@charlespierce charlespierce force-pushed the volta_migrate branch 3 times, most recently from ff7b5d5 to a74f1c2 Compare November 11, 2019 20:44
@charlespierce charlespierce marked this pull request as ready for review November 20, 2019 01:32
@charlespierce
Copy link
Contributor Author

Closing and reopening to debug CI not starting

Copy link
Contributor

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions, and a lot of admiration. This is really good, really well-thought-through, really well tested. 👏

crates/volta-core/src/layout/mod.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/empty.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Show resolved Hide resolved
crates/volta-migrate/src/v1.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/v1.rs Outdated Show resolved Hide resolved
src/common.rs Show resolved Hide resolved
Copy link
Collaborator

@dherman dherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! I'm so excited to have auto-migrations coming. I honestly thought it was much further away, but you made it happen. :D

I left some questions and suggestions, but I'm not entirely confident of all of my feedback. Happy to discuss!

One overall comment: feature flags make it a little easier to break up PRs. This one is about 1k lines of changes. It's pretty daunting to review -- in the future you might want to aim to chunk it up a little more.

crates/volta-core/src/error/details.rs Outdated Show resolved Hide resolved
crates/volta-core/src/error/details.rs Outdated Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
crates/volta-core/src/layout/mod.rs Show resolved Hide resolved
crates/volta-core/src/shim.rs Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Outdated Show resolved Hide resolved
crates/volta-migrate/src/lib.rs Show resolved Hide resolved
fn regenerate_shims_for_dir(dir: &Path) -> Fallible<()> {
debug!("Rebuilding shims");
for shim_name in get_shim_list_deduped(dir)?.iter() {
shim::delete(shim_name)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite see how deleting and recreating makes it possible to gracefully restart. Is the idea that anything you delete is recreate-able? I'm just a little nervous you could delete some data somewhere along the line that you can't recreate.

Would it be safer to recreate/copy everything fresh to a temp directory and then have an atomic (or almost-atomic, anyway) directory swap at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point that this isn't going support graceful restarting. Since the shims are symlinks, the only way (I believe) to re-target them is to remove and recreate them. For these specifically, I think we can probably do a temp directory version, since we can read through the existing directory, create whatever contents / shims we need in the temp, and then swap them over.

I initially wanted this (and the writing of the layout file) to be separated from individual migrations since it would potentially be duplicating work to have them run with each "step". However, thinking more deeply, we actually do want these operations to happen at each migration, because that lets the graceful restart actually apply happen: Any migrations that have completed will be skipped on subsequent restarts, leaving only the remaining work. It also prevents us from missing a regeneration of the shims or having to include it in every migration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Letting this percolate overnight and looking at the code this morning, I'm actually not sure there is a more "restartable" solution. The issue with a temporary directory is that if we get a failure during removing the existing directory or while renameing the temp directory, we lose all the contents of the shim directory (which could also include the volta binaries themselves). While this approach of removing and re-creating the shims has more potential points of failure, each failure will only affect a single shim, as opposed to the entire directory. This means at worst (if we successfully delete a shim but then fail to re-create it), we lose information about a single shim, and the error should give information about which shim failed.

It won't be an easy thing to recover from, but it will be easier than if we failed the operation in a way that lost all of the information about the shim directory at once.

src/volta-shim.rs Show resolved Hide resolved
src/main.rs Outdated Show resolved Hide resolved
@charlespierce
Copy link
Contributor Author

@dherman There was a lot of discussion on Discord about the concurrency problems and corruption, but are there any outstanding concerns about this PR? I'm happy to address if we want to make changes before merging, I'm just not 100% sure where we landed, as there were a lot of hard problems discussed 😄

Copy link
Collaborator

@dherman dherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ship it! 🚢

(just one typo in a comment)

crates/volta-migrate/src/lib.rs Outdated Show resolved Hide resolved
@charlespierce charlespierce merged commit d28d8b7 into volta-cli:master Dec 3, 2019
@charlespierce charlespierce deleted the volta_migrate branch December 3, 2019 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Updates] Create volta-migrate crate / binary
4 participants