-
Notifications
You must be signed in to change notification settings - Fork 452
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
Remove Session god-mediator-object #6206
Conversation
Well you probably won't want to use the launcher for that then. The old format still supported by IPv8. You can pretty much copy this verbatim and insert it wherever you like: tribler/src/tribler-core/tribler_core/session.py Lines 151 to 264 in fecebd6
|
Congratulations 🎉. DeepCode analyzed your code in 2.954 seconds and we found no issues. Enjoy a moment of no bugs ☀️. 👉 View analysis in DeepCode’s Dashboard | Configure the bot👉 The DeepCode service and API will be deprecated in August, 2021. Here is the information how to migrate. Thank you for using DeepCode 🙏 ❤️ !If you are using our plugins, you might be interested in their successors: Snyk's JetBrains plugin and Snyk's VS Code plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have spent a few hours going through this PR, hereby my comments.
Let me start with the positive aspects of this PR. I particularly like that dependencies between Tribler modules are more explicit now. The Session
object used to trickle down to all components, which was kind-of a lazy way of managing dependencies. Dependencies are much more explicit now, so regarding that, job well done! These changes are particularly apparent in the TriblerTunnelCommunity
that heavily relies on various components in the Tribler session object. I see opportunities for follow-up PRs where we can revise some of these dependencies and improve them further (for example, the circular dependency between DownloadManager
and Download
).
Another thing I like is how the tests transition more to real unit tests, where we only initialise a particular component and assess the behaviour of that component in isolation. The changes in this PR are a good step towards that. I also believe that at one point we can think of extracting a few existing tests into dedicated integration tests, where we test the interactions between different components. One thing though is that I am not convinced by your claim that 'tests should become much faster and more parallelizable' by this PR only. I don't think the achieved speedup is that significant but maybe I missed something during my review. We also cannot judge that currently since there are still 156 tests failing.
Having said that, I have two main objections against this PR, one that relates to the content of the PR and another one regarding the nature of the PR.
We agreed last Friday that Tribler benefits from more explicit dependencies. While this PR includes these changes, I was surprised that the whole Session
object has been removed, and I am not convinced that this is a required change to address the upsides you identified. Why not preserve the Session class but redistribute most of the functionality of this class to other components instead? One of the aspects I liked about the Session
object was that it provides a convenient abstraction to work with Tribler (create a Session
, start it, work with it, and shut it down). Now we have to work with Asyncio tasks to achieve this. We have always worked with the Session object and, as I already said, am not convinced it should be killed with fire right now. Even though you can argue that this argument based on history (and maybe emotion) is weak, the truth is that the Session
object has always been an integral component of Tribler, and of software that builds on Tribler (especially Gumby). As apparent by the sheer size of this PR, it is not trivial to change this without introducing other bugs.
Let me also comment on a few of your arguments in favour of removing the session:
no more dumb Session-mocks
This is definitely a good point and these large mocks are something that always bothered me. But at the same time, I argue that the solution to these dumb mocks is not to remove the Session, but rather to refactor or tests to focus on a single argument and not on an entire Tribler session (something you have done in this PR actually). Tribler sessions would still be valuable when having more sophisticated tests (e.g., the tunnel tests).
explicit dependencies between components (almost killed myself with 🤦 a couple of times while untangling those)
You can still have a session object while making these dependencies explicit. I consider removing the session class and making dependencies more explicit as two different (mostly unrelated) tasks.
the ability to make startup sequence asynchronous in the future
How does the Session object prevent you from doing this? Also, I do not think that there are many components that can be asynchronously loaded.
lean and simple startup and shutdown sequences
In this PR, the entire startup/shutdown procedure is moved to a god method (core_session
). This does not make it leaner. As an alternative, I think the way of launching communities (with the not_before
, not_after
etc) is a better way to achieve this goal.
So my second objection is related to the nature of this PR. This PR is currently an amalgamation of many (unrelated) changes. It was definitely not easy for me to navigate through all these changes and to understand the reason behind them. Reviewing this is not for the faint-hearted. To elaborate, here are a few of the (independent) changes that this PR contains:
- Variable renaming (tribler_config -> config)
- Cosmetic changes, for example, in
torrent_checker.py
line 28-30 or inrest_manager.py
, line 60. - The exception handler is moved to a separate class (even though I’m in favour of this change, it could have been done in another PR, making it easier to review stuff and the affected tests).
- Removing the
setup
function from theDownload
class. - Removing dead code (e.g., the
safe_files_to_remove
method). - Combining the
ChannelsEndpointBase
andChannelsEndpoint
classes. - The removal of community launchers.
To properly review this PR, one has to be an expert on all areas of Tribler, and having these unrelated changes in a single PR makes it unnecessarily harder to find potential bugs. It also does not help that this PR is mostly in an unfinished state. The PR could have been much more focussed, only touching the changes that we discussed last Friday (which I believed just concerned making dependencies more explicit). Is there a particular reason why all of these changes have to be included in a single super-commit/super-PR?
Minor comment: I believe there are a few unintended files committed to src/tribler-core/tribler_core/modules/bandwidth_accounting
.
src/tribler-core/tribler_core/modules/bandwidth_accounting/database.py
Outdated
Show resolved
Hide resolved
|
||
def load_communities(config: TriblerConfig, trustchain_keypair, ipv8, dlmgr, metadata_store, | ||
torrent_checker, notifier, bootstrapper): | ||
""" This method will be splitted after grand Vadim's PR is merged |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not fix it in this PR? Doesn't seem to require much work and it would make the review easier.
(I do realise that this contradicts a bit with my other points on incremental PRs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed it in this PR yesterday.
The core idea is to explicitly pass communities into core_session
in DI style:
async def core_session(
config: TriblerConfig,
communities_cls: List[Factory],
shutdown_event = Event()
):
...
for community_cls in communities_cls:
community = community_cls.create_class(peer, ipv8.endpoint, ipv8.network, mediator=mediator, **community_cls.kwargs)
community.fill_mediator(mediator)
for strategy in community.strategies:
ipv8.strategies.append((strategy.create_class(community), strategy.target_peers))
ipv8.overlays.append(community)
So, there are two types of applications that use core_session
(except tests):
- Applications that rely on config "enable/disable" flags for the communities (Tribler itself)
- Applications that don't rely on config "enable/disable" flags for the communities (helpers and experiments)
For the first group the list can be created as:
run_tribler.py
def communities_gen(config: TriblerConfig):
yield Factory(create_class=TriblerDiscoveryCommunity) if config.discovery_community.enabled else ...
yield Factory(create_class=TriblerDHTDiscoveryCommunity) if config.dht.enabled else ...
bandwidth_accounting_kwargs = {'database': config.state_dir / "sqlite" / "bandwidth.db"}
bandwidth_accounting_cls = BandwidthAccountingTestnetCommunity if config.general.testnet or config.bandwidth_accounting.testnet else BandwidthAccountingCommunity
yield Factory(create_class=bandwidth_accounting_cls, kwargs=bandwidth_accounting_kwargs)
tribler_tunnel_cls = TriblerTunnelTestnetCommunity if config.general.testnet or config.tunnel_community.testnet else TriblerTunnelCommunity
yield Factory(create_class=tribler_tunnel_cls) if config.tunnel_community.enabled else ...
yield Factory(create_class=PopularityCommunity) if config.popularity_community.enabled else ...
giga_channel_cls = GigaChannelTestnetCommunity if config.general.testnet else GigaChannelCommunity
yield Factory(create_class=giga_channel_cls) if config.chant.enabled else ...
For the second group the list can be created as:
initial_filling.py
def communities(self):
return [
Factory(create_class=TriblerDiscoveryCommunity),
Factory(create_class=TriblerDHTDiscoveryCommunity),
Factory(create_class=ObservablePopularityCommunity,
kwargs={'interval_in_sec': self._interval_in_sec,
'output_file_path': self._output_file_path}),
]
self.handle.set_max_connections(self.session.config.libtorrent.max_connections_download) | ||
|
||
# Set limit on download for a bootstrap file | ||
if self.config.get_bootstrap_download(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though I know that the bootstrap download is currently disabled, removing this code might backfire in the future if we decide to revive this feature again.
self.trust_graph = TrustGraph(self.public_key, self.bandwidth_db) | ||
|
||
# Start bootstrap download if not already done | ||
if not self.session.bootstrap: | ||
self.session.start_bootstrap_download() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my previous comment on removing code related to bootstrapping.
config.popularity_community.enabled = True | ||
config.chant.enabled = True | ||
|
||
community_loader = create_default_loader(config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create_default_loader
is undefined at the moment. This test probably has to be redesigned.
722ac74
to
c69f55c
Compare
src/tribler-core/tribler_core/modules/tests/bandwidth_accounting/test_bandwidth_endpoint.py
Show resolved
Hide resolved
retest this please |
1 similar comment
retest this please |
retest this please |
3 similar comments
retest this please |
retest this please |
retest this please |
community = DiscoveryCommunity(self.peer, ipv8.endpoint, ipv8.network, max_peers=100) | ||
ipv8.strategies.append((RandomChurn(community), INFINITE)) | ||
ipv8.strategies.append((PeriodicSimilarity(community), INFINITE)) | ||
ipv8.strategies.append((RandomWalk(community), INFINITE)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ipv8.strategies.append((RandomWalk(community), INFINITE)) | |
ipv8.strategies.append((RandomWalk(community), 20)) |
The current PR code would disproportionally (regarding the connections in other network overlays) introduce you to peers you're not interested in. Of course you want some random connections, but not an "infinite" amount.
Trivia: 20 is the default setting.
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
🎉 💀 Session is dead 💀 🎉
Dear All!
This is our chance to slay ⚔️ the wicked Session 🐉 once and for all, thus fulfilling the ancient prophecy 🧙 of freedom from 🏇 race conditions.
I managed to completely remove Session from
run_tribler
which works fine (aside from statistics endpoint). Any kind of help with this PR is very appreciated, that's why I added the branch to our main repo 🙏. Just make PRs on this PR, as we usually do with "collective effort" stuff.Upsides
Some arguments for removing Session:
object is None
race conditions on startup/shutdownapp
has been started, so Upgrader notifications do not work sadly. We'll have to figure that out.ProstethicSession
workaround object to make the thing work. Dear @qstokkink , would you kindly make IPv8 loading procedure follow the dependency injection pattern and accept the exact dependencies instead of the Session mediator-object? 😉UPDATE:
after completing refactoring of the tests suite, it can now be run completely in parallel with
pytest-xdist
. On my machine (Core i7 6 cores) it takes only 18 seconds to complete the whole suite. Yay! 🎉