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

don't block web socket with many plugins #6252

Merged
merged 9 commits into from
Sep 27, 2019
Merged

Conversation

akosyakov
Copy link
Member

@akosyakov akosyakov commented Sep 24, 2019

What it does

  • fix plugin system does not scale over JSON-RPC #6228:
    • use permessage-deflate to compress any ws message greater than 256kb
    • remove raw package data from plugin metadata
      • frontend plugins don't have access to it anymore
      • backend plugins recover it from the disk
    • only fetch not yet loaded plugins to the frontend
    • install contributions info only to process
      metadata in the frontend
    • install dependencies info only to process metadata on the backend
    • initialize debugger contributions from metadata, instead of sending it first to the plugin host and then back to the frontend
  • closes don't send plugin metadata over JSON-RPC #6233 - superseded
  • update the changelog with a breaking change
  • checked license compatibility of new dependencies

How to test

  • remove native extension from the browse example and rebuild:
    • textmate grammar
    • debug-nodejs
    • java
    • java-debug
  • install 57 VS Code extensions in plugins folder:
    https://drive.google.com/file/d/1tuOsorMVOKi_twL9oAuZxgrD7-VUwNsj/view?usp=sharing
    • could not come up with a command to pull it from the gdrive in the terminal, if someone knows how to, please share
  • use system proxy to emulate slow connection, e.g. Charles proxy
    • you cannot do it in Chrome, since it cannot throttle web sockets
  • measure how long does it takes to activate all plugins before and after
  • observe whether you get yellow status bar and ws frames hangs for 20-30 seconds after and before
  • play with unstable connections to confirm that plugins get reloaded/unloaded on the reconnection

Review checklist

Reminder for reviewers

@akosyakov akosyakov added jsonrpc issues related to jsonrpc performance issues related to performance plug-in system issues related to the plug-in system labels Sep 24, 2019
@akosyakov
Copy link
Member Author

akosyakov commented Sep 24, 2019

In order to reproduce on the master state:

My results on 3G for 57 extensions:

  • before:
    • yellow status bar
    • unresponsive UI
    • fetching metadata: getDeployedMetadata took 16066.520000109449 ms
    • sending metadata to the plugin host and staring plugins: startPlugins took 48116.30499991588 ms
  • after:
    • no yellow status bar
    • responsive UI
    • fetching metadata: Sync of 57 plugins took: 2785.3900000918657 ms
    • starting plugins: Start of 57 plugins took: 1804.2249998543411 ms

I also measured on my normal connection. In this case results are not so different than on 3G with this PR.

@benoitf
Copy link
Contributor

benoitf commented Sep 24, 2019

Great improvement.

FYI on macOS there is Apple Network Link Conditioner utility

Copy link
Contributor

@svenefftinge svenefftinge left a comment

Choose a reason for hiding this comment

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

works nicely

yarn.lock Show resolved Hide resolved
@akosyakov akosyakov force-pushed the ak/plugin_perf_2 branch 2 times, most recently from d22e053 to d6361cf Compare September 24, 2019 10:00
@akosyakov
Copy link
Member Author

@benoitf please have a look again, I've addressed what I could.

@akosyakov akosyakov force-pushed the ak/plugin_perf_2 branch 2 times, most recently from 1c78525 to 016f6d6 Compare September 25, 2019 03:11
@akosyakov
Copy link
Member Author

I'm going to rebase and merge it if the CI is green and no one has questions to changes of this PR. Current state is quite broken and works for couple plugins, leading to bad UX whenever a bit more is installed.

Copy link
Contributor

@benoitf benoitf left a comment

Choose a reason for hiding this comment

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

thx @akosyakov for the great work on rewriting commits

I still have issues with the last two commits
e9acc49

016f6d6

for the first one, it seems we're in the middle of something.
We have metadata with model, etc but in fact they're not the same as for some you will have contribution/dependencies and for some case you won't. So when you're manipulating metadata you don't know quickly if you're in a mode where you have access or not to it.

For example in https://github.com/eclipse-theia/theia/blob/e9acc497ae23441529f20290ebcba8d97fa8c599/packages/plugin-dev/src/node/hosted-plugin-reader.ts we're just interested in knowing if it's a frontend or a backend plug-in but we go in full loop

so we don't need all this extra options method everywhere which is bloating the code

about the last one, which is very related to the previous one, it's still a hack

@akosyakov
Copy link
Member Author

@benoitf What is your suggestions how to handle it?

@benoitf
Copy link
Contributor

benoitf commented Sep 25, 2019

remove 2 commits from PR and merge it (but it won't solve the blocking)
or solve the metadata/model.

I'm not saying there are no global issues on deploying a lot of plug-ins, but it's not because we have a PR that fix an issue and that CI test is green that we should merge something that is leading to more complex codebase with dirty hacks.

@akosyakov
Copy link
Member Author

or solve the metadata/model.

I meant what is you suggestion about it? And:

for the first one, it seems we're in the middle of something.
We have metadata with model, etc but in fact they're not the same as for some you will have contribution/dependencies and for some case you won't. So when you're manipulating metadata you don't know quickly if you're in a mode where you have access or not to it.

how do you suggest to solve it?

@akosyakov
Copy link
Member Author

akosyakov commented Sep 25, 2019

for the first one, it seems we're in the middle of something.
We have metadata with model, etc but in fact they're not the same as for some you will have contribution/dependencies and for some case you won't. So when you're manipulating metadata you don't know quickly if you're in a mode where you have access or not to it.

I don't see how it can happen: if the plugin was deleted it will be unloaded by next deployment from the frontend and the plugin host process. ...It can happen if one somehow manually manipulates with the file disk, but when it can happen at any time when the plugin system reads package.json. Don't think we should expect it.

about the last one, which is very related to the previous one, it's still a hack

For the hack for frontend plugins, I'm still not sure what your expectations. Should we install undefined? Should we throw on access of packageJSON?

@benoitf
Copy link
Contributor

benoitf commented Sep 25, 2019

for the first one, it seems we're in the middle of something.
We have metadata with model, etc but in fact they're not the same as for some you will have
contribution/dependencies and for some case you won't. So when you're manipulating metadata >> you don't know quickly if you're in a mode where you have access or not to it.

I don't see how it can happen: if the plugin was deleted it will be unloaded by next deployment
from the frontend and the plugin host process. ...It can happen if one somehow manually
manipulates with the file disk, but when it can happen at any time when the plugin system reads
package.json. Don't think we should expect it.

I'm not talking about the cache or reloading. I mean, when you see a metadata object you might call metadata.model.contributes on it but sometimes it will be there and sometimes no. (even if it's the package.json) because some options are on or off and then you'll have to go to see which option as been given in upper calls, etc.

as I said, by looking in the code, we're giving options parameter without contributes:true just to know if it's a backend or a frontend. overkill.

about the last one, which is very related to the previous one, it's still a hack

For the hack for frontend plugins, I'm still not sure what your expectations. Should we install undefined? Should we throw on access of packageJSON?

the hack is on stubbing and broken raw model

@svenefftinge
Copy link
Contributor

We have metadata with model, etc but in fact they're not the same as for some you will have contribution/dependencies and for some case you won't. So when you're manipulating metadata you don't know quickly if you're in a mode where you have access or not to it.

I don't see how this is a hack or bad code. It is a classic optimization based on fetch profiles and gladly we have a type system that tells us if something can be undefined.

@akosyakov
Copy link
Member Author

akosyakov commented Sep 26, 2019

I've noticed that with an opened editor loading contributions takes around 15s. It's turned out because we try to detect a language for plaintext editors for each extension, more extension we have, longer it takes. I will schedule auto detection for another tick that we can start plugins earlier. - after removing unnecessary language redetection it is around 3-5s

Another issue is that we register languages before grammars, but grammars are activated when a language is activated. So if a language is activated before the grammar is registered then a grammar is never activated. And it happens if you have an opened editor. We don't see it with textmate-grammars native extension, because everything always registered before any Monaco model is created, in this PR it gets very apparent. I will inverse the registration order to fix it.

Updated
Also Monaco 0.17 guesses languages for opened plain text models by default, so doing us the same just increases the loading time. I will revert #5754

@akosyakov
Copy link
Member Author

@svenefftinge addressed #6252 (comment) with 70e51c4

@akosyakov akosyakov mentioned this pull request Sep 26, 2019
1 task
@akosyakov
Copy link
Member Author

akosyakov commented Sep 26, 2019

All commits except reducing the plugin metadata are extracted to #6267

Copy link
Contributor

@benoitf benoitf left a comment

Choose a reason for hiding this comment

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

cc @azatsarynnyy @evidolob

From what I see with the breaking changes, it will be a total breakage of Eclipse Che and remote plug-ins.
BTW it is not so clear in changelog that it involves breakage of deployment APIs and that a lot of APIs were renamed, some sync methods are now async and new lifecycle method is added.
Reading changelog it looks like a smooth change while it is definitely not

We don't have many days of usage (even if it was merged last friday) so I would suggest to postpone the merge just after the release of Theia occuring today to let ppl stick to a stable API and enhance the changelog by explicitely saying interfaces that are broken (it's huge)

@benoitf benoitf dismissed their stale review September 26, 2019 09:35

Anton did a very good rewrite on the last two commits by removing cleanly the raw stuff. I would just postpone merge after upcoming Theia release

@akosyakov
Copy link
Member Author

@benoitf I will update the changelog.

@marcdumais-work the release is today right?

@marcdumais-work
Copy link
Contributor

marcdumais-work commented Sep 26, 2019

Hi @akosyakov , @benoitf

It's the plan to do the release today, yes. I see there was a lot of recent action about plugins, and that the situation is perhaps not completely resolved. So I defer to you about whether we should go ahead today or wait a bit for a more satisfactory resolution. Doing a 0.11.1 release in e.g. a week is also an option.

Please advise.

@akosyakov
Copy link
Member Author

akosyakov commented Sep 26, 2019

@marcdumais-work we are going to land it only afterwards, please proceed with the release

Metadata for many plugins accumulate to a single message over 5Mb big which blocks web socket, compressing such message helps to reduce blocking time.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
Instead of sending it first to the plugin host and then back to the frontend. Debug metadata are big fetching them to the client, then sending back to the plugin host and fetching again is expensive and blocking web socket.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
Instead of sending all intiailzation data on each plugin deployment. It is unnecessary and leads to sending big chunks of data, e.g. all preference values, each time.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
Before on each deployment the entire metdata will be loaded again. It was expensive, since metadata is big with a lot of plugins and unnecessary as well.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
With multiple windows there were duplicate log entries. Now each log entry has a unique uuid to identify a window.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
In order to see performance improvement now and spot degradation later.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
Un order to trim unnecessary data, i.e.:
- only frontend reads plugin contributions
- only backend reads plugin dependencies

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
It accumulates to a lot of data with many plugins and can block the connection leading to slow start up time and unresponsive UI.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
Before a grammar won't be activated for an opened editor after reload because we missed language activation event.

Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
@akosyakov
Copy link
Member Author

akosyakov commented Sep 27, 2019

@benoitf @svenefftinge @azatsarynnyy @evidolob the changelog is updated, the release is done

From what I see with the breaking changes, it will be a total breakage of Eclipse Che and remote plug-ins.

I've looked at Che on usage of renamed/replaced APIs and found:

APIs were renamed, some sync methods are now async and new lifecycle method is added.

PluginHost.init still supports sync version, it is maybe promise, not a promise. I could not find that lifecycle method PluginManagerExt.$init is used in Che.

I've looked at https://github.com/eclipse/che-theia.

Again such work should be done on the product level when it is about to migrate, not a framework level. JS provides access to anything at runtime, even to private stuff via index access, and DI allow to override and inject whatever product wants. I don't think there is a way to break completely.

@svenefftinge
Copy link
Contributor

Awesome work, Anton!
@benoitf I assume we can merge this now?

@benoitf
Copy link
Contributor

benoitf commented Sep 27, 2019

@akosyakov thx for the heads up on che-theia repository.
Well it's not because we have access to private components that the product is extensible. Also DI is not completely there.
BTW thx for the latest improvements, it's a more clean solution.

@svenefftinge yes

@svenefftinge svenefftinge merged commit 6347c5c into master Sep 27, 2019
@akosyakov akosyakov deleted the ak/plugin_perf_2 branch September 27, 2019 08:25
benoitf added a commit that referenced this pull request Oct 1, 2019
…s, deployedPlugins, etc.

Update the extra information to match the real expectations of methods (string ids in some case, DeployedPlugin in the other case)

Change-Id: I82b5e5cdad2e42cba9e1dd469eadbeb0fac29e3e
Signed-off-by: Florent Benoit <fbenoit@redhat.com>
benoitf added a commit that referenced this pull request Oct 1, 2019
…s, deployedPlugins, etc.

Update the extra information to match the real expectations of methods (string ids in some case, DeployedPlugin in the other case)

Change-Id: I82b5e5cdad2e42cba9e1dd469eadbeb0fac29e3e
Signed-off-by: Florent Benoit <fbenoit@redhat.com>
akosyakov pushed a commit to akosyakov/theia that referenced this pull request Feb 24, 2020
…cing pluginIds, deployedPlugins, etc.

Update the extra information to match the real expectations of methods (string ids in some case, DeployedPlugin in the other case)

Change-Id: I82b5e5cdad2e42cba9e1dd469eadbeb0fac29e3e
Signed-off-by: Florent Benoit <fbenoit@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
jsonrpc issues related to jsonrpc performance issues related to performance plug-in system issues related to the plug-in system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

plugin system does not scale over JSON-RPC
4 participants