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

refactor restore command #741

Merged
merged 1 commit into from
Jul 11, 2023
Merged

refactor restore command #741

merged 1 commit into from
Jul 11, 2023

Conversation

aawsome
Copy link
Member

@aawsome aawsome commented Jul 10, 2023

No description provided.

}

/// collect restore information, scan existing files and remove superfluous files
pub(crate) fn collect_and_remove<P: ProgressBars, S: Indexed>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we split up this method into something that collects and something that processes? It's currently nearly 200 LOC, which makes it hard to test in the future and also not easy to maintain, I feel.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, agree that the restore command is pretty complex on one hand and pretty optimized on the other hand...
I also thought about improving it, but then decided to do minimal changes in order to present the command in the API.

Can we work on the restore command in future?
Yes, it is very ugly that there is no possible repo method which would just analyze what needs to be done. But for instance regarding the removal of existing files, you have two alternatives:

  • save the files to-remove in some kind of list (which can be very memory consuming if you delete a lot by this command)
  • scan the destination (a the repository tree) a second time which can be quite costly.

I think refactoring this would need a bit more thinking and hence I don't want to do this parallel to the current task.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, probably we should write unit tests when refactoring it into smaller parts then later!


/// [`restore_contents`] restores all files contents as described by `file_infos`
/// using the [`DecryptReadBackend`] `be` and writing them into the [`LocalBackend`] `dest`.
fn restore_contents<P: ProgressBars, S: Open>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment, could we split this one as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't do this with this change. This function is basically just moved from the CLI crate where it was as this. At least now this function is very local within rustic_core and can be easily refactored.

Comment on lines +550 to +535
// TODO: This is the same logic as in backend/ignore.rs => consollidate!
let mtime = meta
.modified()
.ok()
.map(|t| DateTime::<Utc>::from(t).with_timezone(&Local));
if meta.len() == file.meta.size && mtime == file.meta.mtime {
// File exists with fitting mtime => we suspect this file is ok!
debug!("file {name:?} exists with suitable size and mtime, accepting it!");
self.matched_size += file.meta.size;
return Ok(AddFileResult::Existing);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this is a good candidate to split out as well.

@aawsome
Copy link
Member Author

aawsome commented Jul 10, 2023

I added an example in the example dir.

@aawsome aawsome force-pushed the refactor-restore branch 2 times, most recently from abda6f1 to fe6bd32 Compare July 11, 2023 01:38
..Default::default()
};

let repo = Repository::new(&repo_opts)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let repo = Repository::new(&repo_opts)
let repo = Repository::with_options(&repo_opts)

Maybe it's better to make it explicit?

.unwrap();

// use latest snapshot without filtering snapshots
let node = repo.node_from_snapshot_path("latest", |_| true).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

These parameters could be a reasonable default. Like latest snapshot without filtering and may be a good candidate for an own method.

Copy link
Member Author

Choose a reason for hiding this comment

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

IMO, it is actually vice versa: When you want the latest snapshot, you usually specify extra conditions of w.r.t. what you want the latest snapshot.
On the other hand, if you specify a snapshots by (part of the) ID, you usually don't need any filtering as the snapshot is already defined.

I think the use cases how to get to a certain node which you want to restore will vary a lot depending on the user interface you are using. If you have a CLI, the node_from_snapshot_path is quite handy as it allows to give a string and will automatically resolve ids or latest snapshots from the string.
If you have some kind of GUI, I would prefer just fetching all snapshots using repo.get_matching_snapshots() or even repo.get_all_snapshots() (not present yet but would be repo.get_matching_snapshots() without filtering). Then a user could manually dive into some tree and pick the Node to be used for the restore.

TL;DR: Yes, we need to think about the API, but here I wouldn't do so ;-)

Copy link
Contributor

Choose a reason for hiding this comment

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

These notes are also meant as some future reference when we work on the API, by the way. Not really to change here, just so we are on the same boat (hence why I chose the comment instead of changes review feature).

For example, I would prefer both, get_all_snapshots() as a wrapper around (in this case) get_matching_snapshots() and get_latest_snapshot() etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agree! 👍

Comment on lines +28 to +42
// use list of the snapshot contents using no additional filtering
let recursive = true;
let streamer_opts = TreeStreamerOptions::default();
let ls = repo.ls(&node, &streamer_opts, recursive).unwrap();

let destination = "./restore/"; // restore to this destination dir
let create = true; // create destination dir, if it doesn't exist
let dest = LocalDestination::new(destination, create, !node.is_dir()).unwrap();

let opts = RestoreOpts::default();
let dry_run = false;
// create restore infos. Note: this also already creates needed dirs in the destination
let restore_infos = repo
.prepare_restore(&opts, ls.clone(), &dest, dry_run)
.unwrap();
Copy link
Contributor

@simonsan simonsan Jul 11, 2023

Choose a reason for hiding this comment

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

I think this is overly verbose for the user-facing API. I think we should either replace all boolean values that are user-facing for these kinds of options with enums here, or wrap the booleans with a new type.

We could also use a DescriptorStruct that we pass to T.restore(), so we are able to have all the settings for the user in one place exposing certain values as reasonable defaults, could even combine that with a builder for the DescriptorStruct:
rust-unofficial/patterns#239 (comment)

@aawsome aawsome merged commit 2eced27 into main Jul 11, 2023
17 checks passed
@aawsome aawsome deleted the refactor-restore branch July 11, 2023 12:10
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.

2 participants