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

reload plugins on reconnect #6159

Merged
merged 1 commit into from
Sep 20, 2019
Merged

reload plugins on reconnect #6159

merged 1 commit into from
Sep 20, 2019

Conversation

akosyakov
Copy link
Member

@akosyakov akosyakov commented Sep 11, 2019

What it does

  • fix After a disconnect of the browser, commands from a VSCode extension do not work #5025: reload plugins on reconnect
    • unload plugin static contributions
      • only if a plugin was undeployed
      • unload languages
      • unload tasks
      • unload commands
      • unload menus
      • unload view containers
      • unload views
      • unload keymaps
      • unload schemas
      • unload snippets
      • unload shared css styles
    • unload plugin code and dynamic contributions
      • unload rpc (dispose internal state and does not allow to add new proxies, services and so on)
      • unload command registry (dispose command and handlers)
      • unload quick open (hide quick open and dispose its listeners)
      • unload workspace (dispose workspace and fs watch listeners, content providers)
        • install only one fs watcher for all plugins instead of for each plugin
      • unload dialogs (no listeners or state to dispose)
      • unload message registry (no listeners or state to dispose)
      • unload preference registry (dispose preference service listeners)
      • unload documents (dispose editor and model listeners)
      • unload editors (remove editor decorations)
      • unload status bar registry (remove status bar entries)
      • unload env (no listeners or state to dispose)
      • unload notification (cancel all running progress reporting)
      • unload terminal (dispose terminal service and terminal listeners)
      • unload tree view (unregister tree data providers and remove tree view listeners)
        • preserve tree views on reconnect
      • unload output (not listeners, output channels are preserved)
      • unload languages (language features gets disposed, but diagnostics are preserved)
      • unload webview (dispose webviews and its serializers)
      • unload storage (no listeners or state to dispose, but path service is rewritten to avoid forever pending promises and accepting workspace URI as an arg for each request instead of caching a random one)
      • unload connection (remove connections)
      • unload tasks (unregister task providers, preserve running tasks)
      • unload languages contribution (stop them)
      • unload debug (dispose debug listeners and remove debug contributions)
      • unload fs (dispose fs providers)
      • unload scm (dispose scm listeners and remove contributed repositories)
      • unload explorer decorations (dispose providers)
      • unload window (remove event listeners)
      • unload clipboard (no listeners or state to dispose)
      • unload shared css styles
  • fix ensure that no plugin can be loaded twice #5829: ensure that each plugin is loaded only once
  • revert Reconnect same host plugin process and client #5257: it was bogus to introduce in the first place
  • fix Duplicate language server results when installing a second language plugin #6186: start only one plugin host process for the same host instead of a new for each load as before
  • update the changelog with a breaking change
  • fix Vscode Go debugger freeze on web-socket disconnect #4590: plugin debug sessions are closed on reconnection for now, track Debug session should be resilient to brief web-socket disconnects #4591 and shared debug sessions #3361 for improvements

Out of scope:

  • handling extension dependencies on stop
  • UI to uninstall extensions

How to test

  • reconnect because of a connection failure
    • no reproducible with Chrome since it cannot throttle web socket connections
    • throttle with the system proxy instead, for instance with https://www.charlesproxy.com/
  • reconnect on the server restart
  • test everything with multiple windows for the same backend

Hints:

  • Take memory snapshots when a connection is gone to check that there is no main services are left.

Tested with following VS Code extensions:

Reconnecting tree views

reconnect_tree_views

Review checklist

Reminder for reviewers

@akosyakov akosyakov added the plug-in system issues related to the plug-in system label Sep 11, 2019
@akosyakov akosyakov force-pushed the ak/plugins_reconnect branch 29 times, most recently from 10f41f1 to 2a653b5 Compare September 16, 2019 05:27
@akosyakov akosyakov force-pushed the ak/plugins_reconnect branch 5 times, most recently from 9ef894e to e8d8233 Compare September 19, 2019 04:26
@amiramw
Copy link
Member

amiramw commented Sep 19, 2019

@akosyakov do you think custom APIs should also be treated to allow unload?

@akosyakov akosyakov force-pushed the ak/plugins_reconnect branch 2 times, most recently from ce9e771 to 85be868 Compare September 19, 2019 06:51
@akosyakov
Copy link
Member Author

do you think custom APIs should also be treated to allow unload?

@amiramw Custom main services should implement Disposable interface, then they will be disposed. I will add it in the changelog.

@akosyakov akosyakov marked this pull request as ready for review September 19, 2019 07:10
@akosyakov
Copy link
Member Author

@eclipse-theia/eclipse-theia I'm still trying different extensions to check reload of all contributions, but the code should be in final state and help with testing is welcomed!

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.

The code looks great. Will now test.

packages/monaco/src/browser/textmate/textmate-registry.ts Outdated Show resolved Hide resolved
}
this.locals.set(identifier.id, instance);
if (Disposable.is(instance)) {
this.toDispose.push(instance);
Copy link
Contributor

Choose a reason for hiding this comment

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

It is a little strange that the dispose lifecycle of the passed in arguments get associated with this object. I see that the usages always pass fresh objects here. So maybe not a real problem.
In general we should follow the pattern 'who creates is responsible for disposing'

Copy link
Member Author

@akosyakov akosyakov Sep 20, 2019

Choose a reason for hiding this comment

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

i actually thought that set should be replaced by a contribution point, something like PluginRPCService, then contributions will be created by RPCProtocol and installed, but first we need to refactor the plugin system to use DI.

packages/plugin-ext/src/main/browser/connection-main.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/main/browser/text-editors-main.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/plugin/plugin-manager.ts Outdated Show resolved Hide resolved
@svenefftinge
Copy link
Contributor

Works great as well!

I noticed that after disconnecting and reconnecting again, there is an additional reconnection.
I.e. I see

root INFO Registering scopes for language: java.
logger-protocol.ts:112 root INFO The scopes have been successfully registered for java.
index.js:94 Error: Connection timeout
    at index.js:85
(anonymous) @ index.js:94
setTimeout (async)
emitError @ index.js:84
(anonymous) @ index.js:127
setTimeout (async)
connect @ index.js:124
setTimeout (async)
handleClose @ index.js:113
logger-protocol.ts:112 root INFO Unregistering scopes for language: java.
logger-protocol.ts:112 root INFO The scopes have been successfully unregistered for java.

but I think you mentioned that once to me and it is also unrelated to this PR as it happens without the change, too. With this PR this at least doesn't have such a bad side effect anymore. 🎉

Also:
- fix #5829: ensure that each plugin is loaded only once
- fix #6186: start only one plugin host process for the same host instead of a new for each load as before
- revert #5257: Reconnect same host plugin process and client" 797db75

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

merging if the build is green

@akosyakov akosyakov merged commit 657bda8 into master Sep 20, 2019
@akosyakov akosyakov deleted the ak/plugins_reconnect branch September 20, 2019 07:01
@@ -163,7 +163,7 @@ export const emptyPlugin: Plugin = {
};

export interface PluginManagerExt {
$stopPlugin(contextPath: string): PromiseLike<void>;
$stop(pluginId?: string): PromiseLike<void>;
Copy link
Contributor

Choose a reason for hiding this comment

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

why break the name of this method ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because it does not do what it means: It does not stop one plugin but all plugins. contextPath is unused, changing contextPath to optional pluginId to stop either one plugin or all was already a breaking change. I could introduce another method, but then stopPlugin won't be used anywhere in the Theia repo. I did not see a reason why to keep an unused method.

@@ -80,16 +80,6 @@ export class PluginHostRPC {
}
}

/*
Copy link
Contributor

Choose a reason for hiding this comment

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

Hello, why stopContext has been removed ? it's also not referenced in changelog ?

plugin-host-rpc is quite sensitive as it's used downstream like Eclipse Che.

Also I don't see it related to the changes of this PR

Copy link
Member Author

Choose a reason for hiding this comment

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

There were a clean up of plugin unloading that they can be uninstalled and unused clients of refactored code were removed. Code which should be used in a downstream project should be moved to a corresponding project, not kept in the Theia repo without any internal clients and ability to test by any committer.

There is a record that this PR breaks the plugin system. So far we were not listing individual changes only notified that some part will be broken in next release by some PRs, since we did not really document anything as APIs. Please raise it at the dev meeting if you suggest that each a change to each function should be listed.

Please review a PR while it is not merged, not afterwards. It was 9 days while it was in draft mode for the design review. Committers are not expected to review and test PRs against downstream projects or wait when someone does it.

Please also opened follow-up issues to already merged PRs. Not many committers read comments on them and it can get lost.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you're considering that removing code or changing structural behavior is only requiring one approval from a committer of the same company, I think something is wrong.

Copy link
Member Author

@akosyakov akosyakov Sep 23, 2019

Choose a reason for hiding this comment

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

Please add to discuss to the dev meeting.

Regarding to

Code which should be used in a downstream project should be moved to a corresponding project, not kept in the Theia repo without any internal clients and ability to test by any committer.

I remembered that we don't have something like DI in the plugin host process yet, not sure whether you can actually extend it. If not please let me know, the new issue will be the best.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plug-in system issues related to the plug-in system
Projects
None yet
4 participants