-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Plugin system should fail if Setup lifecycle functionality is called after the setup lifecycle completes. #105439
Comments
Pinging @elastic/kibana-core (Team:Core) |
I'd love further clarification on what constitutes I also think we need to consider the needs of the Kibana page bundle. It's not clear that your plugin's I don't think we can argue that adding hundreds of kb to the page bundle is a winning trade-off for plugin lifecycle purity. We either need to strike a balance, provide lifecycle hooks that allow for it, or refine the philosophy... or all of the above. |
Something else to consider, and why I'd be a fan of discussing expanding the plugin lifecycle... Canvas has no dependents, (at the moment). We expose nothing of use or to be used by other plugins, and yet our So we're likely violating this premise as of #104264 ... I've been experimenting with async loading of function bodies, but it still has a large impact on the page bundle, (see #104990) So it might make sense to consider a lifecycle option that uses setup functionality safely on mount. At the same time, I'm not a wonk in this respect... but I do have to answer for the code we add to the bundle. Perhaps it would be useful to treat Canvas as a case study in lifecycle use and optimization? I'd be happy to be the guinea pig for this, particularly if it will help other plugins do the same! (cc: @spalger and @tylersmalley) |
IMO the answer to this is yes. The whole point of the lifecycles is to provide plugins with guarantees as to when they can expect setup tasks to be complete. If we start performing these tasks whenever we want to, it makes Kibana less predictable and therefore less stable.
This is an interesting idea and I think it would be possible, but we'd need to spend some time discussing how this would look in practice. cc @elastic/kibana-core WDYT?
I think this is creating a false dichotomy: This is not a choice between larger bundles or more lifecycle methods. In most cases (including Canvas) the root cause of the large bundle sizes is a large number of items that need to be registered to a shared service during As I've mentioned in the Expression leasing PR and a few other places, I think one way to solve this problem is by revisiting how we design our registries. If we build more registries to be async, you would be able to register a promise that can lazily import a registry item. The app services team has an issue discussing this here: #65993 |
👍
I defer to the Kibana Core team on the implementation details of this. |
I can certainly take action as stated in #105164 (comment) to absolve Canvas of the anti-pattern. But the idea that it's |
Ah, thanks for the clarification. Totally agreed 👍 Regardless of how/if core starts enforcing this in the plugin system, we should clarify these points in the documentation as part of the scope of this issue. |
I think my concern is: do these need to be registered globally in Kibana? The answer in our case is no... in Canvas, we have no need or desire for these functions to be used anywhere else, and protecting those constructs from use elsewhere is no different than not exporting code from As @lukeelmers pointed out, we can simply fork the Expressions service and continue to isolate these functions, (and give us the flexibility to deprecate and change them as we see fit)... as one option. But if the lifecycle dictates that the |
I've created #66366 to enforce separation between the lifecycles, but we've never had time to work on it. Should we move this discussion to #66366?
Let us know what we can improve in the lifecycle docs https://www.elastic.co/guide/en/kibana/current/kibana-platform-plugin-api.html#plugin-lifecycles
IIRC we discussed using Proxy in |
I've created #105675 to track us fixing the Canvas Plugin inclusion of this anti-pattern. As it turns out, the Canvas plugin lifecycle is kind of tangled up at the moment, but things are also poorly-named. I think a lot of this is left over from not only the New Platform migration, but also from Canvas being conceived as a stand-alone plugin, outside the Kibana repo. So there's a lot to unpack and untangle. Canvas uses the From there, it should be trivial to decide how to remove the call to Be aware: Lens would be subject to failure under this new rule, but I think their fix would be just as easy if they either 1/ consider a So I doubt I have further objection to this kind of guard. But honestly, I'd prefer education to failure, but I leave that to Kibana Core, (like @kobelb so wisely suggested) :-D |
@clintandrewhall |
I would second what Stacey and Luke said here, we should make a clear separation between If we know the object registered into them is already large, we should by default design them as accepting a callback function returning a promise, so we can async load that code when the library is accessed not when registered. Given that a lot of our registries are around for some time (longer than the Kibana Platform) that would def need some refactoring, but I think it's the better way. There might also be other issues (like in this Canvas) example, that are not directly linked to the registry itself, but the overall problem, that Clint mentioned that we need to register items we only need within a plugin in a global registry in another plugin. But also in this case I think we should strive for the right solution then, and make sure that if we consider those to be absolute private to Canvas, they should never need to be registered outside, and basically be passed into the expression executor as a "local registry" solely maintained within Canvas (in which case the page laod bundle size problem) would neither exist. The Lens change above might btw be the best example why calling those async is extremly dangerous. We registered those functions to a global registry and potentially (even without exact knowledge of anyone within the Lens team) they could have been consumed and used in other parts too, directly from the registry. Now switching it over to async loading could break those usage unintentionally. As far as I am aware this did not happen anywhere, but it's def one of those pitfalls why I think using this anti-pattern is not a real solution, but properly thinking about async registries or local registries where more applicable should be the solution to this. Some similar discussion about having a shared registry implementation that would allow exactly those "freezing" after setup phases already came up in #36705 (just for reference). |
We are going to start with logging a warning whenever lifecycle API is called outside of the current lifecycle timeframe #124039 |
Using setup functionality after the setup lifecycle completes is a common anti-pattern I've seen.
Consider the following code:
Plugins should be able to assume that by the time the
start
lifecycle runs, no functionality onsetup
will be used. In other words, the return value ofstart.getSum
from above is stable and will not change.Unfortunately, it's very easy for devs to do something like this:
This raises two questions:
If we agree on 1. we can add docs for 2, but I don't think docs are sufficient. I think we should see if the plugin system itself can detect this and fail, by wrapping plugin lifecycle methods and failing if they are called after the setup lifecycle completes.
cc @lukeelmers @kobelb @clintandrewhall
The text was updated successfully, but these errors were encountered: