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

Add support for removing old state directories on upgrade #6017

Merged
merged 20 commits into from
Mar 16, 2021

Conversation

xoriole
Copy link
Contributor

@xoriole xoriole commented Mar 1, 2021

This PR

  1. Asks user whether to clean up old state directories on upgrade
    settings-data-0

  2. Adds a DATA tab in settings which allows users to selectively select and delete the old state directories.
    settings-data-2

@xoriole
Copy link
Contributor Author

xoriole commented Mar 2, 2021

retest this please

@xoriole xoriole requested a review from ichorid March 2, 2021 17:37
@xoriole xoriole marked this pull request as ready for review March 2, 2021 17:37
Copy link
Contributor

@ichorid ichorid left a comment

Choose a reason for hiding this comment

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

When the "should remove old dirs?" dialog window is shown, if the user waits (thinks) too long and then clicks "No", the GUI never switches from the "Loading" screen. This is reproducible and is probably a race condition between the GUI receiving the "Core started" signal and the dialog window. Also, showing the dialog window before Tribler GUI finished loading falsely indicates to the user that something is wrong.
Please, only show the window after the GUI completely loaded.

Also, please squash the commits 😉

version_dirs_str = "\n- ".join(selected_versions)
versions_info = f"Versions: \n- {version_dirs_str}"

title = "Confirm delete older versions?"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please, change these to
"Really delete old state directories?"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would like to avoid mentioning state directories. I think version is more understandable to users than state directories.

Copy link
Contributor

Choose a reason for hiding this comment

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

"Version" and "state directories" are two different things. I propose asking the guys in the dev chat 😉

Copy link
Collaborator

Choose a reason for hiding this comment

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

To me "version" means binary files of a program, and a suggestion to delete old version I'd probably interpreted as a suggestion to delete old exe/dmg files.

As an alternative to "state directories for selected version" we can probably use "data files for selected version"

versions_info = f"Versions: \n- {version_dirs_str}"

title = "Confirm delete older versions?"
message_body = f"Are you sure to remove the selected versions? " \
Copy link
Contributor

Choose a reason for hiding this comment

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

Better to
"Are you sure you want to remove the state directories for selected versions?"

src/tribler-gui/tribler_gui/widgets/settingspage.py Outdated Show resolved Hide resolved
storage_info += f"{format_size(dir_size)} \t {relpath(old_state_dir, root_state_dir)}\n"

# Show a question to the user asking if the user wants to remove the old data.
title = "Delete older version?"
Copy link
Contributor

Choose a reason for hiding this comment

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

"Delete state directories for old versions?"


# Show a question to the user asking if the user wants to remove the old data.
title = "Delete older version?"
message_body = f"Press 'Yes' to remove data of older versions " \
Copy link
Contributor

Choose a reason for hiding this comment

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

"Press 'Yes' to remove state directories for older versions of Tribler and reclaim {} of storage space."
"Tribler used those directories during upgrades from previous versions. Now those directories can be safely deleted."
"If unsure, press 'No'"
"You will be able to remove those directories from the Settinds->Data page later."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is better 👍


do_cleanup, old_version_dirs = self.should_cleanup_old_versions(root_state_dir, version_id)
if do_cleanup:
self.events_manager.remaining_connection_attempts = 1200
Copy link
Contributor

Choose a reason for hiding this comment

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

This could interfere with the upgrade process and cause other kinds of trouble. Instead, the process must be initiated by the main Tribler Window class after Tribler GUI loads completely.


# Case 0: If the code version and last version is the same, no disposable directories shown at startup
code_version = last_version # 8.9.2
disposable_dirs = get_disposable_state_directories(root_state_dir, code_version, skip_last_version=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

the output of get_disposable_state_directories should be always the same, regardless of whether the code version is there or not. Otherwise, you're mixing decision logic with analysis logic.

Examples: (code version is 8.9.2)

8.7
8.8
8.9
disposable_dirs -> [8.7]
8.7
8.8
disposable_dirs -> []
8.3
8.7
8.8
disposable_dirs -> []

code_version = f"{major_versions[0] + 1}.{minor_versions[0]}.{patch_versions[0]}" # 9.0.0

# Case 1: Skip last version is True, then those two last directories will not returned as disposable dirs.
disposable_dirs = get_disposable_state_directories(root_state_dir, code_version, skip_last_version=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, please don't mix decision logic with analysis logic

assert second_last_version_dir in disposable_dirs


def test_installed_versions_and_removal(tmpdir_factory):
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I don't get what this test is intended to test from the name 🤕


# 3. Skip a few other versions
skip_versions = ['7.5', '7.6']
installed_versions = get_installed_versions(root_state_dir, current_version=False, skip_versions=skip_versions)
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, mixing decision logic with analytical logic

skip_versions = [last_version]

# Get the second last version if available and add it to the dirs to save
second_last_version = version_history.get_last_upgradable_version(root_state_dir, last_version)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do this skip_versions hack instead of just forming a full list of versions and using Python slicing to select every one but the last two?

Copy link
Contributor Author

@xoriole xoriole Mar 3, 2021

Choose a reason for hiding this comment

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

  1. skip_versions is used to give flexibility to choose different version from Settings/Data page.
  2. If only last two version are selected, it does not clean up non-version directories eg. pre 7.4 directories
  3. Full list of version should be sorted separately in the order of installation based on version history, additional logic

Copy link
Contributor

Choose a reason for hiding this comment

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

I would expect choosing the version to delete from the Data page to be completely independent from the process listing the versions to choose from

Full list of version should be sorted separately in the order of installation based on version history, additional logic

I was not clear enough in my request, my fault. By "the full list of versions" I mean "the ordered list of versions". By "ordered" I mean "ordered by last usage time, last usage goes last".

So, the expected logic is:

  1. Get the list of state directories from the directory listing A)
  2. Get the ordered list of versions from the version history file (B)
  3. Filter list B to only contain entries from A
  4. Get B[:-2] and show it to the user. Let the user form the list C of versions to delete.
  5. Delete every version in list C.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure that the B[:-2] logic ("we should suggest to delete everything except two last directories") is correct. If we currently have versions:

  • 8.3
  • 8.4
  • 8.5 (currently running version)
  • 8.6
  • 8.7
    what should we delete? In my opinion, definitely not [8.3, 8.4, 8.5]. I think the correct answer is [8.3, 8.6, 8.7].

@xoriole
Copy link
Contributor Author

xoriole commented Mar 3, 2021

@ichorid

When the "should remove old dirs?" dialog window is shown, if the user waits (thinks) too long and then clicks "No", the GUI never switches from the "Loading" screen. This is reproducible and is probably a race condition between the GUI receiving the "Core started" signal and the dialog window. Also, showing the dialog window before Tribler GUI finished loading falsely indicates to the user that something is wrong.
Please, only show the window after the GUI completely loaded.

It is a good point you pointed out that the GUI could get stuck in the loading screen if user waits too long since the core can timeouts by then. That can be solved by increasing the timeout or pausing it when the dialog is shown. But, showing the dialog only after fully loaded defeats the purpose of the PR to solve the issue with not sufficient storage to upgrade. #5907

Also, please squash the commits wink

Yes, was planning after the review

@ichorid
Copy link
Contributor

ichorid commented Mar 3, 2021

It is a good point you pointed out that the GUI could get stuck in the loading screen if user waits too long since the core can timeouts by then. That can be solved by increasing the timeout or pausing it when the dialog is shown. But, showing the dialog only after fully loaded defeats the purpose of the PR to solve the issue with not sufficient storage to upgrade. #5907

Increasing the timeout is a bad idea. Pausing will work, though. The Core should not be even run before the dialog finishes.

@ghost
Copy link

ghost commented Mar 3, 2021

Congratulations 🎉. DeepCode analyzed your code in 0.431 seconds and we found no issues. Enjoy a moment of no bugs ☀️.

👉 View analysis in DeepCode’s Dashboard | Configure the bot

@xoriole xoriole marked this pull request as draft March 3, 2021 14:14
@kozlovsky
Copy link
Collaborator

kozlovsky commented Mar 8, 2021

Screenshot 2021-03-08 at 10 02 19

  1. Currently, the code suggests deletion of every unknown folder within the root directory. You can see on the screenshot some folders with underscores. These are my "backups" of old versions. Also, you can see three directories for old versions of Tribler, in which the .Tribler folder itself was the data folder: sqlite, channels, and dlcheckpoints. Also, there is a folder "tunnel--1" for tunnel helpers. I don't want to remove any of them except three folders from the ancient version. So, currently, the code suggests deleting every unknown folder inside .Tribler except several known folders (for the current version of Tribler and two previous versions). Probably instead it should only suggest deletion of known folders of previous versions. That is, instead of searching the root directory for all folders, it should construct folder names that are candidates for deletion from the version history.

  2. I think it is better to swap buttons in the dialog to have the default (the safest) button on the right.

  3. As choosing "Yes" lead to data deletion, it is probably better to ask for a second confirmation, like "Press Continue if you really want to delete all these old directories".

Regarding the code itself, it looks good enough for me. I think the code logic may be significantly simplified by introducing TriblerVersion class which is loaded from the version history and combines version name, version number, version directory, and some methods like directory_exists and delete. But this refactoring can be done in a separate PR.

@kozlovsky
Copy link
Collaborator

retest this please

@kozlovsky
Copy link
Collaborator

retest this please

@kozlovsky
Copy link
Collaborator

Retest this please

ichorid
ichorid previously approved these changes Mar 15, 2021
@sonarcloud
Copy link

sonarcloud bot commented Mar 15, 2021

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@kozlovsky kozlovsky marked this pull request as ready for review March 15, 2021 22:43
@kozlovsky kozlovsky merged commit 28e1bce into Tribler:main Mar 16, 2021
@xoriole xoriole deleted the fix/version-upgrade branch August 4, 2021 08:48
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.

None yet

3 participants