-
-
Notifications
You must be signed in to change notification settings - Fork 834
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
Basic Extension Dependency Support #2188
Conversation
ac7cf99
to
f19bbd6
Compare
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 left some comments. But I'd like to discuss the need or maybe the implementation first. This has to be looked at by @franzliedke.
Why would we need a separate field for declaring extensions we depend on? Composer already has Anyway, I won't look at this in more detail until we've made more progress with the frontend rewrite, sorry. |
Composers require doesn't tell us what is and isn't a flarum extension, so if something isn't installed, we don't know if it's needed. Also, we'd need to do a lot more unnecessary computations to scan every extensions composer json whenever we add an extension. Overall this will just result in a clearer codebase imo.
Sorry I should have made it clearer in the description: this is very much a "I wanna code something useful but don't want to add to Franz's workload so I'm gonna explore this cool idea" PR. |
Ah, but we have the information about all Composer-installed packages in memory already, just before the |
I see your point, I still think the code would be a bit messy. But let's table this for now, and revisit after frontend / extenders? |
4cda9bb
to
b47a013
Compare
It now uses dependencies from require, and I was able to do it without significantly messing up big O complexity or code convolution! |
1f4266e
to
41263c0
Compare
What's the memory footprint increase of this? I'm not going to approve this PR unless we know the downside in terms of performance. Having said so, I'm not sure this is the right approach. This code is executed on each request, even though it is only needed during the installation of an extension. I would vote to move this to a composer lifecycle hook (post update/install cmd) instead if possible. That would be my first attempt. |
The process for figuring out which extensions are dependent on which has an O(n) time complexity, and a O(n) space complexity (for creating the hash table of php package names to composer.json files, ie $associativeInstalled). The process of checking whether dependencies are violated have an O(n * m) time compexity (n being number of extensions, m being average number of dependencies per extension). This is, however, inconsequential as it's only run when enabling / disabling an extension, not with every request. The resolution of extension dependency order has an O(n + v) time complexity (as it's just Kahn's algorithm), with n being the number of extensions, and v being the number of vertices in the dependency graph (ie the sum of each extension's number of dependencies). None of these are especially high, and all of these scale well. That being said, this code doesn't just check for whether something should be enabled/disabled, but also ensures that extensions are booted in the proper order, every time. That's why I think it should run on every request. Also, does PHP really boot up the entire system on every request? There isn't a way to boot the application and have it await requests as they come in as a process running in the background? |
@askvortsov1 Standard PHP does not have a way to keep the application running, there are of course PHP work arounds for this using special libraries and stuff but that's not for Flarum to get into. Of course any production environment will have OPCache or APC cache enabled which does store the compiled PHP files so they execute faster. Once PHP 8 is out we'll also get to see how JIT does with performance. |
7825b50
to
a1f2e84
Compare
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 see a bunch of unrelated commits which I guess come from different PRs. It doesn't help to figure out what's part of this PR... Two things below.
src/Extension/ExtensionManager.php
Outdated
@@ -296,7 +338,9 @@ public function getEnabledExtensions() | |||
*/ | |||
public function extend(Container $app) | |||
{ | |||
foreach ($this->getEnabledExtensions() as $extension) { | |||
$enabled = static::resolveExtensionOrder($this->getEnabledExtensions()); |
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.
What about doing this whenever an extension is enabled, and save the new order in the database? That way there's no need to do any computation when Flarum boots.
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 suppose we could... But this way, it'll always be right (even if someone messes with the database), and decouples the storage of enabled extensions from the order in which they're booted. Plus, topological sort runs really fast, it'd hardly be a bottleneck compared to other aspects of the site.
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'm going to have to agree with Sasha here, although it's rare that the database gets modified directly it does happen sometimes. especially during some of our more complex troubleshooting processes.
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.
And I am going to agree with @clarkwinkelmann here. This is all superfluous code that, by itself, might not seriously increase loading times (first-byte) of our forum. But each and every of these kind of decisions will impede performance over time. This scenario, with this code, is especially true with forums that have a ton of extensions - and trust me that will sadly be the reality.
Honestly I would even make this a post update/install hook by saving some kind of order map of all installed extensions to a file or the database. Then during extension enable/disable retrieve this order and store the order of enabled extensions to the database. If anyone decides to manually override that order from the database, they have good reason to do so and don't particularly would hope to see something override that order magically..
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.
Further review into this, I'm going to agree with @luceos and @clarkwinkelmann, I also agree with luceos idea of making this a post update/install hook for composer to store the order map. Further the database edits by people are incredibly rare and if someone did do it on accident (or purpose) we could simply inform them to run the same command that composer does to resolve it.
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 spent some more time thinking about this, and just the dependency checks on enabling / disabling will be a HUGE improvement over what we have now (and much, much simpler to review). And that should also result in a working order.
I'll remove the algorithms stuff and move it over to the extension management commands PR (we'll need it if we're trying to enable/disable extensions in a working order). No need to get everything done in a single PR.
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.
Are you saying this PR will only contain logic to check whether depending extensions are installed? Sounds like a smart move. The composer hook should be quite simple too, but needs some work.
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.
Yep! That way, we can split this into separate PRs and have the composer hook later.
As noted earlier somewhere, due to our current system, if we have this check in place before enabling/disabling extensions, that'll prevent the vast majority of issues (since load order currently depends on order of enabling, and this way, you won't be able to enable anything.
The composer hook stuff would be in a separate PR, definitely not aiming for beta 14.
cca7d1f
to
c73df92
Compare
Yeah that was a messy rebase... sorry about that. All cleaned up now! |
8c63ff2
to
161cd57
Compare
…iative array to their package.jsons to save memory
ff3f9e9
to
ebe0107
Compare
…sn't a flarum extension instead.
…stalled extensions
changing key in collection is not the way to go
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.
Other than the note from luceos I'm good with these changes
We just discovered on discord that the change to use the full package name as key is a breaking change and will impact the getExtension method amongst other. So reverted my approval. |
- Make extensionDependencies protected, call through encapsulated method - The dependency exceptions now store arrays of extensions, not arrays of extension ids. - Improve documentation
[ci skip] [skip ci]
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 see I have been mentioned previously, but I can't find the message that was notified by email. In the current state of the PR, I'm not seeing any issue with other extensions.
I was about to comment on the enabled**Ids` thing, but I see a commit was just pushed fixing it.
[ci skip] [skip ci]
When the commits stop flowing in I might approve 😛 |
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 shared some ideas regarding the error message on Discord, suggesting to maybe show title together with ID. But the current implementation is fine for the time being.
Refs #2093
Refs #1318
Changes proposed in this pull request:
Confirmed
composer test
).Screenshots
Error when disabling with dependent: https://i.imgur.com/X2npKFt.png
Error when enabling with missing dependencies: https://i.imgur.com/rioQMXc.png
Lang English PR: flarum/lang-english#172