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

[PRE-WORK] VPN-5854 - Refactor storage management #8820

Merged
merged 16 commits into from
Dec 21, 2023
Merged

Conversation

brizental
Copy link
Contributor

@brizental brizental commented Dec 15, 2023

This PR is on top of #8819, please ignore the first two commits on it. That has been merged.

This PR is also pre-work for VPN-5854, in PR #8821.

Experiment feature have attached settings, but the way our SettingsHolder is implemented makes it hard to dynamically add settings. There is an underlying assumption that all settings will be defined at build time in the settingslist.h file.

In order to introduce the experimental settings features I had a few options that were all bit a hacky. Such as doing what we did for addon features and adding addon-specific (or in this case, experiment specific) functions to the SettingsHolder. Or just bypassing the SettingsHolder completely and just creating a different QSettings location for experiments settings.

None of these options made sense to me and I decided to refactor the whole settings managent in the app, to be more flexible towards dynamic creation of metrics.

The new design has the following pieces.

  • The SettingsManager singleton, which manages the underlying QSettings location and serves as a bucket for all Settings in the app.
  • The Setting class, which is an abstraction over one single setting in the underlying QSettings store.
  • The SettingFactory class, which exposes the only public constructors for Setting objects. It guarantees all new Setting objects that are created are created in the heap and are immediatelly registered to the SettingsBase class. It also guarantees that no duplicates are ever added to the SettingsManager class.
  • The SettingGroup class, which is an abstraction over the concept of a group of metrics. All Settings under a SettingGroup are under the same QSettings group as well i.e. the key for all of them share the same prefix followed by a slash. SettingGroups are useful for observing changes to a group of related metrics or for grouping metrics in a semantic way.

I have intentionally not renamed or moved the SettingsHolder class and files. The objective was to refactor everything without having to touch the whol codebase to change paths and calls to SettingsHolder functions. If we feel that is something we want to do, we can file a follow up ticket to do so.

Note This ia draft because the QML test I added is failing. It is possible and I would love to get feedback on the approach while fixing that though. Fixed.

src/settings/settingsbase.h Outdated Show resolved Hide resolved
src/settings/settingsbase.h Outdated Show resolved Hide resolved
src/settings/setting.h Outdated Show resolved Hide resolved
@brizental brizental requested a review from vinocher December 18, 2023 11:21
@brizental brizental force-pushed the pre-experiments-work branch from f090870 to 257ac57 Compare December 18, 2023 11:23
@brizental brizental marked this pull request as ready for review December 18, 2023 12:50
@brizental brizental changed the base branch from main to pre-pre-experiments-work December 18, 2023 16:52
Base automatically changed from pre-pre-experiments-work to main December 18, 2023 17:03
Yeah, still learning about managing lifetimes without having
my hand held by the compiler. (I miss Rust).

Turns out I would have to cache the value of the setting in the SettigsGroup object for the previous design to work... I think that would not make much sense so I decided to diverge from the initial design and just expose the getter function itself.
@brizental brizental force-pushed the pre-experiments-work branch from 7671674 to 6455399 Compare December 18, 2023 17:06
src/settings/setting.h Show resolved Hide resolved
src/settings/setting.cpp Outdated Show resolved Hide resolved
src/settings/settingfactory.cpp Outdated Show resolved Hide resolved
src/settings/settinggroup.cpp Outdated Show resolved Hide resolved
src/settings/settinggroup.cpp Outdated Show resolved Hide resolved
src/settings/settingsmanager.cpp Outdated Show resolved Hide resolved
src/settings/settingsmanager.h Outdated Show resolved Hide resolved
src/settingsholder.cpp Show resolved Hide resolved
tests/unit_tests/testaddon.cpp Outdated Show resolved Hide resolved
tests/unit_tests/testaddon.cpp Show resolved Hide resolved
src/settings/qsettingsconnector.h Outdated Show resolved Hide resolved
src/settings/setting.h Outdated Show resolved Hide resolved
src/settings/setting.h Outdated Show resolved Hide resolved
src/settings/settinggroup.h Outdated Show resolved Hide resolved
src/settings/settingsmanager.h Outdated Show resolved Hide resolved
src/mozillavpn.cpp Outdated Show resolved Hide resolved
src/settings/settingsmanager.h Show resolved Hide resolved
src/addons/addon.h Outdated Show resolved Hide resolved
src/addons/addonmessage.h Outdated Show resolved Hide resolved
src/settings/settinggroup.cpp Show resolved Hide resolved
src/settings/settingsmanager.cpp Show resolved Hide resolved
src/settings/settinggroup.h Outdated Show resolved Hide resolved
src/settings/settingsmanager.cpp Show resolved Hide resolved
src/settings/settingsmanager.h Outdated Show resolved Hide resolved
src/settings/settingsmanager.h Outdated Show resolved Hide resolved
src/settings/setting.cpp Show resolved Hide resolved
src/settings/settingsmanager.h Show resolved Hide resolved

#include "helper.h"

class TestSettingsManager final : public TestHelper {
Copy link
Contributor

Choose a reason for hiding this comment

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

Great to see all these tests!

Q_ASSERT(SettingsManager::instance()->getSetting("donotreset"));

// But it will clear the storage, if the setting is setup to do that.
QVERIFY(doReset->get().isNull());
Copy link
Contributor

Choose a reason for hiding this comment

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

Since reset() did not unregister doReset, is the caller expected to call SettingsManager::createOrGetSetting again to re-register?
Should SettingsManager.sync() be expected to register this setting again (although the current implementation doesn't support that)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, the caller is not expected to do so. I am thinking now that hardReset should not unregister the settings either. Neither method is guaranteed to be called in a situation where unregistering is the right move, which could lead to segfaults due to the unexpected behaviour. Remove also shouldn't unregister and so on.

Think about the situation where the user does a hardReset and then keeps using the app. (That is not possible currently, there is only hardResetAndQuit, but there is also nothing stopping us from implementing it.) In this situation suddenly calling any method on the SettingsHolder will result in a segfault.

Another option is to use shared pointers, but that would create the issue that a pointer to a setting exists that the SettingsManager is not aware of -- which invites another host of issues.

I'll go ahead and remove unregistering logic from everywhere but the destructor. Let me know if you disagree.

tests/unit_tests/settings/testsettinggroup.cpp Outdated Show resolved Hide resolved
@brizental brizental requested a review from vinocher December 21, 2023 13:33
Comment on lines +27 to +28
* The only public constructor for Setting objects is
* SettingsManager::instance()->createOrGetSetting, therefore everytime a
Copy link
Contributor

Choose a reason for hiding this comment

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

The Settings constructor is public, so it could be created using new Settings(...). However, not a big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I think though, since the only public constructor requires SettingsConnector it might as well be private, because it's just too hard to use outside of the SettingsManager :D

Copy link
Contributor

@vinocher vinocher left a comment

Choose a reason for hiding this comment

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

Looks great!

@brizental brizental enabled auto-merge (squash) December 21, 2023 18:57
@github-actions github-actions bot added the 🛬 Landing This PR is marked as "auto-merge" label Dec 21, 2023
@brizental brizental merged commit 2e38e8d into main Dec 21, 2023
126 checks passed
@brizental brizental deleted the pre-experiments-work branch December 21, 2023 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🛬 Landing This PR is marked as "auto-merge"
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants