-
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
Non-global injected initial data #52553
Comments
Pinging @elastic/kibana-platform (Team:Platform) |
When #52161 lands, the pages rendering will be done in NP. That will allow us way more control on the 'injected metadatas'. We could provide an API like coreSetup.rendering.setPluginDataProvider(async (request) => {
return await someData(request);
}) That could be consumed, on a per-plugin basis, on the frontend with // will only access data registered by my plugin
coreSetup.someService.getInjectedData<MyPluginInjectedData>(); PROS:
CONS:
I definitely see the usages and gains of the proposal, but my feeling is that this goes against what #45796 tries to achieve. |
#45796 does not only impact server-side, it is also about making the client-side plugins lifecycle methods synchronous to avoid that a plugin could cause the whole app to freeze/hang/never start on the client-side. Awaiting some per-plugin data during the rendering process of the html page seems like a similar problematic. |
Good point. |
This is a good observation. If we added an async way for plugins to provide rendering data, we'd be trading off a slower first byte (and thus no visual feedback that Kibana is doing anything) for no UI flicker. It seems like the UI flickering issue could be at least mitigated by thoughtful UI design using placeholders that fade in the real UI once the requisite data is loaded. |
What about capabilities? Almost nothing will be able to render until those are available. |
@kobelb Those are fetched by Core before plugin |
🤦♂ I forgot the KP's "core" doesn't have to abide by all of the same rules as the plugins, the Perhaps the security and spaces plugins really are the exception here... |
Edit: I had a stale version of the page open, so didn't see the conversation already evolved. @pgayvallet is right in that the intent is to limit the impact of async bugs in lifecycles but also context handlers which are called while Kibana is already started. So if a plugin rendered it's own html page on it's own endpoint it would be fine, but anything else introduces a weakness where one plugin's "injected vars" bug prevents the entire Kibana from showing. |
After discussion, we've decided to punt on adding any blocking behavior here. If this becomes a significant usability problem in the future, we should reopen this issue and reconsider. |
Notes from duplicate issue I opened: Summary of problems with the legacy injected vars:
In the Kibana Platform, we introduced the exposeToBrowser config option to expose config values to the frontend. This covers most cases that injected vars was being used for, however there are some use cases that cannot be solved by this:
Drawbacks with requiring these plugins to get this data via a fetch to a dedicated endpoint:
Possible SolutionWe could add an API to the RenderingService that allows plugins to register functions that provide data to the frontend, similar to the legacy platform, with some distinct differences:
class MyPluginServer {
setup(core) {
// Register a function to be executed by the RenderingService
core.rendering.registerRenderData(async () => ({
helloWorld: someAsyncFunc()
}));
}
} class MyPluginPublic {
constructor(initContext) {
// Only data provided by this plugin's server-side code would be available
this.helloWorld = initContext.renderData.helloWorld;
}
} Discussion
|
Correct me if I'm wrong:
If we decide to go with this (which I'm not against) however, I'm not sure the synchronous lifecycles changes really makes sense anymore as the initial issue it tries to address will be present with this custom data feature. |
The data that the Security and Spaces plugin would like to have provided in the injected initial data is already retrieved in the auth, or post-auth lifecycles and could easily be cached and so that we don't end up executing this request twice.
Is this a common problem in the LP? I've struggled to form an opinion on how much this concern should be driving the design. |
Do we really need to support async hooks? can Security, Spaces & UrlShortener can go with sync getter?
|
About the short url use case - we need to fetch a saved object to fill in the injected data. But we only need to do this when rendering the app using a custom route anyway (using the newly introduced rendering api). So for short url it would be enough to be able to inject the data only when calling the rendering api directly (not as a hook for all routes/apps) Basically as a separate parameter in this function call: |
I don't have a strong opinion on this. It's one of The preloaded data is a very common practice with a lot of upsides regarding performances (reducing the number of requests is almost always better than the first-byte timing). This just goes against the Long story short, I'm neither yea nor nay |
I like having a core design that allows to scale the system without making it brittle and unreliable. My suggestion to cover the existing use cases without hurting the lifecycles: Sync hooks that can be run on every request: class MyPluginServer {
setup(core) {
// Register a function to be executed by the RenderingService
core.rendering.registerRenderData(() => ({
helloWorld: someFunc()
}));
}
} Plus passing an object to the explicit render call: const injectedData: await myAsyncFunc();
return response.ok({
headers: {
'content-security-policy': http.csp.header,
},
body: await context.core.rendering.render({ injectedData }),
}); The first case can't block the request because it's synchronous, the second one will only slow down the route controlled by the plugin itself, in which case it's okay to block it. |
Sync hooks does not have the same issue and there is no reasons we shouldn't implement it if this is a need. Only question I would have regarding them is: Is there an actual realistic usage of them? It seems to me that the
True. However I'm not sure to see the potential gain of this: When we'll be fully migrated, Kibana will be a true SPA, and there will no longer be page-reload between apps. These data would only be loaded for your custom route if that's the first page the user accesses or when he would be performing a page refresh. |
@kobelb I think you have to answer that question :) Would a sync hook cover your use case?
Exactly, that's what's necessary for the short url service. It's basically a workaround to resolve really long URLs in browsers that do not support them. The URL params are never part of an actual URL, but are passed as part of the initial HTML payload and then put into the session storage of the browser. Of course this can also be done with a fetch call, but that would slow down the rendering of the actual app the user wants to navigate to because it adds a client/server roundtrip:
It's not a big deal, I just raised it because it's a regression from current state. I'm also fine with avoiding this additional complexity in the core if it's just for this very case. I can think about potential future use cases that would profit from being able to this though, e.g. inlining data into an embedded dashboard that's shown in an iframe on another web page. In general all cases where we can deduct from the URL of the initial request that the page will definitely request a defined set of additional data and directly putting it into the initial request itself. Of course the tradeoff here is time to first byte vs time to load, but it would be nice to let the plugin have control over this. |
Thanks for the detailed clarification
What bothers me here is that it may not be crystal clear for developers that these additional data shall only be used for that specific use case. I'm afraid some tries to use the feature thinking that it's just a data-preload that will work in any case, even if accessing their application after another initial page load. If we want to provide that, we will need to be very explicit on the API naming and documentation |
I'm torn whether the introduced complexity (and potential confusion and misuse of consumers) is worth it. Fine with both ways here. Maybe someone can think of a better way to expose the functionality to make the intended purpose easier to understand. |
It would for the moment, but it's coincidental and in the future it might not. |
The not-a-platform has the concept of "injected vars" which allows us to load various data in the initial http response embedded within the HTML. This allows us to immediately be able to render other HTML elements based on this data. Without this facility, and once we no longer allow
setup
/start
lifecycle events to be "async" in the Kibana Platform per this RFC, we will have to perform a XHR to get this data and then render the dependent HTML elements. This can cause the page to "flicker" as we wait for the request to complete, as can be seen in #52386 and #40856.It'd be nice to have non-global injected initial data so that we no longer have this page flicker. If I understand correctly, our main complaint with the not-a-platform's implementation of "injected vars" was with it being scoped globally, and crossing plugin boundaries.
The text was updated successfully, but these errors were encountered: